麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

JavaIO之FileInputStream&FileOutputStream源碼分析

2019-11-14 15:29:22
字體:
來源:轉載
供稿:網友
io java FileInputStream FileOutputStream

 

目錄[-]

  • 一、引子
  • 二、FileInputStream源碼分析
  • 三、FileOutputStream 源碼分析
  • 四、使用案例
  • 五、思考與小結

Writer      :BYSocket(泥沙磚瓦漿木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

一、引子

文件,作為常見的數據源。關于操作文件的字節流就是 — FileInputStream & FileOutputStream。它們是Basic IO字節流中重要的實現類。

二、FileInputStream源碼分析

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 FileDescriptor fd;
  
    /* 引用文件的路徑 */
    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(重寫)了抽象類InputStreamread方法。

int read() 方法,即

 

?
1
public int read() throws IOException

代碼實現中很簡單,一個try中調用本地nativeread0()方法,直接從文件輸入流中讀取一個字節。IoTrace.fileReadEnd(),字面意思是防止文件沒有關閉讀的通道,導致讀文件失敗,一直開著讀的通道,會造成內存泄露。

 

int read(byte b[]) 方法,即

 

?
1
public int read(byte b[]) throws IOException

代碼實現也是比較簡單的,也是一個try中調用本地nativereadBytes()方法,直接從文件輸入流中讀取最多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 源碼分析

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(重寫)了抽象類OutputStreamwrite方法。

void write(int b) 方法,即

 

?
1
public void write(intb) throws IOException

 

代碼實現中很簡單,一個try中調用本地nativewrite()方法,直接將指定的字節b寫入文件輸出流。IoTrace.fileReadEnd()的意思和上面FileInputStream意思一致。

 

void write(byte b[]) 方法,即

 

?
1
public void write(byteb[]) throws IOException

 

代碼實現也是比較簡單的,也是一個try中調用本地nativewriteBytes()方法,直接將指定的字節數組寫入該文件輸入流。

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. 代碼調用的流程如圖所示:

iostream

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 ———-

全能程序員交流QQ群290551701,群內程序員都是來自,百度、阿里、京東、小米、去哪兒、餓了嗎、藍港等高級程序員 ,擁有豐富的經驗。加入我們,直線溝通技術大牛,最佳的學習環境,了解業內的一手的資訊。如果你想結實大牛,那 就加入進來,讓大牛帶你超神!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 国产一区二区三区精品在线观看 | 九九热精品视频在线 | 高清在线国产 | 深夜影院a | 萌白酱福利视频在线网站 | 久久老司机 | 欧美伦交| 91精品国产一区二区三区动漫 | 精品中文字幕久久久久四十五十骆 | 在线观看一二区 | 全黄裸片武则天一级第4季 九色p | 国产精品久久久久久久娇妻 | 国产精品一品二区三区四区18 | 免费看成人av | 亚洲国产网址 | av在线播放免费观看 | 一级空姐毛片 | 国产精品成人久久 | 曰韩精品 | 久久成人精品视频 | 一区二区视频在线看 | 国产色视频一区 | 欧美一级毛片欧美一级成人毛片 | 色婷婷久久一区二区 | 黄网站免费观看视频 | 久久精品久| 毛片哪里看 | 成人精品一区二区三区中文字幕 | 成人9禁啪啪无遮挡免费 | 亚洲日本高清 | 海外中文字幕在线观看 | 日本aaaa片毛片免费观看视频 | 欧美高清在线精品一区二区不卡 | 一区二区久久久久草草 | 国产视频在线播放 | 午夜视频在线看 | 最新亚洲国产 | 51国产偷自视频区视频小蝌蚪 | 日韩精品久久久久久久电影99爱 | 久久久成人一区二区免费影院 | 成人午夜在线免费视频 |