<div class="markdown_views"> <p>[TOC] <br> 轉載自:<a href="http://blog.csdn.net/akon_vm/article/details/7429245" target="_blank">http://blog.csdn.net/akon_vm/article/details/7429245</a></p>java
<h2 id="java-randomaccessfile"><a name="t0"></a>Java RandomAccessFile</h2>數組
<p>RandomAccessFile是用來訪問那些保存數據記錄的文件的,你就能夠用seek( )方法來訪問記錄,並進行讀寫了。這些記錄的大小沒必要相同;可是其大小和位置必須是可知的。可是該類僅限於操做文件。</p>markdown
<p>RandomAccessFile不屬於InputStream和OutputStream類系的。實際上,除了實現DataInput和DataOutput接口以外(DataInputStream和DataOutputStream也實現了這兩個接口),它和這兩個類系絕不相干,甚至不使用InputStream和OutputStream類中已經存在的任何功能;它是一個徹底獨立的類,全部方法(絕大多數都只屬於它本身)都是從零開始寫的。這多是由於RandomAccessFile能在文件裏面先後移動,因此它的行爲與其它的I/O類有些根本性的不一樣。總而言之,它是一個直接繼承Object的,獨立的類。</p>多線程
<p>基本上,RandomAccessFile的工做方式是,把DataInputStream和DataOutputStream結合起來,再加上它本身的一些方法,好比定位用的getFilePointer( ),在文件裏移動用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少字節數。此外,它的構造函數還要一個表示以只讀方式(「r」),仍是以讀寫方式(「rw」)打開文件的參數 (和C的fopen( )如出一轍)。它不支持只寫文件。</p>app
<p>只有RandomAccessFile纔有seek搜尋方法,而這個方法也只適用於文件。BufferedInputStream有一個mark( )方法,你能夠用它來設定標記(把結果保存在一個內部變量裏),而後再調用reset( )返回這個位置,可是它的功能太弱了,並且也不怎麼實用。</p>dom
<p>RandomAccessFile的絕大多數功能,但不是所有,已經被JDK 1.4的nio的」內存映射文件(memory-mapped files)」給取代了,你該考慮一下是否是用」內存映射文件」來代替RandomAccessFile了。</p>函數
<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-keyword">import</span> java.io.IOException; </br> <span class="hljs-keyword">import</span> java.io.RandomAccessFile; </br> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestRandomAccessFile</span> {</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> IOException { </br> RandomAccessFile rf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"rtest.dat"</span>, <span class="hljs-string">"rw"</span>); </br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) { </br> <span class="hljs-comment">//寫入基本類型double數據 </span></br> rf.writeDouble(i * <span class="hljs-number">1.414</span>); </br> } </br> rf.close(); </br> rf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"rtest.dat"</span>, <span class="hljs-string">"rw"</span>); </br> <span class="hljs-comment">//直接將文件指針移到第5個double數據後面 </span></br> rf.seek(<span class="hljs-number">5</span> * <span class="hljs-number">8</span>); </br> <span class="hljs-comment">//覆蓋第6個double數據 </span></br> rf.writeDouble(<span class="hljs-number">47.0001</span>); </br> rf.close(); </br> rf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"rtest.dat"</span>, <span class="hljs-string">"r"</span>); </br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) { </br> System.out.println(<span class="hljs-string">"Value "</span> + i + <span class="hljs-string">": "</span> + rf.readDouble()); </br> } </br> rf.close(); </br> } </br> } </code></pre>性能
<h2 id="內存映射文件"><a name="t1"></a>內存映射文件</h2>測試
<p>內存映射文件能讓你建立和修改那些由於太大而沒法放入內存的文件。有了內存映射文件,你就能夠認爲文件已經所有讀進了內存,而後把它當成一個很是大的數組來訪問。這種解決辦法能大大簡化修改文件的代碼。 <br> fileChannel.map(FileChannel.MapMode mode, long position, long size)將此通道的文件區域直接映射到內存中。注意,你必須指明,它是從文件的哪一個位置開始映射的,映射的範圍又有多大;也就是說,它還能夠映射一個大文件的某個小片段。</p>this
<p>MappedByteBuffer是ByteBuffer的子類,所以它具有了ByteBuffer的全部方法,但新添了force()將緩衝區的內容強制刷新到存儲設備中去、load()將存儲設備中的數據加載到內存中、isLoaded()位置內存中的數據是否與存儲設置上同步。這裏只簡單地演示了一下put()和get()方法,除此以外,你還可使用asCharBuffer( )之類的方法獲得相應基本類型數據的緩衝視圖後,能夠方便的讀寫基本類型數據。</p>
<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-keyword">import</span> java.io.RandomAccessFile; </br> <span class="hljs-keyword">import</span> java.nio.MappedByteBuffer; </br> <span class="hljs-keyword">import</span> java.nio.channels.FileChannel; </br> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LargeMappedFiles</span> {</span> </br> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> length = <span class="hljs-number">0x8000000</span>; <span class="hljs-comment">// 128 Mb </span></br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Exception { </br> <span class="hljs-comment">// 爲了以可讀可寫的方式打開文件,這裏使用RandomAccessFile來建立文件。 </span></br> FileChannel fc = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"test.dat"</span>, <span class="hljs-string">"rw"</span>).getChannel(); </br> <span class="hljs-comment">//注意,文件通道的可讀可寫要創建在文件流自己可讀寫的基礎之上 </span></br> MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, <span class="hljs-number">0</span>, length); </br> <span class="hljs-comment">//寫128M的內容 </span></br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < length; i++) { </br> out.put((<span class="hljs-keyword">byte</span>) <span class="hljs-string">'x'</span>); </br> } </br> System.out.println(<span class="hljs-string">"Finished writing"</span>); </br> <span class="hljs-comment">//讀取文件中間6個字節內容 </span></br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = length / <span class="hljs-number">2</span>; i < length / <span class="hljs-number">2</span> + <span class="hljs-number">6</span>; i++) { </br> System.out.print((<span class="hljs-keyword">char</span>) out.get(i)); </br> } </br> fc.close(); </br> } </br> }</code></pre>
<blockquote> <p><strong>儘管映射寫彷佛要用到FileOutputStream,可是映射文件中的全部輸出 必須使用RandomAccessFile,但若是隻須要讀時可使用FileInputStream,寫映射文件時必定要使用隨機訪問文件,可能寫時要讀的緣由吧。</strong></p> </blockquote>
<p>該程序建立了一個128Mb的文件,若是一次性讀到內存可能致使內存溢出,但這裏訪問好像只是一瞬間的事,這是由於,真正調入內存的只是其中的一小部分,其他部分則被放在交換文件上。這樣你就能夠很方便地修改超大型的文件了(最大能夠到2 GB)。注意,Java是調用操做系統的」文件映射機制」來提高性能的。</p>
<h2 id="randomaccessfile類的應用"><a name="t2"></a>RandomAccessFile類的應用:</h2>
<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-comment">/* * 程序功能:演示了RandomAccessFile類的操做,同時實現了一個文件複製操做。 */</span> <span class="hljs-keyword">package</span> com.lwj.demo; <span class="hljs-keyword">import</span> java.io.*; <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RandomAccessFileDemo</span> {</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Exception { </br> RandomAccessFile file = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"file"</span>, <span class="hljs-string">"rw"</span>); </br> <span class="hljs-comment">// 如下向file文件中寫數據 </span></br> file.writeInt(<span class="hljs-number">20</span>);<span class="hljs-comment">// 佔4個字節 </span></br> file.writeDouble(<span class="hljs-number">8.236598</span>);<span class="hljs-comment">// 佔8個字節 </span></br> file.writeUTF(<span class="hljs-string">"這是一個UTF字符串"</span>);<span class="hljs-comment">// 這個長度寫在當前文件指針的前兩個字節處,可用readShort()讀取 </span></br> file.writeBoolean(<span class="hljs-keyword">true</span>);<span class="hljs-comment">// 佔1個字節 </span></br> file.writeShort(<span class="hljs-number">395</span>);<span class="hljs-comment">// 佔2個字節 </span></br> file.writeLong(<span class="hljs-number">2325451</span>l);<span class="hljs-comment">// 佔8個字節 </span></br> file.writeUTF(<span class="hljs-string">"又是一個UTF字符串"</span>); </br> file.writeFloat(<span class="hljs-number">35.5</span>f);<span class="hljs-comment">// 佔4個字節 </span></br> file.writeChar(<span class="hljs-string">'a'</span>);<span class="hljs-comment">// 佔2個字節 </span></br> </br> file.seek(<span class="hljs-number">0</span>);<span class="hljs-comment">// 把文件指針位置設置到文件起始處 </span></br> </br> <span class="hljs-comment">// 如下從file文件中讀數據,要注意文件指針的位置 </span></br> System.out.println(<span class="hljs-string">"——————從file文件指定位置讀數據——————"</span>); </br> System.out.println(file.readInt()); </br> System.out.println(file.readDouble()); </br> System.out.println(file.readUTF()); </br> </br> file.skipBytes(<span class="hljs-number">3</span>);<span class="hljs-comment">// 將文件指針跳過3個字節,本例中即跳過了一個boolean值和short值。 </span></br> System.out.println(file.readLong()); </br> </br> file.skipBytes(file.readShort()); <span class="hljs-comment">// 跳過文件中「又是一個UTF字符串」所佔字節,注意readShort()方法會移動文件指針,因此不用加2。 </span></br> System.out.println(file.readFloat()); </br> </br> <span class="hljs-comment">//如下演示文件複製操做 </span></br> System.out.println(<span class="hljs-string">"——————文件複製(從file到fileCopy)——————"</span>); </br> file.seek(<span class="hljs-number">0</span>); </br> RandomAccessFile fileCopy=<span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"fileCopy"</span>,<span class="hljs-string">"rw"</span>); </br> <span class="hljs-keyword">int</span> len=(<span class="hljs-keyword">int</span>)file.length();<span class="hljs-comment">//取得文件長度(字節數) </span></br> <span class="hljs-keyword">byte</span>[] b=<span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[len]; file.readFully(b); </br> fileCopy.write(b); </br> System.out.println(<span class="hljs-string">"複製完成!"</span>); </br> } </br> }</code></pre>
<h2 id="randomaccessfile-插入寫示例"><a name="t3"></a>RandomAccessFile 插入寫示例:</h2>
<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-javadoc">/** </br> * </br> *<span class="hljs-javadoctag"> @param</span> skip 跳過多少過字節進行插入數據 </br> *<span class="hljs-javadoctag"> @param</span> str 要插入的字符串 </br> *<span class="hljs-javadoctag"> @param</span> fileName 文件路徑</br> */</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">beiju</span>(<span class="hljs-keyword">long</span> skip, String str, String fileName){ </br> <span class="hljs-keyword">try</span> { </br> RandomAccessFile raf = <span class="hljs-keyword">new</span> RandomAccessFile(fileName,<span class="hljs-string">"rw"</span>); </br> <span class="hljs-keyword">if</span>(skip < <span class="hljs-number">0</span> || skip > raf.length()){ </br> System.out.println(<span class="hljs-string">"跳過字節數無效"</span>); </br> <span class="hljs-keyword">return</span>; </br> } </br> <span class="hljs-keyword">byte</span>[] b = str.getBytes(); </br> raf.setLength(raf.length() + b.length); </br> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">long</span> i = raf.length() - <span class="hljs-number">1</span>; i > b.length + skip - <span class="hljs-number">1</span>; i--){ </br> raf.seek(i - b.length); </br> <span class="hljs-keyword">byte</span> temp = raf.readByte(); </br> raf.seek(i); </br> raf.writeByte(temp); </br> } </br> raf.seek(skip); </br> raf.write(b); </br> raf.close(); </br> } <span class="hljs-keyword">catch</span> (Exception e) { </br> e.printStackTrace(); </br> } </br> }</code></pre>
<p>利用RandomAccessFile實現文件的多線程下載,即多線程下載一個文件時,將文件分紅幾塊,每塊用不一樣的線程進行下載。下面是一個利用多線程在寫文件時的例子,其中預先分配文件所須要的空間,而後在所分配的空間中進行分塊,而後寫入:</p>
<pre class="prettyprint" name="code"><code class="language-java hljs has-numbering"><span class="hljs-keyword">import</span> java.io.FileNotFoundException; </br> <span class="hljs-keyword">import</span> java.io.IOException; </br> <span class="hljs-keyword">import</span> java.io.RandomAccessFile; </br> <span class="hljs-javadoc">/** </br> * 測試利用多線程進行文件的寫操做 </br> */</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> {</span> </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> Exception { </br> <span class="hljs-comment">// 預分配文件所佔的磁盤空間,磁盤中會建立一個指定大小的文件 </span></br> RandomAccessFile raf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"D://abc.txt"</span>, <span class="hljs-string">"rw"</span>); </br> raf.setLength(<span class="hljs-number">1024</span>*<span class="hljs-number">1024</span>); <span class="hljs-comment">// 預分配 1M 的文件空間 </span></br> raf.close(); </br> </br> <span class="hljs-comment">// 所要寫入的文件內容 </span></br> String s1 = <span class="hljs-string">"第一個字符串"</span>; </br> String s2 = <span class="hljs-string">"第二個字符串"</span>; </br> String s3 = <span class="hljs-string">"第三個字符串"</span>; </br> String s4 = <span class="hljs-string">"第四個字符串"</span>; </br> String s5 = <span class="hljs-string">"第五個字符串"</span>; </br> </br> <span class="hljs-comment">// 利用多線程同時寫入一個文件 </span> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">1</span>,s1.getBytes()).start(); <span class="hljs-comment">// 從文件的1024字節以後開始寫入數據 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">2</span>,s2.getBytes()).start(); <span class="hljs-comment">// 從文件的2048字節以後開始寫入數據 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">3</span>,s3.getBytes()).start(); <span class="hljs-comment">// 從文件的3072字節以後開始寫入數據 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">4</span>,s4.getBytes()).start(); <span class="hljs-comment">// 從文件的4096字節以後開始寫入數據 </span></br> <span class="hljs-keyword">new</span> FileWriteThread(<span class="hljs-number">1024</span>*<span class="hljs-number">5</span>,s5.getBytes()).start(); <span class="hljs-comment">// 從文件的5120字節以後開始寫入數據 </span></br> } </br> <span class="hljs-comment">// 利用線程在文件的指定位置寫入指定數據 </span></br> <span class="hljs-keyword">static</span> class FileWriteThread extends Thread{ </br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> skip; </br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">byte</span>[] content; </br> <span class="hljs-keyword">public</span> <span class="hljs-title">FileWriteThread</span>(<span class="hljs-keyword">int</span> skip,<span class="hljs-keyword">byte</span>[] content){ <span class="hljs-keyword">this</span>.skip = skip; </br> <span class="hljs-keyword">this</span>.content = content; </br> } </br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>(){ </br> RandomAccessFile raf = <span class="hljs-keyword">null</span>; </br> <span class="hljs-keyword">try</span> { </br> raf = <span class="hljs-keyword">new</span> RandomAccessFile(<span class="hljs-string">"D://abc.txt"</span>, <span class="hljs-string">"rw"</span>); </br> raf.seek(skip); </br> raf.write(content); </br> } <span class="hljs-keyword">catch</span> (FileNotFoundException e) { </br> e.printStackTrace(); </br> } <span class="hljs-keyword">catch</span> (IOException e) { </br> <span class="hljs-comment">// TODO Auto-generated catch block </span></br> e.printStackTrace(); </br> } <span class="hljs-keyword">finally</span> { </br> <span class="hljs-keyword">try</span> { </br> raf.close(); </br> } <span class="hljs-keyword">catch</span> (Exception e) { </br> } </br> } </br> } </br> } </br> }</code></pre> </div>