![Java核心技术·卷Ⅱ:高级特性(原书第10版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/937/34339937/b_34339937.jpg)
2.1.3 组合输入/输出流过滤器
FileInputStream和FileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流,而你只需向其构造器提供文件名或文件的完整路径名。例如:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/046-i.jpg?sign=1738825337-kjmjG5xswpQHZeXEZWTOJ6O7IYgvn2Vo-0-7d9f48dc9e958012f23293fb7fb5f901)
这行代码可以查看在用户目录下名为“employee.dat”的文件。
提示:所有在java.io中的类都将相对路径名解释为以用户工作目录开始,你可以通过调用System.getProperty("user.dir")来获得这个信息。
警告:由于反斜杠字符在Java字符串中是转义字符,因此要确保在Windows风格的路径名中使用\\(例如,C:\\Windows\\win.ini)。在Windows中,还可以使用单斜杠字符(C:/Windows/win.ini),因为大部分Windows文件处理的系统调用都会将斜杠解释成文件分隔符。但是,并不推荐这样做,因为Windows系统函数的行为会因与时俱进而发生变化。因此,对于可移植的程序来说,应该使用程序所运行平台的文件分隔符,我们可以通过常量字符串java.io.File.separator获得它。
与抽象类InputStream和OutputStream一样,这些类只支持在字节级别上的读写。也就是说,我们只能从fin对象中读入字节和字节数组。
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/046-5-i.jpg?sign=1738825337-feZRnN0MjiCmse4ZWcKVXNo7nff1KCAi-0-9fe143c3fcd5f5efbac36780f9498163)
正如下节中看到的,如果我们只有DataInputStream,那么我们就只能读入数值类型:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/046-2-i.jpg?sign=1738825337-yYqZpLi4Hu6nA2fhVRNydEDAAwPfJM6y-0-295218d2ef8aac2f761075d865de6f5b)
但是正如FileInput Stream没有任何读入数值类型的方法一样,DataInputStream也没有任何从文件中获取数据的方法。
Java使用了一种灵巧的机制来分离这两种职责。某些输入流(例如FileInputStream和由URL类的openStream方法返回的输入流)可以从文件和其他更外部的位置上获取字节,而其他的输入流(例如DataInputStream)可以将字节组装到更有用的数据类型中。Java程序员必须对二者进行组合。例如,为了从文件中读入数字,首先需要创建一个FileInputStream,然后将其传递给DataInputStream的构造器:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/046-3-i.jpg?sign=1738825337-QXl4wa2bg5dZ6Ci8w8KbK8VLN5962OeP-0-23d3076a925fb1fce250b98b6197a497)
如果再次查看图2-1,你就会看到FilterInputStream和FilterOutputStream类。这些文件的子类用于向处理字节的输入/输出流添加额外的功能。
你可以通过嵌套过滤器来添加多重功能。例如,输入流在默认情况下是不被缓冲区缓存的,也就是说,每个对read的调用都会请求操作系统再分发一个字节。相比之下,请求一个数据块并将其置于缓冲区中会显得更加高效。如果我们想使用缓冲机制,以及用于文件的数据输入方法,那么就需要使用下面这种相当恐怖的构造器序列:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/046-4-i.jpg?sign=1738825337-KSa65TV3T3ebabFlv8LI5RD3FuqqokGC-0-6be77292fa0e8b498c9212f007734f03)
注意,我们把DataInputStream置于构造器链的最后,这是因为我们希望使用DataInputStream的方法,并且希望它们能够使用带缓冲机制的read方法。
有时当多个输入流链接在一起时,你需要跟踪各个中介输入流(intermediate input stream)。例如,当读入输入时,你经常需要预览下一个字节,以了解它是否是你想要的值。Java提供了用于此目的的PushbackInputStream:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/047-i.jpg?sign=1738825337-vI1F34t1YxJzqDdg9u1CVM3EADBT0zrA-0-415dd127d014ea78785e6a79f2de3644)
现在你可以预读下一个字节:
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/047-2-i.jpg?sign=1738825337-IPP256z3tmgixddvjP3bZuNTcbVv4Tf5-0-1c6aafc5001dce1c476511d37e37bf6a)
并且在它并非你所期望的值时将其推回流中。
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/047-3-i.jpg?sign=1738825337-QiIhNUIgd7XUz5ZRhZuDx42sgv5uQzRd-0-39ddbcf24a2186731efeb27d0a6611fd)
但是读入和推回是可应用于可回推(pushback)输入流的仅有的方法。如果你希望能够预先浏览并且还可以读入数字,那么你就需要一个既是可回推输入流,又是一个数据输入流的引用。
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/047-4-i.jpg?sign=1738825337-tsiDSZH6LwzKWvGgE7rvxmdLXzE6qFkr-0-8e4d1ee6cd94875cd97b2fe20775a848)
当然,在其他编程语言的输入/输出流类库中,诸如缓冲机制和预览等细节都是自动处理的。因此,相比较而言,Java就有一点麻烦,它必须将多个流过滤器组合起来。但是,这种混合并匹配过滤器类以构建真正有用的输入/输出流序列的能力,将带来极大的灵活性,例如,你可以从一个ZIP压缩文件中通过使用下面的输入流序列来读入数字(请参见图2-4):
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/047-5-i.jpg?sign=1738825337-z6uHkybsq95fBIc3adBwGmnHGnli6mxq-0-bc69030bdb05faccf954a3a9aaa5a4be)
(请查看2.3.3节以了解更多有关Java处理ZIP文件功能的知识。)
![](https://epubservercos.yuewen.com/F21227/18365861501241106/epubprivate/OEBPS/Images/2a4-i.jpg?sign=1738825337-PZlMCVTegAPWU0HVCDDqfoJ11F5fxthA-0-d3516a61ba264b4499e17d14c96d0f1e)
图2-4 过滤器流序列
java.io.FileInputStream 1.0
·FileInputStream(String name)
·FileInputStream(File file)
使用由name字符串或file对象指定路径名的文件创建一个新的文件输入流(File类在本章结尾处描述)。非绝对的路径名将按照相对于VM启动时所设置的工作目录来解析。
java.io.FileOutputStream 1.0
·FileOutputStream(String name)
·FileOutputStream(String name,boolean append)
·FileOutputStream(File file)
·FileOutputStream(File file,boolean append)
使用由name字符串或file对象指定路径名的文件创建一个新的文件输出流(File类在本章结尾处描述)。如果append参数为true,那么数据将被添加到文件尾,而具有相同名字的已有文件不会被删除;否则,这个方法会删除所有具有相同名字的已有文件。
java.io.BufferedInputStream 1.0
·BufferedInputStream(InputStream in)
创建一个带缓冲区的输入流。带缓冲区的输入流在从流中读入字符时,不会每次都对设备访问。当缓冲区为空时,会向缓冲区中读入一个新的数据块。
java.io.BufferedOutputStream 1.0
·BufferedOutputStream(OutputStream out)
创建一个带缓冲区的输出流。带缓冲区的输出流在收集要写出的字符时,不会每次都对设备访问。当缓冲区填满或当流被冲刷时,数据就被写出。
java.io.PushbackInputStream 1.0
·PushbackInputStream(InputStream in)
·PushbackInputStream(InputStream in,int size)
构建一个可以预览一个字节或者具有指定尺寸的回推缓冲区的输入流。
·void unread(int b)
回推一个字节,它可以在下次调用read时被再次获取。
参数:b 要再次读入的字节。