目錄[-]
Writer :BYSocket(泥沙磚瓦漿木匠)
微 博:BYSocket
豆 瓣:BYSocket
FaceBook:BYSocket
Twitter :BYSocket
文件,作為常見的數據源。關于操作文件的字節流就是 — FileInputStream & FileOutputStream。它們是Basic IO字節流中重要的實現類。
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 從文件系統的文件中獲取輸入字節流。文件取決于主機系統。 * 比如讀取圖片等的原始字節流。如果讀取字符流,考慮使用 FiLeReader。 */ publicclassSFileInputStream extendsInputStream { /* 文件描述符類---此處用于打開文件的句柄 */ /* 引用文件的路徑 */ 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 ; } /* 通過文件路徑名來創建FileInputStream */ public FileInputStream(String name) throws FileNotFoundException { this (name != null ? new File(name) : null ); } /* 通過文件來創建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); } /* 通過文件描述符類來創建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(); } /* 打開文件,為了下一步讀取文件內容。native方法 */ private native void open(String name) throws FileNotFoundException; /* 從此輸入流中讀取一個數據字節 */ 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; } /* 從此輸入流中讀取一個數據字節。native方法 */ private native int read0() throws IOException; /* 從此輸入流中讀取多個字節到byte數組中。native方法 */ private native int readBytes( byte b[], int off, int len) throws IOException; /* 從此輸入流中讀取多個字節到byte數組中。 */ 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; } /* 從此輸入流中讀取最多len個字節到byte數組中。 */ 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; /* 返回下一次對此輸入流調用的方法可以不受阻塞地從此輸入流讀取(或跳過)的估計剩余字節數。 */ public native int available() throws IOException; /* 關閉此文件輸入流并釋放與此流有關的所有系統資源。 */ 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對象 */ 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. 三個核心方法
三個核心方法,也就是Override(重寫)了抽象類InputStream的read方法。
int read() 方法,即
1 | public int read() throws IOException |
代碼實現中很簡單,一個try中調用本地native的read0()方法,直接從文件輸入流中讀取一個字節。IoTrace.fileReadEnd(),字面意思是防止文件沒有關閉讀的通道,導致讀文件失敗,一直開著讀的通道,會造成內存泄露。
int read(byte b[]) 方法,即
1 | public int read( byte b[]) throws IOException |
代碼實現也是比較簡單的,也是一個try中調用本地native的readBytes()方法,直接從文件輸入流中讀取最多b.length個字節到byte數組b中。
int read(byte b[], int off, int len) 方法,即
1 | public int read( byte b[], int off, int len) throws IOException |
代碼實現和 int read(byte b[])方法 一樣,直接從文件輸入流中讀取最多len個字節到byte數組b中。
可是這里有個問:
Q: 為什么 int read(byte b[]) 方法需要自己獨立實現呢? 直接調用 int read(byte b[], int off, int len) 方法,即read(b , 0 , b.length),等價于read(b)?
A:待完善,希望路過大神回。。。。向下兼容?? Finally??
2. 值得一提的native方法
上面核心方法中為什么實現簡單,因為工作量都在native方法里面,即JVM里面實現了。native倒是不少一一列舉吧:
native void open(String name) // 打開文件,為了下一步讀取文件內容
native int read0() // 從文件輸入流中讀取一個字節
native int readBytes(byte b[], int off, int len) // 從文件輸入流中讀取,從off句柄開始的len個字節,并存儲至b字節數組內。
native void close0() // 關閉該文件輸入流及涉及的資源,比如說如果該文件輸入流的FileChannel對被獲取后,需要對FileChannel進行close。
其他還有值得一提的就是,在jdk1.4中,新增了NIO包,優化了一些IO處理的速度,所以在FileInputStream和FileOutputStream中新增了FileChannel getChannel()的方法。即獲取與該文件輸入流相關的 java.nio.channels.FileChannel對象。
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 | /** * 文件輸入流是用于將數據寫入文件或者文件描述符類 * 比如寫入圖片等的原始字節流。如果寫入字符流,考慮使用 FiLeWriter。 */ publicclassSFileOutputStream extendsOutputStream { /* 文件描述符類---此處用于打開文件的句柄 */ private final FileDescriptor fd; /* 引用文件的路徑 */ private final String path; /* 如果為 true,則將字節寫入文件末尾處,而不是寫入文件開始處 */ private final boolean append; /* 關聯的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 ; } /* 通過文件名創建文件輸入流 */ public FileOutputStream(String name) throws FileNotFoundException { this (name != null ? new File(name) : null , false ); } /* 通過文件名創建文件輸入流,并確定文件寫入起始處模式 */ public FileOutputStream(String name, boolean append) throws FileNotFoundException { this (name != null ? new File(name) : null , append); } /* 通過文件創建文件輸入流,默認寫入文件的開始處 */ public FileOutputStream(File file) throws FileNotFoundException { this (file, false ); } /* 通過文件創建文件輸入流,并確定文件寫入起始處 */ 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); } /* 通過文件描述符類創建文件輸入流 */ 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(); } /* 打開文件,并確定文件寫入起始處模式 */ private native void open(String name, boolean append) throws FileNotFoundException; /* 將指定的字節b寫入到該文件輸入流,并指定文件寫入起始處模式 */ private native void write( int b, boolean append) throws IOException; /* 將指定的字節b寫入到該文件輸入流 */ 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); } } /* 將指定的字節數組寫入該文件輸入流,并指定文件寫入起始處模式 */ private native void writeBytes( byte b[], int off, int len, boolean append) throws IOException; /* 將指定的字節數組b寫入該文件輸入流 */ 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長度的字節數組b寫入該文件輸入流 */ 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); } } /* 關閉此文件輸出流并釋放與此流有關的所有系統資源 */ 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. 三個核心方法
三個核心方法,也就是Override(重寫)了抽象類OutputStream的write方法。
void write(int b) 方法,即
1 | public void write(intb) throws IOException |
代碼實現中很簡單,一個try中調用本地native的write()方法,直接將指定的字節b寫入文件輸出流。IoTrace.fileReadEnd()的意思和上面FileInputStream意思一致。
void write(byte b[]) 方法,即
1 | public void write(byteb[]) throws IOException |
代碼實現也是比較簡單的,也是一個try中調用本地native的writeBytes()方法,直接將指定的字節數組寫入該文件輸入流。
void write(byte b[], int off, int len) 方法,即
1 | public void write( byte b[], int off, int len) throws IOException |
代碼實現和 void write(byte b[]) 方法 一樣,直接將指定的字節數組寫入該文件輸入流。
2. 值得一提的native方法
上面核心方法中為什么實現簡單,因為工作量都在native方法里面,即JVM里面實現了。native倒是不少一一列舉吧:
native void open(String name) // 打開文件,為了下一步讀取文件內容
native void write(int b, boolean append) // 直接將指定的字節b寫入文件輸出流
native native void writeBytes(byte b[], int off, int len, boolean append) // 直接將指定的字節數組寫入該文件輸入流。
native void close0() // 關閉該文件輸入流及涉及的資源,比如說如果該文件輸入流的FileChannel對被獲取后,需要對FileChannel進行close。
相似之處:
其實到這里,該想一想。兩個源碼實現很相似,而且native方法也很相似。其實不能說“相似”,應該以“對應”來概括它們。
它們是一組,是一根吸管的兩個孔的關系:“一個Input一個Output”。
休息一下吧~ 看看小廣告:
開源代碼都在我的gitHub上哦 — https://github.com/JeffLi1993 作者留言“請手賤,點項目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 { // 創建文件輸入流 FileInputStream fileInputStream = new FileInputStream(thisFilePath); // 創建文件輸出流 FileOutputStream fileOutputStream = new FileOutputStream( "data.txt" ); // 創建流的最大字節數組 byte [] inOutBytes = new byte [fileInputStream.available()]; // 將文件輸入流讀取,保存至inOutBytes數組 fileInputStream.read(inOutBytes); // 將inOutBytes數組,寫出到data.txt文件中 fileOutputStream.write(inOutBytes); fileOutputStream.close(); fileInputStream.close(); } } |
運行后,會發現根目錄中出現了一個“data.txt”文件,內容為上面的代碼。
1. 簡單地分析下源碼:
1、創建了FileInputStream,讀取該代碼文件為文件輸入流。
2、創建了FileOutputStream,作為文件輸出流,輸出至data.txt文件。
3、針對流的字節數組,一個 read ,一個write,完成讀取和寫入。
4、關閉流
2. 代碼調用的流程如圖所示:
3. 代碼雖簡單,但是有點小問題:
FileInputStream.available() 是返回流中的估計剩余字節數。所以一般不會用此方法。
一般做法,比如創建一個 byte數組,大小1K。然后read至其返回值不為-1,一直讀取即可。邊讀邊寫。
FileInputStream & FileOutputStream 是一對來自 InputStream和OutputStream的實現類。用于本地文件讀寫(二進制格式按順序讀寫)。
本文小結:
1、FileInputStream 源碼分析
2、FileOutputStream 資源分析
3、FileInputStream & FileOutputStream 使用案例
4、其源碼調用過程
歡迎點擊我的博客及GitHub — 博客提供rss訂閱哦!
———- http://www.bysocket.com/ ————- https://github.com/JeffLi1993 ———-
新聞熱點
疑難解答