Java 与 IO 流
作者: dkvirus 发表于: 2018-08-14 16:38:50 最近更新: 2018-08-15 10:15:41

很多地方都会提到 IO 流操作,个人理解就是数据传递,比如创建文件、复制文件等操作就属于 io 流操作。java 里的流有字节流、字符流、缓存流、数据流、对象流等,很容易弄晕。相比之下,nodejs 在这方面的 api 就显得极其友好。

一、意淫流

1. 水流和人流

流这玩意很抽象,不是具体看得见摸得着的实物,感觉理解要靠顿悟,关键这种靠悟的东西你也不知道自个悟的对不对,只能用自个悟的那套去处理问题,问题解决了可能、也许悟对了,问题没解决,那八成悟错了,接着悟呗。

流有三个非常明显的特点,我给它们起名叫做:通道流体方向性

在水流这个案例里,首先得有条臭水沟,这是通道;水在水沟上流动,水就是流体;水从上游往下游流动,这是方向性。人站在下游看水流动,水是朝着我们流过来的(输入流),人站在上游看水流动,水是远离我们流出去的(输出流)。

去火车站检票入站需要排很长的队,于是有了人流。火车站会规定一条走廊,大伙只能在这条走廊上有序排队,这条走廊就是通道;人在走廊上有序向前移动,人就是流体;人流从火车站外往火车站内流动,这是方向性。站在火车站内检票小姐姐的位置看不停的有人进入火车站(输入流),站在火车站外送朋友家属的人的位置看人流不停远离站外往站内移动(输出流)。

2. IO 流

IO 流作为流的一种,也是有 通道流体方向性 三个特性的。IO 是两个单词的首字母 —— Input 和 Output,因此 IO 流也可以叫做 输入输出流

2.1 通道

IO 流要进行数据传递,首先需要建立通道,供流体在上面流动。想一想水沟让水可以流动,走廊让排队的人群可以流动,IO 流的通道自然让数据流可以流动。

下面代码中 FileInputStream fis = new FileInputStream(f); 仅仅是建立了通道,此时还没有任何流在上面流动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {
try {
File f = new File("d:/lol.txt");
// 创建文件流通道,仅仅是创建通道而已,还没有流在上面流动
FileInputStream fis = new FileInputStream(f);

// ......

// 每次使用完流,都应该进行关闭
fis.close();

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

每次数据传递完之后还要手动关闭流。一个 IO 流操作完成后,通道也就没有利用价值了,再保留着只会占用系统资源(想一想火车站如果没有人排队了,还要那条排队的走廊干甚),如果保留的通道很多,那占用的资源也会很多,可能会导致死机等异常情况。有时我在想,如果 Java 能自动判断 IO 流什么时候结束,自动关闭通道该多好。

使用文件流,必定要捕获异常。如果你实际操作过 FileInputStream,就会发现不加 try…catch 会报错,这是为什么呢??流传递过程还是挺脆弱的,复制一个文件,由于网络原因或者人为操作取消复制操作都会中断流的传递,如果不捕获异常,Java 程序会报错进而直接中断程序的运行。

2.2 流体

计算机这个铁疙瘩通电后只能认识 0 和 1,也就是说电脑上的小视频、照片、文本本质上都是保存为 0011011101011 。计算机最小单位是 比特(bit),一个 0 或者一个 1 就是一个比特,但是这个单位太小了,因此又有了 字节(byte) 这个单位,一个字节等于八个比特(1byte = 8bit)。将 test.txt 从 C 盘复制到 D 盘,实际上就是将 test.txt 对应的字节从 C 盘复制到 D 盘。

字节流的使用范围是很广的,电脑上任何资源:视频、音乐、图片、文本本质都是字节,都可以进行数据传递。但是如果只操作文本文件,字节流传递的数据程序员是不能直接看懂的,都是十六进制的一串串数字。此时如果能直接传递过来字符是极好的,于是有了字符流。

Java 中流的流体也就 字节流字符流 两种。处理文本用字符流,处理其他文件用字节流。

2.3 方向性

使用 Java 程序读取 test.txt 文件内容,对于 Java 程序来说,是获取 test.txt 文件的内容,得用输入流。

使用 Java 程序将一段数据写到 test.txt 文件中,对于 Java 程序来说,是将自己的东东给别人,因此是输出流。

二、Java 中的 IO 流

1. 简单的流操作示例

读取 c:/test.txt 文件内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TestStream {

public static void main(String[] args) {
try {
File f =new File("c:/test.txt");

// 创建输入流通道
FileInputStream fis =new FileInputStream(f);

// 创建字节数组,其长度就是文件的长度
byte[] all =new byte[(int) f.length()];
// 读取流
fis.read(all);

// 每次使用完流,都应该进行关闭
fis.close();

} catch (IOException e) {
e.printStackTrace();
}

}
}

1.1 建立通道

File f =new File("c:/test.txt"); 将文件路径字符串对应的文件转换为 Java 对象,好处是可以获取该文件的其它属性,如:文件长度,文件修改日期等更加详细的信息。

FileInputStream fis =new FileInputStream(f); 这里创建了输入流通道。

1.2 流传输

fis.read(all); 传输流数据,可以选择传输多少个字节的内容,这里 byte[] all =new byte[(int) f.length()]; 为文件的全部内容长度,也就是读取文件所有内容。如果这是是 fis.read(30);,那也就只会读取文件内前 30 个字节的内容。

1.3 关闭通道

fis.close(); IO 流操作完成后,关闭通道。像 dk 刚开始学习的时候就会经常忘记关闭通道,也不明白为什么要关闭通道,不过现在基本不会再犯这个错误了。

1.4 捕获异常

用 try…catch 包裹整个流操作,如果中间出现问题,不会直接中断程序,友好的在打印日志里记录出错原因,程序还能健康的运行。

2. Java 中流关系

关于由字节流和字符流引申出的其它流,具体使用参阅:how2j-java中级-IO流

1
2
3
4
5
6
|-- 流
|-- 字节流
|-- 数据流
|-- 对象流
|-- 字符流
|-- 缓存流
首页
友链
归档
dkvirus
动态
RSS