数据流是一组有序、有起点和终点的字节的数据序列,包括输入流和输出流。
一、IO 简介
流序列中的数据既可以是未经过加工的原始二进制数据,也可以是经过一定编码处理后符合某种格式规定的特定数据。在Java 中流分为 2 种: 字节流 和 字符流:
- 字节流: 数据流中最小的数据单位是字节
- 字符流:数据流中最小的数据单元是字符,Java 中的字符是 Unicode 编码,一个字符占用 2 个字节
java.io 中最重要的就是 5 个类和一个接口:
5个类指: File、字节流中的 OutputStream 和 InputStream、字符流中的 Writer 和 Reader ;
一个接口是指 : Serializable
在 Android 中,文本文件/XML 等这些都是用 字符流来读写;而如 RAR、图片等非文本,则用字节流来读写。
二、Java IO 中的装饰模式
以一段容易让人费解的代码开始:
1 | DataOutputStream out = new DataOutputStream( |
这个代码看起来很乱,多重嵌套,其实要理解 IO ,也不难,难的是要更优地去使用 IO 。接下来抽丝剥茧地解释一下上述代码:
new File() 就是创建一个 File 文件,这个没什么好说的
FileOutputStream 这层就是将 File 转为字节流
BufferedOutputStream 就是用来提升速度的,减少对磁头的调用,如果没有它,就会一个一个字节地去访问磁盘;有了它之后,就是一块一块地访问
而使用 DataOutputStream 就是保持数据的格式
我们在日常使用的时候,一般都要是优先考虑 DataOutputStream ,然后交给 BufferedOutputStream 提升效率。上面的例子就是典型的用法。
要理解输入输出流,要把自己当做电脑的 内存,站在内存的角度思考,这样 OutputStream 就是写出到磁盘上了,InputStream 就是读入到内存了。
为了能更好地理解,我们这里写一个 Demo:
1 | private static void testDataOutputStream() throws Exception{ |
我们能看到上述代码往文件中写入了东西,但是直接打开看起来是乱码的,这只是编码的问题,使用正确的编码就不会有问题。
还有一点要注意,使用 DataOutputStream 写入之后,通过 DataIntputStream 读出来的时候,需要保证顺序,比如上述例子里面先写入 int ,后写入 boolean ,读的时候也要先读 int 后读 boolean ,否则会错乱
序列化的时候,大量使用这个 IO 操作。
三、装饰器模式
在上述代码中,我们使用 FileOutputStream 也能满足需求,但是为了磁盘好我们又包装了 BufferedOutputStream ,此时也已经能用了,但是为了数据格式,我们又加了 DataOutputStream 。
这种整个一层一层封装就是我们常见的装饰模式,如果要对装饰模式理解深刻一点的话,可以用视频中老师举的例子: 人本身就是一个类(类似Android 中的 Context),但是人一般都穿内衣(),而内衣之外,一般都会穿日常衣服装饰自己。整个装饰器模式的结构如下图所示:
四、字节流与字符流的区别
在开发中到底使用字节流好还是使用字符流好呢?
首先,在硬盘上保存文件或者进行传输的时候都是以字节的形式进行的,包括图片也是按字节完成。字符只有到了内存中才会形成。所以,如果要使用 Java 程序实现拷贝功能的话,应该选用字节流进行操作,并且采用边读边写的方式(节省内存)。
五、字节流与字符流的转换
虽然 Java 支持字节流和字符流,但是有时候需要在二者之间进行转换, InputStreamReader 和 OutputStream 这 2 个类是字节流和字符流之间相互转换的类。
5.1 InputStreamReader
用于将字节流的字节码转成字符,其中一个构造 方法:
1 | //用默认字符集创建一个 InputStreamReader 对象 |
5.2 OutputStreamWriter
用于将写入的字符转成字节后写入一个字节流,其中一个构造方法:
1 | //用默认字符集创建一个 OutputStreamWriter |
5.3 BufferedWriter 和 BufferedReader
为了频繁转换字节流和字符流,对以上2个类进行了封装, 即 BufferedWriter类封装了OutputStreamWriter类;BufferedReader类封装了InputStreamReader类;示例代码如下:
1 | BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out)); |