第十一章IO/输入输出大多数应用程序都需要与外部设备进行数据交换,最常见的外部设备包含磁盘和网络,IO就是指应用程序对这些设备的数据输入与输出,在程序中,键盘被当作输入文件,显示器被当作输出文件使用。Java语言定义了许多类专门负责各种方式的输入输出,这些类都被放在java.io包中。11.1File类File类是IO包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操纵文件,通过调用File类提供的各种方法,我们能够创建、删除文件,重命名文件,判断文件的读写权限及是否存在,设置和查询文件的最近修改时间。在Java中,目录也被当作File使用,只是多了一些目录特有的功能——可以用list方法列出目录中的文件名。在Unix下的路径分隔符为(/),在Dos下的路径分隔符为(\),Java可以正确处理Unix和Dos的路径分隔符,即使我们在Windows环境下使用(/)作为路径分隔符,Java仍然能够正确处理。我们用下面的一个简单应用来演示一下File类用法,判断某个文件是否存在,存在则删除,不存在则创建,读者可以在Windows的资源管理器下观察到这个变化。程序清单:FileTest.javaimportjava.io.*;publicclassFileTest{publicstaticvoidmain(String[]args){Filef=newFile(c:\\1.txt);if(f.exists())f.delete();elsetry{f.createNewFile();}catch(Exceptione){System.out.println(e.getMessage());}System.out.println(Filename:+f.getName());System.out.println(Filepath:+f.getPath());System.out.println(Abspath:+f.getAbsolutePath());System.out.println(Parent:+f.getParent());System.out.println(f.exists()?exists:doesnotexist);System.out.println(f.canWrite()?iswriteable:isnotwriteable);System.out.println(f.canRead()?isreadable:isnotreadable);System.out.println(f.isDirectory()?is:isnot+adirectory);System.out.println(f.isFile()?isnormalfile:mightbeanamedpipe);System.out.println(f.isAbsolute()?isabsolute:isnotabsolute);System.out.println(Filelastmodified:+f.lastModified());System.out.println(Filesize:+f.length()+Bytes);}}当运行这个程序时会因为文件1.txt的存在和不存在而出现两种结果:结果1:Filename:1.txtFilepath:c:\1.txtAbspath:c:\1.txtParent:c:\existsiswriteableisreadableisnotadirectoryisnormalfileisabsoluteFilelastmodified:1051755103126Filesize:0Bytes结果2:Filename:1.txtFilepath:c:\1.txtAbspath:c:\1.txtParent:c:\doesnotexistisnotwriteableisnotreadableisnotadirectorymightbeanamedpipeisabsoluteFilelastmodified:0Filesize:0Bytes注:delete方法删除由File对象的路径所表示的磁盘文件。它只能删除普通文件,而不能删除目录,即使是空目录也不行。关于File类的其它方法,是没法死记硬背的,读者在需要时自己查看JDK文档,应该能够明白怎么使用。初步接触了File类,我们发现File类不能访问文件的内容,即不能够从文件中读取数据或往文件里写数据,它只能对文件本身的属性进行操作。11.2RandomAccessFile类RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类,它提供了众多的文件访问方法。RandomAccessFile类支持“随机访问”方式,我们可以跳转到文件的任意位置处读写数据。在你访问一个文件的时候,不想把文件从头读到尾,并希望像访问一个数据库一样的访问一个文本文件,使用RandomAccessFile类就是你的最佳选择。RandomAccessFile对象类有个位置指示器,指向当前读写处的位置,当读写n个字节后,文件指示器将指向这n个字节后的下一个字节处。刚打开文件时,文件指示器指向文件的开头处,我们可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。RandomAccessFile在等长记录格式文件的随机(相对顺序而言)读取时有很大的优势,但该类仅限于操作文件,不能访问其他的IO设备,如网络,内存映象等。有关RandomAccessFile类中的成员方法及使用说明,请参阅JDK文档。下面是一个使用RandomAccessFile的例子,往文件中写入三名员工的信息,然后按照第二名员工,第一名员工,第三名员工的先后顺序读出。RandomAccessFile可以以只读或读写方式打开文件,具体使用哪种方式取决于我们创建RandomAccessFile类对象的构造方式:newRandomAccessFile(f,rw);//读写方式newRandomAccessFile(f,r);//只读方式注:当我们的程序需要以读写的方式打开一个文件时,如果这个文件不存在,程序会为你创建它。我们还需要设计一个类来封装员工信息。一个员工信息就是文件中的一条记录,我们必须保证每条记录在文件中的大小相同,也就是每个员工的姓名字段在文件中的长度是一样的,我们才能够准确定位每条记录在文件中的具体位置。假设name中有八个字符,少于八个则补空格(这里我们用\u0000),多于八个则去掉后面多余的部分。由于年龄是整型数,不管这个数有多大,只要它不超过整型数的范围,在内存中都是占4个字节大小。程序清单:RandomFileTest.javaimportjava.io.*;publicclassRandomFileTest{publicstaticvoidmain(String[]args)throwsException{Employeee1=newEmployee(zhangsan,23);Employeee2=newEmployee(Lisi,24);Employeee3=newEmployee(Wangwu,25);RandomAccessFilera=newRandomAccessFile(c:\\1.txt,rw);ra.write(e1.name.getBytes());ra.writeInt(e1.age);ra.write(e2.name.getBytes());ra.writeInt(e2.age);ra.write(e3.name.getBytes());ra.writeInt(e3.age);ra.close();RandomAccessFileraf=newRandomAccessFile(c:\\1.txt,r);intlen=8;raf.skipBytes(12);//跳过第一个员工的信息,其中姓名8字节,年龄4字节System.out.println(第二个员工信息:);Stringstr=;for(inti=0;ilen;i++)str=str+(char)raf.readByte();System.out.println(name:+str);System.out.println(age:+raf.readInt());System.out.println(第一个员工的信息:);raf.seek(0);//将文件指针移动到文件开始位置str=;for(inti=0;ilen;i++)str=str+(char)raf.readByte();System.out.println(name:+str);System.out.println(age:+raf.readInt());System.out.println(第三个员工的信息:);raf.skipBytes(12);//跳过第二个员工信息str=;for(inti=0;ilen;i++)str=str+(char)raf.readByte();System.out.println(name:+str.trim());System.out.println(age:+raf.readInt());raf.close();}}classEmployee{Stringname;intage;finalstaticintLEN=8;publicEmployee(Stringname,intage){if(name.length()LEN){name=name.substring(0,8);}else{while(name.length()LEN)name=name+\u0000;}this.name=name;this.age=age;}}运行结果:第二个员工信息:name:Lisiage:24第一个员工的信息:name:zhangsanage:23第三个员工的信息:name:Wangwuage:25c盘还多了个文件1.txt:图11.1上面的这个程序完成了我们想要的功能,演示了RandomAccessFile类的作用。String.substring(intbeginIndex,intendIndex)方法可以用于取出一个字符串中的部分子字符串,要注意的一个细节是:子字符串中的第一个字符对应的是原字符串中的脚标为beginIndex处的字符,但最后的字符对应的是原字符串中的脚标为endIndex-1处的字符,而不是endIndex处的字符。在实际生活中,我们常用的数据库和数据库管理工具实际上就是这种原理。我们的1.txt就相当于数据库的数据文件,而我们这个程序提供了往这个数据文件写入和读取数据的功能。11.3节点流11.3.1理解流的概念数据流是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。我们将IO流类分为两个大类,节点流类和过滤流类(也叫处理流类)。程序用于直接操作目标设备所对应的类叫节点流类,程序也可以通过一个间接流类去调用节点流类,以达到更加灵活方便地读写各种类型的数据,这个间接流类就是过滤流类(也叫处理流类),我更喜欢称之为包装类。不管叫什么,都只是一个代名词而已,读者不要太在意,你可以根据自己的习惯和喜好来定。11.3.2InputStream与OutputStream程序可以从中连续读取字节的对象叫输入流,用InputStream类完成,程序能向其中连续写入字节的对象叫输出流,用OutputStream类完成。InputStream与OutputStream对象是两个抽象类,还不能