第二章 输入与输出
本章是对输入输出相关API的介绍
输入/输出流
在Java API中,可以从其中读入一个字节序列的对象称作输入流,可以向其中写入一个字节序列的对象称为输出流。
字节序列的来源地和目的地可以是文件、网络连接和内存块等。
以Unicode形式存储的信息使用一个单独的类进行处理,它的读入写出操作是基于两字节的Char值,即Unicode码元,而不是字节。
读写字节
InputStream类有一个read抽象方法,OutputStream类有一个write抽象方法,具体的输入输出流类需要覆盖方法以提供功能。
这两个方法执行时都会阻塞,available方法可以检查当前可读入的字节数量。完成读写操作后,应调用close方法关闭,关闭输出流时还会冲刷关于该输出流的缓冲区,该冲刷操作会将缓冲区中的内容送出,若不关闭文件可能会导致缓冲区中的内容一直都无法传递出。
组合输入/输出流过滤器
FileInputStream和FileOutputStream提供关于磁盘文件的输入输出流,对这两个类的构造器传入文件路径时,注意传入的路径中的分隔符需要使用双反斜杠进行转义。可以发现传入正斜杠时代码也不报错,但并不建议这样做。此外,对于可移植的程序应使用java.io.File.separator作为分隔符。
每种过滤器各司其职,在实际使用中需要将其嵌套结合,如为了读取增加了缓冲机制的文件中的数字。
DataInputStream din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat")));
文本输入与输出
文本输入输出时需要在构造器中传入编码方式。
文本输出器PrintWriter的构造方法中第三个参数是代表是否自动冲刷,若自动冲刷,则println被调用时缓冲区中的所有字符就会被发送到目的地。
正则“\\|”的意思|,因为|在正则中有特殊的含义需要转义,而\又需要另一个\进行转义,因此这里有两个反斜杠和一个分隔符。
读写二进制数据
DataInput和DataOutput接口定义了以二进制格式读写数组、字符等数据的方法,这些方法对于给定类型的值会分配固定的空间。不同处理器在内存存储整数和浮点数有高位在前和低位在前两种方式,Java发挥了其跨平台的特性,不管在何处理器上都采用高位在前。
随机访问文件
RandomAccessFile类可以在文件中的任何位置查找和写入数据,其中有一个表示下一个即将被读入或写出的字节所在的文件指针,可以通过seek方法指定指针位置,getFilePointer方法会返回文件指针当前位置。
ZIP文档
ZIP文档通常以压缩格式存储了一个或多个文件,每个ZIP文档都有一个头,包含每个文件名字和所使用压缩方法等信息。JAR文件是一种特殊的ZIP文件。
对象输入/输出流与序列化
对象序列化是用于将任何对象写出到输出流中以便之后读回。
和Cloneable接口类似,序列化对象所属类需要实验Serializable接口,但是Cloneable接口实现后需要重写clone方法,然而序列化接口中并不需要做任何事。每一个对象都是用一个序列号保存的。
当存储一个对象时,它所属类的描述也会被存储,包括
类名
序列化的版本唯一的ID,它时数据域类型和方法签名的指纹
描述序列化方法的标志集
对数据域的描述
指纹是通过对类、超类、接口、域类型和方法签名按照规范方式进行排序,然后将上述数据进行安全散列(SHA)获得。
对象流输出中包含所有对象的类型和数据域,每个对象都被赋予了一个序列号,相同对象的重复出现会存储为这个对象的序列号的引用。
transient关键字可以用于标记不可序列化的域。
版本管理
如果一个类具有名为serialVersionUID的静态数据成员,其指纹就会是该值而不必重新计算。定义该值后,序列化系统就可以读入这个类的对象的不同版本。
为克隆使用序列化
序列化机制为克隆对象提供了一种简便方法,只要对应的类是可序列化的即可。
操作文件
Path
Path接口是对File类的改进,Path表示的是一个目录名序列,以根部件开始的路径是绝对路径,否则就是相对路径。
Paths的get方法是接收一个或多个字符串并将其用默认文件系统的路径分隔符连接。
p.resolve(q)方法按照以下规则返回一个路径:
如果q是绝对路径,那么结果就是q。
否则返回p后面跟着q的结果。
resolveSibling方法可以解析出传入路径的兄弟路径。relativize方法可以给出以目标路径针对基准路径得到的相对路径。normilize方法用于清除路径中的冗余内容,toAbsolutePath方法返回给定路径的绝对路径。
创建文件和目录
创建新目录可以调用Files.createDirectory方法,传入的路径只允许最后一个部件不存在。若要创建中间路径不存在的目录,应调用Files.createDirectories方法。Files.CreateFiles方法可以创建一个空文件。
访问目录中的项
Files.list方法会返回一个可以读取目录中各个项的Stream
如果要处理目录中的子目录,需要使用Files.walk方法。
使用目录流
glob模式
*.java:匹配当前目录中所有的Java文件。
**.java:匹配在所有子目录中的Java文件。
?.java:匹配一个字符名的Java文件。
Test[0-9A-F].java:匹配诸如Test1.java,Test2.java的文件。
*.{java,class}:匹配所有的Java和Class文件。
\:转义
Windows中需要对反斜杠转移两次,一次是Java字符串的转义,一次是glob语法的转义。
内存映射文件
- 从文件中获得通道。
- 从通道中生成缓冲区。
- 使用get/put方法读写数据。
缓冲区数据结构
使用缓冲区的目的是执行“写,然后读入”的循环。一个缓冲区初始时位置为0,界限等于容量。不断调用put将数据存入直到耗尽数据或达到容量大小时,切换为读入操作。调用flip方法将界限设为所存数据所在的当前位置,将位置复位到0,在remaining方法返回正数时不断调用get读取缓冲区中数据。数据读取完毕后,调用clear方法将位置复位到0,界限复位到容量。
正则表达式
正则表达式时用于指定字符串的模式。
- 字符类是一个括在括号中的可选的字符集,例如[0-9],这个括号必须是第一项,这里的-表示一个范围,且只能在第一项或最后一项。^表示补集,可以在除开始位置外的任意位置。
- 大部分字符都与自身匹配。。
- .符号匹配任何字符。
- \作为转义字符,如要匹配点号本身,需要在点号前加\。
- 对于一行而言,^匹配开头,$匹配结尾。
- X和Y都是正则表达式,XY表示任何X的匹配后跟随Y的匹配,X|Y表示任何X或Y的匹配。
- X+是一个或多个X,X*是0个或多个X,X?是0个或1个X。
- ?表示吝啬匹配,+表示贪婪匹配。
- 可以用括号分割表达式生成子表达式,嵌套群组的群组0是整个输入,后续群组按照左括号为序排列。