Java学习笔记 - 流

流与文件

Java中用于输入和输出的 应用编程接口(Application Programming Interface, API)。

  • 如何访问文件与目录,
  • 如何以二进制格式和文本格式来读写数据。
  • 对象序列化机制
  • 新I/O(nio)
  • 正则表达式

1.1 流

  • 可以从其中读入一个字节序列的对象称做输入流
  • 可以向其中写入一个字节序列的对象称做输出流

字节的来源可以是文件,也可以是网络连接,甚至是内存块。

抽象类InputStream和OutputStream构成了输入输出I/O类层次构成的基础

面向字节的流不便于处理以Unicode形式存储的信息
所以从抽象类Reader和Writer中继承出来了一个
专门用于处理Unicode字符的单独的类层次结构。
这些类拥有的读入和写入操作都是基于两字节的Unicode码元的, 而不是基于单字节的字符。

1.1.1读写字节

InputStream类有一个抽象方法。 abstract int read() 这个方法读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1。
在设计具体的输入流时,必须覆盖这个方法以提供适用的功能,
InputStream类还有若干个非抽象的方法。它们可以读入一个字节数组,
或者跳过大量的字节。这些方法都要调用抽象的read方法, 因此各个子类都只需要覆盖这个方法。

与此类似,OutputStream类定义了下面的抽象方法。
abstract void write(int b)

read和write方法在执行时都将阻塞,直至字节确实被读入或者写入。
available方法使我们可以去检查当前可读入的字节数量,这意味着下面这样的代码不可能被阻塞。

int byteAvailable = in.available();
if (byteAvailable > 0){
    byte[] data = new byte[byteAvailable];
    in.read(data);
}

当完成对流的读写时,应该通过调用close方法来关闭它。
即使某个流提供了使用原生的read和write功能的某些具体方法,
应用系统的程序员还是很少使用它们,因为大家最终要处理的数据是数字,字符串和对象,
而不是原生字节。

Java提供了众多从基本的InputStream和OutStream类导出的类, 这些类使我们可以处理那些以常用格式表示的数据,而不只是字节。

完整的流家族 按照使用方法可以划分成两类。
一类是处理字节和处理字符的两个单独层次。
InputStream和OutStream累可以读写单个字节或字节数组。
要想读写字符串和数字等,就需要功能更加强大的子类。
例如:DataInputStream,和DataOutputStream
可以以2进制格式读写所有的基本Java类型。
例如:ZipInputStream,和ZipOutputStream 可以以常用的ZIP压缩格式读写文件

另一方面,对于Unicode文本,可以使用抽象类Reader和Writer的子类。
Reader和Writer的基本方法与InputStream和OutputStream中的方法类似。

4个接口:Closeable,Flushable,Readable,Appendable
Reader,Writer,InputStream,OutputStream都实现了Closeable接口
注意:java.io.Closeable接口扩展了java.lang.AutoCloseable接口。
因此,对任何Closeable进行操作时,都可以使用try-with-resource语句。

Closeable接口的close方法只抛出IOException,
而AutoCloseable.close方法可以抛出任何异常。

OutputSream和Writer还实现了Flushable接口

Readable接口只有一个方法。
int read(CharBuffer cb)
CharBuffer类拥有按顺序和随机地进行读写方法,它表示一个内存中的缓冲区
或者一个内存映像的文件(1.7.1)

Appendable接口有两个用于添加单个字符和字符序列的方法:
Appendable append(char c)
Appendable append(CharSequence s)

1.1.3 组合流过滤器

FileInputStream和FileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流。 只需要向构造器提供文件名或者文件的完整路径。

java.io中的类都把相对路径名解释为以用户工作目录开始,
可以通过调用System.getProperty("user.dir")来获取。

由于反斜杠在java中是转义字符、正确写法为“C:\\Windows\\win.ini

DataInputStream类没有任何从文件中或许数据的方法。
Java的机制来分离两种职责。
FileInputStream可以从文件和其他更外部的位置上获取字节,
而DataInputStream可以将字节组黄德奥更有用的数据类。

FileInputStream fin = new FileInputStream("employee.dat");
DataInputStream din = new DataInputStream(fin);
double s = din.readDouble();

可以通过嵌套过滤器来添加多重功能,例如想使用缓冲机制

DataInputStream din = new DataInputStream(
    new BufferedInputStream(
        new FileInputStream("employee.dat")))

1.2 文本输入与输出 在保存数据时,可以选择二进制格式或文本格式。
- 本章讨论文本格式的I/O,
- 然后在第1.3节“读写二进制数据”中讨论二进制格式的I/O

在存储文本字符串时,需要考虑字符编码(character encoding)方式。
UTF-16编码方式中,字符串“1234”编码为“00 31 00 32 00 33 00 34”
也就是“1”这个字符使用一个“00 31”这个4个字节的16进制数来表示。
还有很多编码方法,比如“ISO 8859-1”这种在美国和西欧最常用的编码方式中。
字符串被写出为“31 32 33 34”,其中并没有任何0字节。

OutputStreamWriter类使用选定字符编码方式,把Unicode字符流转换成字节流。
- 字符流:
- 字节流:
就像把字符串(字符流)转化成二进制流(字节流)。
而InputstreamReader类将包含字节(用某种字符编码方式表示的字符)的输入流,转换成可以产生Unicode码元的读入器。
InputStreamReader in = new InputStreamReader(System.in);
这个输入流读入器缺省使用主机系统所使用的默认字符编码方式, 例如西欧采用ISO 8859-1
可以通过在InputStreamReader的构造器中进行指定的方式来选择不同的编码方式。
InputSteamReader in = new InputStreamReader(FileInputStream("krenlin.data"),"ISO8859_5");

1.2.1 如何写出文本输出

对于文本输出,可以使用PrintWriter。这个类拥有以文本格式打印字符串和数字的方法,
它甚至还有一个将PrintWriter链接到FileWriter的便捷方法。
PrintWriter out = new PrintWriter("employee.txt"); 等同于
PrintWriter out = new PrintWriter(new FileWriter("employee.txt");
用print就能打出内容。内容被输出到写出器out,然后被转换成字节,