目錄[-]
Writer :BYSocket(泥沙磚瓦漿木匠)
微 博:BYSocket
豆 瓣:BYSocket
FaceBook:BYSocket
Twitter :BYSocket
文件,作為常見(jiàn)的數(shù)據(jù)源。關(guān)于操作文件的字節(jié)流就是 — FileInputStream & FileOutputStream。它們是Basic IO字節(jié)流中重要的實(shí)現(xiàn)類。
FileInputStream源碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | /** * FileInputStream 從文件系統(tǒng)的文件中獲取輸入字節(jié)流。文件取決于主機(jī)系統(tǒng)。 * 比如讀取圖片等的原始字節(jié)流。如果讀取字符流,考慮使用 FiLeReader。 */publicclassSFileInputStream extendsInputStream{ /* 文件描述符類---此處用于打開(kāi)文件的句柄 */ /* 引用文件的路徑 */ private final String path; /* 文件通道,NIO部分 */ private FileChannel channel = null; private final Object closeLock = new Object(); private volatile boolean closed = false; private static final ThreadLocal<Boolean> runningFinalize = new ThreadLocal<>(); private static boolean isRunningFinalize() { Boolean val; if ((val = runningFinalize.get()) != null) return val.booleanValue(); return false; } /* 通過(guò)文件路徑名來(lái)創(chuàng)建FileInputStream */ public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); } /* 通過(guò)文件來(lái)創(chuàng)建FileInputStream */ public FileInputStream(File file) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } fd = new FileDescriptor(); fd.incrementAndGetUseCount(); this.path = name; open(name); } /* 通過(guò)文件描述符類來(lái)創(chuàng)建FileInputStream */ public FileInputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkRead(fdObj); } fd = fdObj; path = null; fd.incrementAndGetUseCount(); } /* 打開(kāi)文件,為了下一步讀取文件內(nèi)容。native方法 */ private native void open(String name) throws FileNotFoundException; /* 從此輸入流中讀取一個(gè)數(shù)據(jù)字節(jié) */ public int read() throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int b = 0; try { b = read0(); } finally { IoTrace.fileReadEnd(traceContext, b == -1 ? 0 : 1); } return b; } /* 從此輸入流中讀取一個(gè)數(shù)據(jù)字節(jié)。native方法 */ private native int read0() throws IOException; /* 從此輸入流中讀取多個(gè)字節(jié)到byte數(shù)組中。native方法 */ private native int readBytes(byte b[], int off, int len) throws IOException; /* 從此輸入流中讀取多個(gè)字節(jié)到byte數(shù)組中。 */ public int read(byte b[]) throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int bytesRead = 0; try { bytesRead = readBytes(b, 0, b.length); } finally { IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead); } return bytesRead; } /* 從此輸入流中讀取最多l(xiāng)en個(gè)字節(jié)到byte數(shù)組中。 */ public int read(byte b[], int off, int len) throws IOException { Object traceContext = IoTrace.fileReadBegin(path); int bytesRead = 0; try { bytesRead = readBytes(b, off, len); } finally { IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead); } return bytesRead; } public native long skip(long n) throws IOException; /* 返回下一次對(duì)此輸入流調(diào)用的方法可以不受阻塞地從此輸入流讀?。ɑ蛱^(guò))的估計(jì)剩余字節(jié)數(shù)。 */ public native int available() throws IOException; /* 關(guān)閉此文件輸入流并釋放與此流有關(guān)的所有系統(tǒng)資源。 */ public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { fd.decrementAndGetUseCount(); channel.close(); } int useCount = fd.decrementAndGetUseCount(); if ((useCount <= 0) || !isRunningFinalize()) { close0(); } } public final FileDescriptor getFD() throws IOException { if (fd != null) return fd; throw new IOException(); } /* 獲取此文件輸入流的唯一FileChannel對(duì)象 */ publicFileChannel getChannel() { synchronized(this) { if(channel == null) { channel = FileChannelImpl.open(fd, path, true, false, this); fd.incrementAndGetUseCount(); } returnchannel; } } privatestaticnativevoidinitIDs(); privatenativevoidclose0() throwsIOException; static{ initIDs(); } protected void finalize() throws IOException { if((fd != null) && (fd != FileDescriptor.in)) { runningFinalize.set(Boolean.TRUE); try{ close(); } finally{ runningFinalize.set(Boolean.FALSE); } } }} |
1. 三個(gè)核心方法
三個(gè)核心方法,也就是Override(重寫(xiě))了抽象類InputStream的read方法。
int read() 方法,即
1 | public int read() throws IOException |
代碼實(shí)現(xiàn)中很簡(jiǎn)單,一個(gè)try中調(diào)用本地native的read0()方法,直接從文件輸入流中讀取一個(gè)字節(jié)。IoTrace.fileReadEnd(),字面意思是防止文件沒(méi)有關(guān)閉讀的通道,導(dǎo)致讀文件失敗,一直開(kāi)著讀的通道,會(huì)造成內(nèi)存泄露。
int read(byte b[]) 方法,即
1 | public int read(byte b[]) throws IOException |
代碼實(shí)現(xiàn)也是比較簡(jiǎn)單的,也是一個(gè)try中調(diào)用本地native的readBytes()方法,直接從文件輸入流中讀取最多b.length個(gè)字節(jié)到byte數(shù)組b中。
int read(byte b[], int off, int len) 方法,即
1 | public int read(byte b[], int off, int len) throws IOException |
代碼實(shí)現(xiàn)和 int read(byte b[])方法 一樣,直接從文件輸入流中讀取最多l(xiāng)en個(gè)字節(jié)到byte數(shù)組b中。
可是這里有個(gè)問(wèn):
Q: 為什么 int read(byte b[]) 方法需要自己獨(dú)立實(shí)現(xiàn)呢? 直接調(diào)用 int read(byte b[], int off, int len) 方法,即read(b , 0 , b.length),等價(jià)于read(b)?
A:待完善,希望路過(guò)大神回。。。。向下兼容?? Finally??
2. 值得一提的native方法
上面核心方法中為什么實(shí)現(xiàn)簡(jiǎn)單,因?yàn)楣ぷ髁慷荚?strong>native方法里面,即JVM里面實(shí)現(xiàn)了。native倒是不少一一列舉吧:
native void open(String name) // 打開(kāi)文件,為了下一步讀取文件內(nèi)容
native int read0() // 從文件輸入流中讀取一個(gè)字節(jié)
native int readBytes(byte b[], int off, int len) // 從文件輸入流中讀取,從off句柄開(kāi)始的len個(gè)字節(jié),并存儲(chǔ)至b字節(jié)數(shù)組內(nèi)。
native void close0() // 關(guān)閉該文件輸入流及涉及的資源,比如說(shuō)如果該文件輸入流的FileChannel對(duì)被獲取后,需要對(duì)FileChannel進(jìn)行close。
其他還有值得一提的就是,在jdk1.4中,新增了NIO包,優(yōu)化了一些IO處理的速度,所以在FileInputStream和FileOutputStream中新增了FileChannel getChannel()的方法。即獲取與該文件輸入流相關(guān)的 java.nio.channels.FileChannel對(duì)象。
FileOutputStream 源碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | /** * 文件輸入流是用于將數(shù)據(jù)寫(xiě)入文件或者文件描述符類 * 比如寫(xiě)入圖片等的原始字節(jié)流。如果寫(xiě)入字符流,考慮使用 FiLeWriter。 */publicclassSFileOutputStream extendsOutputStream{ /* 文件描述符類---此處用于打開(kāi)文件的句柄 */ private final FileDescriptor fd; /* 引用文件的路徑 */ private final String path; /* 如果為 true,則將字節(jié)寫(xiě)入文件末尾處,而不是寫(xiě)入文件開(kāi)始處 */ private final boolean append; /* 關(guān)聯(lián)的FileChannel類,懶加載 */ private FileChannel channel; private final Object closeLock = new Object(); private volatile boolean closed = false; private static final ThreadLocal<Boolean> runningFinalize = new ThreadLocal<>(); private static boolean isRunningFinalize() { Boolean val; if ((val = runningFinalize.get()) != null) return val.booleanValue(); return false; } /* 通過(guò)文件名創(chuàng)建文件輸入流 */ public FileOutputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null, false); } /* 通過(guò)文件名創(chuàng)建文件輸入流,并確定文件寫(xiě)入起始處模式 */ public FileOutputStream(String name, boolean append) throws FileNotFoundException { this(name != null ? new File(name) : null, append); } /* 通過(guò)文件創(chuàng)建文件輸入流,默認(rèn)寫(xiě)入文件的開(kāi)始處 */ public FileOutputStream(File file) throws FileNotFoundException { this(file, false); } /* 通過(guò)文件創(chuàng)建文件輸入流,并確定文件寫(xiě)入起始處 */ public FileOutputStream(File file, boolean append) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } this.fd = new FileDescriptor(); this.append = append; this.path = name; fd.incrementAndGetUseCount(); open(name, append); } /* 通過(guò)文件描述符類創(chuàng)建文件輸入流 */ public FileOutputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkWrite(fdObj); } this.fd = fdObj; this.path = null; this.append = false; fd.incrementAndGetUseCount(); } /* 打開(kāi)文件,并確定文件寫(xiě)入起始處模式 */ private native void open(String name, boolean append) throws FileNotFoundException; /* 將指定的字節(jié)b寫(xiě)入到該文件輸入流,并指定文件寫(xiě)入起始處模式 */ private native void write(int b, boolean append) throws IOException; /* 將指定的字節(jié)b寫(xiě)入到該文件輸入流 */ public void write(int b) throws IOException { Object traceContext = IoTrace.fileWriteBegin(path); int bytesWritten = 0; try { write(b, append); bytesWritten = 1; } finally { IoTrace.fileWriteEnd(traceContext, bytesWritten); } } /* 將指定的字節(jié)數(shù)組寫(xiě)入該文件輸入流,并指定文件寫(xiě)入起始處模式 */ private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException; /* 將指定的字節(jié)數(shù)組b寫(xiě)入該文件輸入流 */ public void write(byte b[]) throws IOException { Object traceContext = IoTrace.fileWriteBegin(path); int bytesWritten = 0; try { writeBytes(b, 0, b.length, append); bytesWritten = b.length; } finally { IoTrace.fileWriteEnd(traceContext, bytesWritten); } } /* 將指定len長(zhǎng)度的字節(jié)數(shù)組b寫(xiě)入該文件輸入流 */ public void write(byte b[], int off, int len) throws IOException { Object traceContext = IoTrace.fileWriteBegin(path); int bytesWritten = 0; try { writeBytes(b, off, len, append); bytesWritten = len; } finally { IoTrace.fileWriteEnd(traceContext, bytesWritten); } } /* 關(guān)閉此文件輸出流并釋放與此流有關(guān)的所有系統(tǒng)資源 */ publicvoidclose() throwsIOException { synchronized(closeLock) { if(closed) { return; } closed = true; } if(channel != null) { fd.decrementAndGetUseCount(); channel.close(); } intuseCount = fd.decrementAndGetUseCount(); if((useCount <= 0) || !isRunningFinalize()) { close0(); } } public final FileDescriptor getFD() throws IOException { if(fd != null) returnfd; thrownewIOException(); } publicFile Channel getChannel() { synchronized(this) { if(channel == null) { channel = FileChannelImpl.open(fd, path, false, true, append, this); fd.incrementAndGetUseCount(); } returnchannel; } } protected void finalize() throws IOException { if(fd != null) { if(fd == FileDescriptor.out || fd == FileDescriptor.err) { flush(); } else{ runningFinalize.set(Boolean.TRUE); try{ close(); } finally{ runningFinalize.set(Boolean.FALSE); } } } } private native void close0() throws IOException; private static native void initIDs(); static{ initIDs(); } } |
1. 三個(gè)核心方法
三個(gè)核心方法,也就是Override(重寫(xiě))了抽象類OutputStream的write方法。
void write(int b) 方法,即
1 | public void write(intb) throws IOException |
代碼實(shí)現(xiàn)中很簡(jiǎn)單,一個(gè)try中調(diào)用本地native的write()方法,直接將指定的字節(jié)b寫(xiě)入文件輸出流。IoTrace.fileReadEnd()的意思和上面FileInputStream意思一致。
void write(byte b[]) 方法,即
1 | public void write(byteb[]) throws IOException |
代碼實(shí)現(xiàn)也是比較簡(jiǎn)單的,也是一個(gè)try中調(diào)用本地native的writeBytes()方法,直接將指定的字節(jié)數(shù)組寫(xiě)入該文件輸入流。
void write(byte b[], int off, int len) 方法,即
1 | public void write(byte b[], int off, int len) throws IOException |
代碼實(shí)現(xiàn)和 void write(byte b[]) 方法 一樣,直接將指定的字節(jié)數(shù)組寫(xiě)入該文件輸入流。
2. 值得一提的native方法
上面核心方法中為什么實(shí)現(xiàn)簡(jiǎn)單,因?yàn)楣ぷ髁慷荚?strong>native方法里面,即JVM里面實(shí)現(xiàn)了。native倒是不少一一列舉吧:
native void open(String name) // 打開(kāi)文件,為了下一步讀取文件內(nèi)容
native void write(int b, boolean append) // 直接將指定的字節(jié)b寫(xiě)入文件輸出流
native native void writeBytes(byte b[], int off, int len, boolean append) // 直接將指定的字節(jié)數(shù)組寫(xiě)入該文件輸入流。
native void close0() // 關(guān)閉該文件輸入流及涉及的資源,比如說(shuō)如果該文件輸入流的FileChannel對(duì)被獲取后,需要對(duì)FileChannel進(jìn)行close。
相似之處:
其實(shí)到這里,該想一想。兩個(gè)源碼實(shí)現(xiàn)很相似,而且native方法也很相似。其實(shí)不能說(shuō)“相似”,應(yīng)該以“對(duì)應(yīng)”來(lái)概括它們。
它們是一組,是一根吸管的兩個(gè)孔的關(guān)系:“一個(gè)Input一個(gè)Output”。
休息一下吧~ 看看小廣告:
開(kāi)源代碼都在我的gitHub上哦 — https://github.com/JeffLi1993 作者留言“請(qǐng)手賤,點(diǎn)項(xiàng)目star,支持支持拜托拜托”
下面先看代碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | packageorg.javacore.io; importjava.io.File;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException; /* * Copyright [2015] [Jeff Lee] * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Jeff Lee * @since 2015-10-8 20:06:03 * FileInputStream&FileOutputStream使用案例 */public class FileIOStreamT { private static final String thisFilePath = "src"+ File.separator + "org"+ File.separator + "javacore"+ File.separator + "io"+ File.separator + "FileIOStreamT.java"; public static void main(String[] args) throws IOException { // 創(chuàng)建文件輸入流 FileInputStream fileInputStream = new FileInputStream(thisFilePath); // 創(chuàng)建文件輸出流 FileOutputStream fileOutputStream = new FileOutputStream("data.txt"); // 創(chuàng)建流的最大字節(jié)數(shù)組 byte[] inOutBytes = new byte[fileInputStream.available()]; // 將文件輸入流讀取,保存至inOutBytes數(shù)組 fileInputStream.read(inOutBytes); // 將inOutBytes數(shù)組,寫(xiě)出到data.txt文件中 fileOutputStream.write(inOutBytes); fileOutputStream.close(); fileInputStream.close(); }} |
運(yùn)行后,會(huì)發(fā)現(xiàn)根目錄中出現(xiàn)了一個(gè)“data.txt”文件,內(nèi)容為上面的代碼。
1. 簡(jiǎn)單地分析下源碼:
1、創(chuàng)建了FileInputStream,讀取該代碼文件為文件輸入流。
2、創(chuàng)建了FileOutputStream,作為文件輸出流,輸出至data.txt文件。
3、針對(duì)流的字節(jié)數(shù)組,一個(gè) read ,一個(gè)write,完成讀取和寫(xiě)入。
4、關(guān)閉流
2. 代碼調(diào)用的流程如圖所示:

3. 代碼雖簡(jiǎn)單,但是有點(diǎn)小問(wèn)題:
FileInputStream.available() 是返回流中的估計(jì)剩余字節(jié)數(shù)。所以一般不會(huì)用此方法。
一般做法,比如創(chuàng)建一個(gè) byte數(shù)組,大小1K。然后read至其返回值不為-1,一直讀取即可。邊讀邊寫(xiě)。
FileInputStream & FileOutputStream 是一對(duì)來(lái)自 InputStream和OutputStream的實(shí)現(xiàn)類。用于本地文件讀寫(xiě)(二進(jìn)制格式按順序讀寫(xiě))。
本文小結(jié):
1、FileInputStream 源碼分析
2、FileOutputStream 資源分析
3、FileInputStream & FileOutputStream 使用案例
4、其源碼調(diào)用過(guò)程
歡迎點(diǎn)擊我的博客及GitHub — 博客提供rss訂閱哦!
———- http://www.bysocket.com/ ————- https://github.com/JeffLi1993 ———-
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注