外文资料译文及原文JavaJava I/O 系统对编程语言的设计者来说,创建一套好的输入输出(I/O)系统,是一项难度极高的任务。
这一点可以从解决方案的数量之多上看出端倪。
这个问题难就难在它要面对的可能性太多了。
不仅是因为有那么多I/O的源和目地(文件,控制台,网络连接等等),而且还有很多方法(顺序的『sequential』,随机的『random-access』,缓存的『buffered』,二进制的『binary』,字符方式的『character』,行的『by lines』,字的『by words』,等等)。
Java类库的设计者们用"创建很多类"的办法来解决这个问题。
坦率地说Java I/O系统的类实在是太多了,以至于初看起来会把人吓着(但是,具有讽刺意味的是,这种设计实际上是限制了类的爆炸性增长)。
此外,Java在1.0版之后又对其I/O类库作了重大的修改,原先是面向byte的,现在又补充了面向Unicode字符的类库。
为了提高性能,完善功能,JDK 1.4又加了一个nio(意思是"new I/O"。
这个名字会用上很多年)。
这么以来,如果你想对Java的I/O 类库有个全面了解,并且做到运用自如,你就得先学习大量的类。
此外,了解I/O类库的演化的历史也是相当重要的。
可能你的第一反应是"别拿什么历史来烦我了,告诉我怎么用就可以了!"但问题是,如果你对这段历史一无所知,很快就会被一些有用或是没用的类给搞糊涂了。
本章会介绍Java标准类库中的各种I/O类,及其使用方法。
File 类在介绍直接从流里读写数据的类之前,我们先介绍一下处理文件和目录的类。
File类有一个极具欺骗性的名字;或许你会认为这是一个关于文件的类,但它不是。
你可以用它来表示某个文件的名字,也可以用它来表示目录里一组文件的名字。
如果它表示的是一组文件,那么你还可以用list( )方法来进行查询,让它会返回String数组。
由于元素数量是固定的,因此数组会比容器更好一些。
如果你想要获取另一个目录的清单,再建一个File对象就是了。
实际上,叫它"FilePath"可能会更好一些。
下面我们举例说明怎样使用这个类及其相关的FilenameFilter接口。
目录列表器假设你想看看这个目录。
有两个办法。
一是不带参数调用list( )。
它返回的是File对象所含内容的完整清单。
但是,如果你要的是一个"限制性列表(restricted list)"的话——比方说,你想看看所有扩展名为.java的文件——那么你就得使用"目录过滤器"了。
这是一个专门负责挑选显示File对象的内容的类。
下面就是源代码。
看看,用了java.utils.Arrays.sort( )和11章的AlphabeticComparator之后,我们没费吹灰之力就对结果作了排序(按字母顺序)://: c12:DirList.java// Displays directory listing using regular expressions.// {Args: "D.*\.java"}import java.io.*;import java.util.*;import java.util.regex.*;import com.bruceeckel.util.*;public class DirList {public static void main(String[] args) {File path = new File(".");String[] list;if(args.length == 0)list = path.list();elselist = path.list(new DirFilter(args[0]));Arrays.sort(list, new AlphabeticComparator());for(int i = 0; i < list.length; i++)System.out.println(list[i]);}}class DirFilter implements FilenameFilter {private Pattern pattern;public DirFilter(String regex) {pattern = pile(regex);}public boolean accept(File dir, String name) {// Strip path information, search for regex:return pattern.matcher(new File(name).getName()).matches();}} ///:~DirFilter实现了FilenameFilter接口。
我们来看看FilenameFilter究竟有多简单:public interface FilenameFilter {boolean accept(File dir, String name);}也就是说,这类对象的任务就是提供一个accept( )的方法。
之所以要创建这个类,就是要给list( )提供一个accept( )方法,这样当list( )判断该返回哪些文件名的时候,能够"回过头来调用"accept( )方法。
因此,这种结构通常被称为回调(callback)。
更准确地说,由于list( )实现了基本功能,而FilenameFilter提供了"对外服务所需的算法",因此这是一种"策略模式(Strategy Pattern)"。
由于list( )拿FilenameFilter对象当参数,因此你可以将任何实现FilenameFilter接口的对象传给它,并以此(甚至是在运行时)控制list( )的工作方式。
回调能提高程序的灵活性。
DirFilter还告诉我们,interface只是包含了一些方法,它没说你只能写这些方法。
(但是,你至少要定义接口里有的方法。
) 这里我们还定义了DirFilter的构造函数。
accept( )方法需要两个参数,一个是File对象,表示这个文件是在哪个目录里面的;另一个是String,表示文件名。
虽然你可以忽略它们中的一个,甚至两个都不管,但是你大概总得用一下文件名吧。
记住,list( )会对目录里的每个文件调用accept( ),并以此判断是不是把它包括到返回值里;这个判断依据就是accept( )的返回值。
切记,文件名里不能有路径信息。
为此你只要用一个String对象来创建File 对象,然后再调用这个File对象的getName( )就可以了。
它会帮你剥离路径信息(以一种平台无关的方式)。
然后再在accept( )里面用正则表达式(regularexpression)的matcher对象判断,regex是否与文件名相匹配。
兜完这个圈子,list( )方法返回了一个数组。
匿名内部类这是用匿名内部类(详见第八章)来重写程序的绝佳机会。
下面我们先创建一个返回FilenameFilter的filter( )方法。
//: c12:DirList2.java// Uses anonymous inner classes.// {Args: "D.*\.java"}import java.io.*;import java.util.*;import java.util.regex.*;import com.bruceeckel.util.*;public class DirList2 {public static FilenameFilter filter(final String regex) { // Creation of anonymous inner class:return new FilenameFilter() {private Pattern pattern = pile(regex);public boolean accept(File dir, String name) {return pattern.matcher(new File(name).getName()).matches();}}; // End of anonymous inner class}public static void main(String[] args) {File path = new File(".");String[] list;if(args.length == 0)list = path.list();elselist = path.list(filter(args[0]));Arrays.sort(list, new AlphabeticComparator());for(int i = 0; i < list.length; i++)System.out.println(list[i]);}} ///:~注意,filter( )的参数必须是final的。
要想在匿名内部类里使用其作用域之外的对象,只能这么做。
这是对前面所讲的代码的改进,现在FilenameFilter类已经与DirList2紧紧地绑在一起了。
不过你还可以更进一步,把这个匿名内部类定义成list( )的参数,这样代码会变得更紧凑://: c12:DirList3.java// Building the anonymous inner class "in-place."// {Args: "D.*\.java"}import java.io.*;import java.util.*;import java.util.regex.*;import com.bruceeckel.util.*;public class DirList3 {public static void main(final String[] args) {File path = new File(".");String[] list;if(args.length == 0)list = path.list();elselist = path.list(new FilenameFilter() {private Pattern pattern = pile(args[0]);public boolean accept(File dir, String name) {return pattern.matcher(new File(name).getName()).matches();}});Arrays.sort(list, new AlphabeticComparator());for(int i = 0; i < list.length; i++)System.out.println(list[i]);}} ///:~现在该轮到main( )的参数成final了,因为匿名内部类要用它的arg[0]了。