五、思考与小结

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

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

{

/* 文件描述符类---此处用于打开文件的句柄 */

PRivatefinalFileDescriptor fd;

/* 引用文件的路径 */

privatefinalString path;

/* 文件通道,NIO部分 */

privateFileChannel channel =null;

privatefinalObject closeLock =newObject();

privatevolatilebooleanclosed =false;

privatestaticfinalThreadLocal<Boolean> runningFinalize =

newThreadLocal<>();

privatestaticbooleanisRunningFinalize() {

Boolean val;

if((val = runningFinalize.get()) !=null)

returnval.booleanValue();

returnfalse;

}

/* 通过文件路径名来创建FileInputStream */

publicFileInputStream(String name)throwsFileNotFoundException {

this(name !=null?newFile(name) :null);

}

/* 通过文件来创建FileInputStream */

publicFileInputStream(File file)throwsFileNotFoundException {

String name = (file !=null? file.getPath() :null);

SecurityManager security = System.getSecurityManager();

if(security !=null) {

security.checkRead(name);

}

if(name ==null) {

thrownewNullPointerException();

}

if(file.isInvalid()) {

thrownewFileNotFoundException("Invalid file path");

}

fd =newFileDescriptor();

fd.incrementAndGetUseCount();

this.path = name;

open(name);

}

/* 通过文件描述符类来创建FileInputStream */

publicFileInputStream(FileDescriptor fdObj) {

SecurityManager security = System.getSecurityManager();

if(fdObj ==null) {

thrownewNullPointerException();

}

if(security !=null) {

security.checkRead(fdObj);

}

fd = fdObj;

path =null;

fd.incrementAndGetUseCount();

}

/* 打开文件,为了下一步读取文件内容。native方法 */

privatenativevoidopen(String name)throwsFileNotFoundException;

/* 从此输入流中读取一个数据字节 */

publicintread()throwsIOException {

Object traceContext = IoTrace.fileReadBegin(path);

intb =0;

try{

b = read0();

}finally{

IoTrace.fileReadEnd(traceContext, b == -1?0:1);

}

returnb;

}

/* 从此输入流中读取一个数据字节。native方法 */

privatenativeintread0()throwsIOException;

/* 从此输入流中读取多个字节到byte数组中。native方法 */

privatenativeintreadBytes(byteb[],intoff,intlen)throwsIOException;

/* 从此输入流中读取多个字节到byte数组中。 */

publicintread(byteb[])throwsIOException {

Object traceContext = IoTrace.fileReadBegin(path);

intbytesRead =0;

try{

bytesRead = readBytes(b,0, b.length);

}finally{

IoTrace.fileReadEnd(traceContext, bytesRead == -1?0: bytesRead);

}

returnbytesRead;

}

/* 从此输入流中读取最多len个字节到byte数组中。 */

publicintread(byteb[],intoff,intlen)throwsIOException {

Object traceContext = IoTrace.fileReadBegin(path);

intbytesRead =0;

try{

bytesRead = readBytes(b, off, len);

}finally{

IoTrace.fileReadEnd(traceContext, bytesRead == -1?0: bytesRead);

}

returnbytesRead;

}

publicnativelongskip(longn)throwsIOException;

/* 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 */

publicnativeintavailable()throwsIOException;

/* 关闭此文件输入流并释放与此流有关的所有系统资源。 */

publicvoidclose()throwsIOException {

synchronized(closeLock) {

if(closed) {

return;

}

closed =true;

}

if(channel !=null) {

fd.decrementAndGetUseCount();

channel.close();

}

intuseCount = fd.decrementAndGetUseCount();

if((useCount <=0) || !isRunningFinalize()) {

close0();

}

}

publicfinalFileDescriptor getFD()throwsIOException {

if(fd !=null)returnfd;

thrownewIOException();

}

/* 获取此文件输入流的唯一FileChannel对象 */

publicFileChannel getChannel() {

synchronized(this) {

if(channel ==null) {

channel = FileChannelImpl.open(fd, path,true,false,this);

fd.incrementAndGetUseCount();

}

returnchannel;

}

}

privatestaticnativevoidinitIDs();

privatenativevoidclose0() throwsIOException;

static{

initIDs();

}

protectedvoidfinalize()throwsIOException {

if((fd !=null) && (fd != FileDescriptor.in)) {

runningFinalize.set(Boolean.TRUE);

try{

close();

}finally{

runningFinalize.set(Boolean.FALSE);

}

}

}

}

1. 三个核心方法

三个核心方法,也就是Override(重写)了抽象类InputStream的read方法。

int read() 方法,即

?

1

publicintread()throwsIOException

代码实现中很简单,一个try中调用本地native的read0()方法,直接从文件输入流中读取一个字节。IoTrace.fileReadEnd(),字面意思是防止文件没有关闭读的通道,导致读文件失败,一直开着读的通道,会造成内存泄露。

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

?

1

publicintread(byteb[])throwsIOException

代码实现也是比较简单的,也是一个try中调用本地native的readBytes()方法,直接从文件输入流中读取最多b.length个字节到byte数组b中。

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

?

1

publicintread(byteb[],intoff,intlen)throwsIOException

代码实现和 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

{

/* 文件描述符类---此处用于打开文件的句柄 */

privatefinalFileDescriptor fd;

/* 引用文件的路径 */

privatefinalString path;

/* 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处 */

privatefinalbooleanappend;

/* 关联的FileChannel类,懒加载 */

privateFileChannel channel;

privatefinalObject closeLock =newObject();

privatevolatilebooleanclosed =false;

privatestaticfinalThreadLocal<Boolean> runningFinalize =

newThreadLocal<>();

privatestaticbooleanisRunningFinalize() {

Boolean val;

if((val = runningFinalize.get()) !=null)

returnval.booleanValue();

returnfalse;

}

/* 通过文件名创建文件输入流 */

publicFileOutputStream(String name)throwsFileNotFoundException {

this(name !=null?newFile(name) :null,false);

}

/* 通过文件名创建文件输入流,并确定文件写入起始处模式 */

publicFileOutputStream(String name,booleanappend)

throwsFileNotFoundException

{

this(name !=null?newFile(name) :null, append);

}

/* 通过文件创建文件输入流,默认写入文件的开始处 */

publicFileOutputStream(File file)throwsFileNotFoundException {

this(file,false);

}

/* 通过文件创建文件输入流,并确定文件写入起始处 */

publicFileOutputStream(File file,booleanappend)

throwsFileNotFoundException

{

String name = (file !=null? file.getPath() :null);

SecurityManager security = System.getSecurityManager();

if(security !=null) {

security.checkWrite(name);

}

if(name ==null) {

thrownewNullPointerException();

}

if(file.isInvalid()) {

thrownewFileNotFoundException("Invalid file path");

}

this.fd =newFileDescriptor();

this.append = append;

this.path = name;

fd.incrementAndGetUseCount();

open(name, append);

}

/* 通过文件描述符类创建文件输入流 */

publicFileOutputStream(FileDescriptor fdObj) {

SecurityManager security = System.getSecurityManager();

if(fdObj ==null) {

thrownewNullPointerException();

}

if(security !=null) {

security.checkWrite(fdObj);

}

this.fd = fdObj;

this.path =null;

this.append =false;

fd.incrementAndGetUseCount();

}

/* 打开文件,并确定文件写入起始处模式 */

privatenativevoidopen(String name,booleanappend)

throwsFileNotFoundException;

/* 将指定的字节b写入到该文件输入流,并指定文件写入起始处模式 */

privatenativevoidwrite(intb,booleanappend)throwsIOException;

/* 将指定的字节b写入到该文件输入流 */

publicvoidwrite(intb)throwsIOException {

Object traceContext = IoTrace.fileWriteBegin(path);

intbytesWritten =0;

try{

write(b, append);

bytesWritten =1;

}finally{

IoTrace.fileWriteEnd(traceContext, bytesWritten);

}

}

/* 将指定的字节数组写入该文件输入流,并指定文件写入起始处模式 */

privatenativevoidwriteBytes(byteb[],intoff,intlen,booleanappend)

throwsIOException;

/* 将指定的字节数组b写入该文件输入流 */

publicvoidwrite(byteb[])throwsIOException {

Object traceContext = IoTrace.fileWriteBegin(path);

intbytesWritten =0;

try{

writeBytes(b,0, b.length, append);

bytesWritten = b.length;

}finally{

IoTrace.fileWriteEnd(traceContext, bytesWritten);

}

}

/* 将指定len长度的字节数组b写入该文件输入流 */

publicvoidwrite(byteb[],intoff,intlen)throwsIOException {

Object traceContext = IoTrace.fileWriteBegin(path);

intbytesWritten =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();

}

}

publicfinalFileDescriptor getFD() throwsIOException {

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;

}

}

protectedvoidfinalize()throwsIOException {

if(fd !=null) {

if(fd == FileDescriptor.out || fd == FileDescriptor.err) {

flush();

}else{

runningFinalize.set(Boolean.TRUE);

try{

close();

}finally{

runningFinalize.set(Boolean.FALSE);

}

}

}

}

privatenativevoidclose0()throwsIOException;

privatestaticnativevoidinitIDs();

static{

initIDs();

}

}

1. 三个核心方法

三个核心方法,也就是Override(重写)了抽象类OutputStream的write方法。

void write(int b) 方法,即

?

1

publicvoidwrite(intb)throwsIOException

代码实现中很简单,一个try中调用本地native的write()方法,直接将指定的字节b写入文件输出流。IoTrace.fileReadEnd()的意思和上面FileInputStream意思一致。

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

?

1

publicvoidwrite(byteb[])throwsIOException

代码实现也是比较简单的,也是一个try中调用本地native的writeBytes()方法,直接将指定的字节数组写入该文件输入流。

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

?

1

publicvoidwrite(byteb[],intoff,intlen)throwsIOException

代码实现和 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使用案例

*/

publicclassFileIOStreamT {

privatestaticfinalString thisFilePath =

"src"+ File.separator +

"org"+ File.separator +

"javacore"+ File.separator +

"io"+ File.separator +

"FileIOStreamT.java";

publicstaticvoidmain(String[] args)throwsIOException {

// 创建文件输入流

FileInputStream fileInputStream =newFileInputStream(thisFilePath);

// 创建文件输出流

FileOutputStream fileOutputStream = newFileOutputStream("data.txt");

// 创建流的最大字节数组

byte[] inOutBytes =newbyte[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 ———-

全能程序员交流QQ群290551701,群内程序员都是来自,百度、阿里、京东、小米、去哪儿、饿了吗、蓝港等高级程序员 ,拥有丰富的经验。加入我们,直线沟通技术大牛,最佳的学习环境,了解业内的一手的资讯。如果你想结实大牛,那 就加入进来,让大牛带你超神!

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航