有关输入输出流(IOStream)的内容,分成两个文档。此篇文档主要介绍非节点流。

非节点流不能直接从一个节点(设备)读或写数据,而是通过节点流将数据保存到一个数组中,然后再通过节点流将数据写出去。它们每一种都有一些特殊的功能:

  • 数据流:将基本类型数据和字节之间进行相互转换
  • 缓冲流:可以提高其他流的读写效率
  • 快速输出流:对流进行快速输出
  • 转换流:将字节流转为字符流
  • 对象流:将对象序列化为字节,或反序列化为对象
  • 随机访问流:对文件进行随机访问

    数据流

    字节流是以字节文件单位进行读写,但很多时候这样并不方便。使用数据流能够对字节流进行增强。

数据流包括DataOutputStream和DataInputStream,可以将若干个字节自动转换为指定基本数据类型的数据,例如int、float、char等。也可以直接把一个数据自动转成指定基本数据类型再写出去。一般怎么读就怎么写,如果读和写的类型长度不一致——写比读长时会出现EOF异常;写比读短会出现数据错误。
数据流在使用时必须“包裹”一个字节流,能够增强这个字节流的功能,使得可以一次读取或写出一个具体类型的数据。数据流原理是通过移位操作来拿到每一个字节的值,放到数组中,再写出去。

  1. public class Test {
  2. public static void main(String[] args) throws Exception {
  3. DataInputStream dis = new DataInputStream(
  4. new FileInputStream("c.txt")); // 将从文件读取
  5. System.out.println(dis.readInt()); // 从文件读取一个int并打印
  6. DataOutputStream out = new DataOutputStream(
  7. new ByteArrayOutputStream()); // 将向ByteArray输出流写入
  8. out.writeLong(1000L); // 写入一个long类型数据
  9. out.flush();
  10. }
  11. }

缓冲流

缓冲流包括BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter,可以为构造时包裹的流对象设置一个默认大小的缓冲区数组,通过缓冲区进行读写,减少系统磁盘的IO次数,从而提高读写的效率。一般情况下,是读取效率最快的流。**

字节缓冲流

  1. BufferedInputStream bis = new BufferedInputStream(
  2. new FileInputStream("src/背影.txt")); // 增强文件输入流
  3. bis.skip(3); // 跳过3个字节不读
  4. int i = bis.read(); // 读一个字节
  5. byte[] bytes = new byte[200];
  6. bis.read(bytes); // 读200个字节
  7. System.out.println(Arrays.toString(bytes));
  8. BufferedOutputStream bos =
  9. new BufferedOutputStream(new FileOutputStream("kk.txt")); // 增强文件输出流
  10. bos.write("ABCBoy".getBytes()); // 向文件写入
  11. bos.flush();
  12. bos.close();

字节缓冲流有可能会拆分字符,故一般用于读取多媒体文件,如音频、视频等。

字符缓冲流

  1. BufferedReader br = new BufferedReader(
  2. new FileReader("src/背影.txt"));
  3. br.skip(br.readLine().length() + 2); // 跳过一些字符,换行和回车各是一个字符
  4. String line = null;
  5. while((line = br.readLine()) != null) {
  6. // 一次读取一行,读到换行或回车表示一行结束
  7. // 如果整个文件读完,返回null
  8. System.out.println(line);
  9. }
  10. BufferedWriter bw = new BufferedWriter(
  11. new FileWriter("src/ll.txt"));
  12. bw.write("ABCBoy");
  13. bw.flush();
  14. bw.close();

字符缓冲流主要使用其中的字符输入流,每次一般读取一行。而输出字符一般使用输出流。

输出流

输出流包括PrintOutputStream和PrintWriter。一般主要使用PrintWriter作为字符输出的首选,速度快。输出流增强字符流、字节流和文件。

  1. PrintWriter pw = new PrintWriter(
  2. new FileOutputStream("src/ll.txt", true)); // 准备写入一个文件
  3. for(int i = 0; i < 1000; i++) {
  4. pw.println("ABCBoy"); // 一次写入一行,并加上回车换行(\r\n,Windows中)/换行(\n,Linux中)
  5. }
  6. pw.flush();
  7. pw.close(); // PrintWriter的close方法没有抛出异常

转换流

转换流包括InputStreamReader和OutputStreamWriter,能够将字节流转换为字符流的同时,指定字符流的编码。

  1. FileInputStream fis = new FileInputStream("src/1.txt");
  2. InputStreamReader isr = new InputStreamReader(fis); // 默认编码为UTF-8
  3. InputStreamReader isr2 = new InputStreamReader(fis, "GBK"); // 指定编码GBK
  4. BufferedReader br = new BufferedReader(isr); // 套上缓冲流方便读一行
  5. System.out.println(br.readLine());
  6. br.close(); // 关闭最外层即可将内层的流关闭
  7. FileOutputStream fos = new FileOutputStream("2.txt");
  8. OutputStreamWriter osw = new OutputStreamWriter(fos); // 默认编码为UTF-8
  9. OutputStreamWriter osw2 = new OutputStreamWriter(fos, "GBK"); // 指定编码GBK
  10. PrintWriter pw = new PrintWriter(osw, true); // 在文件中追加
  11. pw.println("怎么费事"); // 按行输出
  12. pw.flush();
  13. pw.close(); // 关闭最外层即可将内层的流关闭

对象流

对象流又称序列化流/串行化流(Serializable Stream),可以将对象转化为可传输到文件、网络、数组、管道等节点的字节序列(序列化),或者序列转化回对象(反序列化)。

  • 要序列化一个对象,该对象的类须要实现Serializable接口(但并不需要实现任何方法)。如果是集合或数组,则内部元素需要实现序列化接口。
  • 如果要避免对对象中的一些属性进行序列化,可以对这些属性添加transient修饰符,使得对象在进行序列化的时忽略该属性。在反序列化后,该属性为默认值。
  • 反序列化重新生成的对象,其类型和数据等都和之前一致,但内存地址有可能不同。从文件中反序列化时,只能从一个文件中读取一个对象(包括集合和数组)。反序列化不会调用构造方法。(克隆对象也不会调用构造方法)
  • 如果对一个对象序列化后,如果没有指定版本号,并对这个对象的类进行了修改(版本号也会被同步修改),再反序列化时会报错。故实现序列化时需提供一个版本号。在反序列化前后若版本号一致,则不会报错。 ```java class Student implements Serializable { // 能够序列化的类需要实现这个接口 private static final long serialVersionUID = 1L; // 指定版本号 private int id = 2; private String name = “ABCBoy”; private transient String password = “123456”; // 被transient修饰的属性不会被序列化 // 构造方法等略 }

public class SerializableTest { public void test1() throws Exception { Student student = new Student(); // 序列化 将对象转换成一个可传输的字节序列 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(“3.txt”)); // 将对象序列化到一个文件中 oos.writeObject(student); oos.flush(); oos.close(); }

  1. public void test2() throws Exception {
  2. ObjectInputStream ois = new ObjectInputStream(
  3. new FileInputStream("3.txt")); // 将文件中的数据反序列化成对象
  4. // 反序列化创建对象不会调用构造方法
  5. Student student = (Student)ois.readObject();
  6. }
  7. public void test3() throws Exception {
  8. List<Student> students = new ArrayList<>();
  9. students.add(new Student());
  10. students.add(new Student());
  11. ObjectOutputStream ois = new ObjectOutputStream(
  12. new FileOutputStream("4.txt")); // 可以通过集合批量序列化对象到文件中
  13. ois.writeObject(students);
  14. ois.flush();
  15. ois.close();
  16. }
  17. public void test4() throws Exception {
  18. ObjectInputStream ois = new ObjectInputStream(
  19. new FileInputStream("4.txt"));
  20. Object obj = ois.readObject();
  21. Object obj2 = ois.readObject(); // 一个文件只能反序列化一次,报错
  22. }

}

  1. <a name="6EyV6"></a>
  2. ## 随机访问流
  3. 随机访问流只继承了Object类,没有继承之前IO流的类。它用于读写文件,可以定位到任意位置,也可以前后反复定位。它具有各种模式,且不需要进行刷新(flush)。<br />具有的模式包括:
  4. - r:只读模式。
  5. - rw:读写模式,若文件不存在则创建。
  6. - rws:实时读写模式,实时写入。
  7. - rwd:缓存读写模式,写入缓存,缓存满后再写入文件。
  8. ```java
  9. RandomAccessFile raf = new RandomAccessFile("src/1.txt", "rw");
  10. raf.seek(4); // 跳过4个字符
  11. raf.readLine(); // 读一行,读完返回null
  12. raf.write("aaaa".getBytes()); // 写并覆盖aaaa(4字节)
  13. raf.close();