Java输入输出流_第1页
Java输入输出流_第2页
Java输入输出流_第3页
Java输入输出流_第4页
Java输入输出流_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第7章 IO/输入输出2037.1 File类2037.2 RandomAccessFile类2057.3 节点流2077.3.1 理解流的概念2077.3.2 InputStream与OutputStream208指点迷津:1.如何选择输入与输出 2.为什么要调用close方法 多学两招:IO中的缓冲区7.3.3 FileInputStream与FileOutputStream2107.3.4 Reader与Writer211独家见解:隐含的缓冲区7.3.5 PipedInputStream与PipedOutputStream212独家见解:管道流类的作用 7.3.6 ByteArrayIn

2、putStream与ByteArrayOutputStream2147.3.7 IO程序代码的复用2167.4 过滤流与包装类2187.4.1 理解包装类的概念与作用2187.4.2 BufferedInputStream与 BufferedOuputStream219脚下留心:使用mark时应考虑的问题 7.4.3 DataInputStream与 DataOutputStream2197.4.4 PrintStream222指点迷津:何谓格式化输出7.4.5 ObjectInputStream与ObjectOutputStream223指点迷津:文件中的数据可读但不见得可用7.4.6 字节

3、流与字符流的转换2257.4.7 IO包中的类层次关系图2277.5 IO中的高级应用2287.5.1 字符集的编码问题228指点迷津:如何处理字符乱码问题7.5.2 Decorator 设计模式2387.5.3 Java虚拟机读写其他进程的数据239多学两招:提高程序的运行效率第7章 IO/输入输出大多数应用程序都需要与外部设备进行数据交换,最常见的外部设备包含磁盘和网络,IO就是指应用程序对这些设备的数据输入与输出,在程序中,键盘被当作输入文件,显示器被当作输出文件使用。Java语言定义了许多类专门负责各种方式的输入输出,这些类都被放在java.io包中。7.1 File类File类是IO

4、包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操纵文件,通过调用File类提供的各种方法,我们能够创建、删除文件,重命名文件,判断文件的读写权限及是否存在,设置和查询文件的最近修改时间。在Java中,目录也被当作File使用,只是多了一些目录特有的功能可以用list方法列出目录中的文件名。在Unix下的路径分隔符为(/),在Dos下的路径分隔符为(),Java可以正确处理Unix和Dos的路径分隔符,即使我们在Windows环境下使用(/)作为路径分隔符,Java仍然能够正确处理。我们用下面的一个简单应用来演示一下File类用法,判断某个文件是否存在,存在则删除,不存在

5、则创建,读者可以在Windows的资源管理器下观察到这个变化。程序清单:FileTest.javaimport java.io.*;public class FileTestpublic static void main(String args)File f=new File("c:1.txt");if(f.exists()f.delete();elsetryf.createNewFile();catch(Exception e)System.out.println(e.getMessage();System.out.println("File name:&quo

6、t;+f.getName();System.out.println("File path:"+f.getPath();System.out.println("Abs path:"+f.getAbsolutePath();System.out.println("Parent:"+f.getParent();System.out.println(f.exists()?"exists":"does not exist");System.out.println(f.canWrite()?"is

7、 writeable":"is not writeable");System.out.println(f.canRead()?"is readable":"is not readable");System.out.println(f.isDirectory()?"is ":"is not"+" a directory");System.out.println(f.isFile()?"is normal file":"might be a

8、 named pipe");System.out.println(f.isAbsolute()?"is absolute":"is not absolute");System.out.println("File last modified:"+f.lastModified();System.out.println("File size:"+f.length()+" Bytes");当运行这个程序时会因为文件1.txt的存在和不存在而出现两种结果:结果1:File name:1.txtF

9、ile path:c:1.txtAbs path:c:1.txtParent:c:existsis writeableis readableis not a directoryis normal fileis absoluteFile size:0 Bytes结果2:File name:1.txtFile path:c:1.txtAbs path:c:1.txtParent:c:does not existis not writeableis not readableis not a directorymight be a named pipeis absoluteFile last modi

10、fied:0File size:0 Bytes注:delete方法删除由File对象的路径所表示的磁盘文件。它只能删除普通文件,而不能删除目录,即使是空目录也不行。关于File类的其它方法,是没法死记硬背的,读者在需要时自己查看JDK文档,应该能够明白怎么使用。初步接触了File类,我们发现File类不能访问文件的内容,即不能够从文件中读取数据或往文件里写数据,它只能对文件本身的属性进行操作。7.2 RandomAccessFile类RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类,它提供了众多的文件访问方法。RandomAccessFile类支持“随机访问”方

11、式,我们可以跳转到文件的任意位置处读写数据。在你访问一个文件的时候,不想把文件从头读到尾,并希望像访问一个数据库一样的访问一个文本文件,使用RandomAccessFile类就是你的最佳选择。RandomAccessFile对象类有个位置指示器,指向当前读写处的位置,当读写n 个字节后,文件指示器将指向这n个字节后的下一个字节处。刚打开文件时,文件指示器指向文件的开头处,我们可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。RandomAccessFile在等长记录格式文件的随机(相对顺序而言)读取时有很大的优势,但该类仅限于操作文件,不能访问其他的IO设备,如网络,内存映象等。有

12、关RandomAccessFile类中的成员方法及使用说明,请参阅JDK文档。下面是一个使用RandomAccessFile的例子,往文件中写入三名员工的信息,然后按照第二名员工,第一名员工,第三名员工的先后顺序读出。RandomAccessFile可以以只读或读写方式打开文件,具体使用哪种方式取决于我们创建RandomAccessFile类对象的构造方式:new RandomAccessFile(f,"rw"); /读写方式new RandomAccessFile(f,"r"); /只读方式注:当我们的程序需要以读写的方式打开一个文件时,如果这个文件不

13、存在,程序会为你创建它。我们还需要设计一个类来封装员工信息。一个员工信息就是文件中的一条记录,我们必须保证每条记录在文件中的大小相同,也就是每个员工的姓名字段在文件中的长度是一样的,我们才能够准确定位每条记录在文件中的具体位置。假设name中有八个字符,少于八个则补空格(这里我们用"u0000"),多于八个则去掉后面多余的部分。由于年龄是整型数,不管这个数有多大,只要它不超过整型数的范围,在内存中都是占4个字节大小。程序清单:RandomFileTest.javaimport java.io.*;public class RandomFileTestpublic stati

14、c void main(String args) throws Exception Employee e1 = new Employee("zhangsan",23);Employee e2 = new Employee("Lisi",24);Employee e3 = new Employee("Wangwu",25);RandomAccessFile ra=new RandomAccessFile("c:1.txt","rw");ra.write(.getBytes();ra.

15、writeInt(e1.age);ra.write(.getBytes();ra.writeInt(e2.age);ra.write(.getBytes();ra.writeInt(e3.age);ra.close();RandomAccessFile raf=new RandomAccessFile("c:1.txt","r");int len=8;raf.skipBytes(12); /跳过第一个员工的信息,其中姓名8字节,年龄4字节System.out.println("第二个员工信息:");Stri

16、ng str=""for(int i=0;i<len;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(int i=0;i<len;i+)str=str+(char)raf.readByte

17、();System.out.println("name:"+str);System.out.println("age:"+raf.readInt();System.out.println("第三个员工的信息:");raf.skipBytes(12); /跳过第二个员工信息str=""for(int i=0;i<len;i+)str=str+(char)raf.readByte();System.out.println("name:"+str.trim();System.out.printl

18、n("age:"+raf.readInt();raf.close();class EmployeeString name;int age;final static int LEN=8;public Employee(String name,int age)if(name.length()>LEN)name = name.substring(0,8);elsewhile(name.length()<LEN)name=name+"u0000"=name;this.age=age;运行结果:第二个员工信息:name:Lisiage

19、:24第一个员工的信息:name:zhangsanage:23第三个员工的信息:name:Wangwuage:25c盘还多了个文件1.txt:图7.1上面的这个程序完成了我们想要的功能,演示了RandomAccessFile类的作用。String.substring(int beginIndex,int endIndex)方法可以用于取出一个字符串中的部分子字符串,要注意的一个细节是:子字符串中的第一个字符对应的是原字符串中的脚标为beginIndex处的字符,但最后的字符对应的是原字符串中的脚标为endIndex-1处的字符,而不是endIndex处的字符。在实际生活中,

20、我们常用的数据库和数据库管理工具实际上就是这种原理。我们的1.txt就相当于数据库的数据文件,而我们这个程序提供了往这个数据文件写入和读取数据的功能。7.3 节点流7.3.1 理解流的概念数据流是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。数据写入程序可以是一段、一段地向数据流管道中写入数据,这些数据段会按先后顺序形成一个长的数据流。对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时

21、的效果都是完全一样的。我们将IO流类分为两个大类,节点流类和过滤流类(也叫处理流类)。程序用于直接操作目标设备所对应的类叫节点流类,程序也可以通过一个间接流类去调用节点流类,以达到更加灵活方便地读写各种类型的数据,这个间接流类就是过滤流类(也叫处理流类),我更喜欢称之为包装类。不管叫什么,都只是一个代名词而已,读者不要太在意,你可以根据自己的习惯和喜好来定。7.3.2 InputStream与OutputStream程序可以从中连续读取字节的对象叫输入流,用InputStream类完成,程序能向其中连续写入字节的对象叫输出流,用OutputStream类完成。InputStream与Outpu

22、tStream对象是两个抽象类,还不能表明具体对应哪种IO设备。它们下面有许多子类,包括网络,管道,内存,文件等具体的IO设备,如FileInputStream类对应的就是文件输入流,是一个节点流类,我们将这些节点流类所对应的IO源和目标称为流节点(Node)。F指点迷津:很多人搞不清程序要将A文件的内容写入B文件中,程序对A文件的操作所用的是输出类还是输入类这个问题。读者也先自己想想,再记住下面的话,输入输出类是相对程序而言的,而不是代表文件的,所以我们应该创建一个输入类来完成对A文件的操作,创建一个输出类来完成对B文件的操作。InputStream定义了Java的输入流模型。该类中的所有方

23、法在遇到错误的时候都会引发IOException异常,下面是InputStream类中方法的一个简要说明:ü int read()返回下一个输入字节的整型表示,,如果返回-1表示遇到流的末尾,结束。ü int read(byte b)读入b.length个字节放到b中并返回实际读入的字节数。ü int read(byte b,int off,int len) 这个方法表示把流中的数据读到,数组b中,第off个开始的len个数组元素中.ü long skip(long n) 跳过输入流上的n个字节并返回实际跳过的字节数。ü int availab

24、ale() 返回当前输入流中可读的字节数。ü void mark(int readlimit)在输入流的当前位置处放上一个标志,允许最多再读入readlimit个字节。ü void reset() 把输入指针返回到以前所做的标志处。ü boolean markSupported() 如果当前流支持mark/reset操作就返回true。ü void close() 在操作完一个流后要使用此方法将其关闭, 系统就会释放与这个流相关的资源。InputStream是一个抽象类,程序中实际使用的是它的各种子类对象。不是所有的子类都会支持InputStream中定

25、义的某些方法的,如skip,mark,reset等,这些方法只对某些子类有用。F指点迷津:一个对象在没有引用变量指向它时会变成垃圾,最终会被垃圾回收器从内存中清除。对于我们创建的流对象,干嘛还要“调用close方法将它关闭,以释放与其相关的资源”呢?这相关的资源到底是些什么呢?我们在程序中创建的对象都是对应现实世界中有形或无形的事物,计算机操作系统所产生的东西当然也是现实世界中的事物,也就是说,程序中的对象也可以对应计算机操作系统所产生的一个其他东西,专业地说,这些东西叫资源,流就是操作系统产生的一种资源。当我们在程序中创建了一个IO流对象,同时系统内也会创建了一个叫流的东西,在这种情况下,计

26、算机内存中实际上产生了两个事物,一个是Java程序中的类的实例对象,一个是系统本身产生的某种资源,我们以后讲到的窗口,Socket等都是这样的情况。Java垃圾回收器只能管理程序中的类的实例对象,没法去管理系统产生的资源,所以程序需要调用close方法,去通知系统释放其自身产生的资源。OutputStream是一个定义了输出流的抽象类,这个类中的所有方法均返回void,并在遇到错误时引发IOException异常。下面是OutputStream的方法:ü void write(int b) 将一个字节写到输出流。注意,这里的参数是int型,它允许write使用表达式而不用强制转换成b

27、yte型。ü void write(byte b) 将整个字节数组写到输出流中。ü void write(byte b,int off,int len) 将字节数组b中的从off开始的len个字节写到输出流。ü void flush彻底完成输出并清空缓冲区。ü void close关闭输出流。&多学两招:计算机访问外部设备,要比直接访问内存慢得多,如果我们每一次write方法的调用都直接写到外部设备(如直接写入硬盘文件),CPU就要花费更多的时间等待外部设备;如果我们开辟一个内存缓冲区,程序的每一次write方法都是写到这个内存缓冲区中,只有这个

28、缓冲区被装满后,系统才将这个缓冲区的内容一次集中写到外部设备。使用内存缓冲区有两个方面的好处,一是有效地提高了CPU的使用率,二是write并没有马上真正写入到外设,我们还有机会回滚部分写入的数据。使用缓冲区,能提高整个计算机系统的效率,但也会降低单个程序自身的效率,由于有这么一个中间缓冲区,数据并没有马上写入到目标中去,例如在网络流中,就会造成一些滞后。对于输入流,我们也可以使用缓冲区技术。在程序与外部设备之间到底用不用缓冲区,是由编程语言本身决定的,我们通常用的C语言默认情况下就会使用缓冲区,而在Java语言中,有的类使用了缓冲区,有的类没有使用缓冲区,我们还可以在程序中使用专门的包装类来

29、实现自己的缓冲区。flush方法就是用于即使在缓冲区没有满的情况下,也将缓冲区的内容强制写入到外设,习惯上称这个过程为刷新。可见,flush方法不是对所有的OutputStream子类都起作用的,它只对那些使用缓冲区的OutputStream子类有效。如果我们调用了close方法,系统在关闭这个流之前,也会将缓冲区的内容刷新到硬盘文件的。作者开发过一个邮件服务器程序,需要7*24小时不间断工作,这个服务器程序要面对internet上各种可能的非法格式的数据输入和攻击,而我的程序正好又没考虑到某种非法格式的数据,一旦碰到这样的情况,程序就会崩溃。有经验的人都知道,为了找出服务器程序崩溃的原因,我

30、们可以将程序每次接收到的数据都记录到一个文件中,当服务器程序崩溃后,我们便打开这个记录文件,查看最后记录的那条数据,这个数据就是让我的程序毙命的罪魁祸首,然后拿着这条数据一步步测试我们的程序,就很容易找出程序中的问题了。遗憾的是,我每次用最后记录的这条数据测试我的程序,程序均安然无恙。最后,我发现就是因为有缓冲区的原因,缓冲区的内容还没来得及刷新到硬盘文件,程序就崩溃了,所以,文件中并没有记录最后接收到的那些数据,我在文件中看到的最后以条记录并不是真正最后接收到的那条数据。发现了这个原因,我修改程序,在每一次调用write语句后,都立即调用flush语句,这样,我就终于找到了肇事元凶,并修复了

31、程序的这个漏洞。尽管我以前从来没有真正认真思考和编程试验过缓冲区问题,但是正因为还有那么一点点概念和印象,所以,在出现问题时,我才能从多方面去思考并最终解决问题。我建议读者花更多的时间去开阔自己的知识面和思维,了解更多的原理,而不是去花大量时间去死记硬背某些细节和术语,特别是一个类中的每个函数名的具体拼写、具体的参数形式,Java中有哪些关键字等这些死板的东西,只要有个印象就足够了。7.3.3 FileInputStream与FileOutputStream这两个流节点用来操作磁盘文件,在创建一个FileInputStream对象时通过构造函数指定文件的路径和名字,当然这个文件应当是存在的和可

32、读的。在创建一个 FileOutputStream对象时指定文件如果存在将要被覆盖。下面是对同一个磁盘文件创建FileInputStream对象的两种方式。其中用到的两个构造函数都可以引发FileNotFoundException异常:FileInputStream inOne=new FileInputStream("hello.test");File f = new File("hello.test"); FileInputStream inTwo = new FileInputStream(f);尽管第一个构造函数更简单,但第二个构造函数允许在把文

33、件连接到输入流之前对文件做进一步分析。FileOutputStream对象也有两个和FileInputStream对象具有相同参数的构造函数,创建一个FileOutputStream对象时,可以为其指定还不存在的文件名,但不能是存在的目录名,也不能是一个已被其他程序打开了的文件。FileOutputStream先创建输出对象,然后再准备输出。其实在上一章中讲Properties类的时候,我们已经使用过这两个类。在下面的例子中,我们用FileOutputStream类向文件中写入一串字符,并用FileInputStream读出。程序清单:FileStream.javaimport java.io

34、.*;public class FileStreampublic static void main(String args)File f = new File("hello.txt"); tryFileOutputStream out = new FileOutputStream(f);byte buf="".getBytes();out.write(buf);out.close();catch(Exception e)System.out.println(e.getMessage();tryFileInputStream in = new FileIn

35、putStream(f);byte buf = new byte1024;int len = in.read(buf);System.out.println(new String(buf,0,len);catch(Exception e)System.out.println(e.getMessage();编译运行上面的程序,我们能够看到当前目录下产生了一个hello.txt的文件,用记事本程序打开这个文件,能看到我们写入的内容。随后,程序开始读取文件中的内容,并将读取到的内容打印出来。在这个例子中,我们演示了怎样用FileOutputStream往一个文件中写东西和怎样用FileInputSt

36、ream从一个文件中将内容读出来。有一点不足的是,这两个类都只提供了对字节或字节数组进行读取的方法,对于字符串的读写,我们还需要进行额外的转换。7.3.4 Reader与WriterJava中的字符是unicode编码,是双字节的,而InputStream与OutputStream是用来处理字节的,在处理字符文本时不太方便,需要编写额外的程序代码。Java为字符文本的输入输出专门提供了一套单独的类,Reader、Writer两个抽象类与InputStream、OutputStream两个类相对应,同样,Reader、Writer下面也有许多子类,对具体IO设备进行字符输入输出,如FileRea

37、der就是用来读取文件流中的字符。对于Reader和Writer,我们就不过多的说明了,大体的功能和InputStream、OutputStream两个类相同,但并不是它们的代替者,只是在处理字符串时简化了我们的编程。我们上面的程序改为使用FileWriter和FileReader来实现,修改后的程序代码如下:import java.io.*;public class FileStreampublic static void main(String args)File f = new File("hello.txt"); tryFileWriter out = new Fi

38、leWriter(f);out.write("");out.close();catch(Exception e)System.out.println(e.getMessage();tryFileReader in = new FileReader(f);char buf = new char1024;int len = in.read(buf);System.out.println(new String(buf,0,len);catch(Exception e)System.out.println(e.getMessage();我们发现编译运行后的结果与先前没有什么两样,由

39、于FileWriter可以往文件中写入字符串,我们不用将字符串转换为字节数组。相对于FileOutputStream来说,使用FileReader读取文件中的内容,并没有简化我们的编程工作,FileReader的优势,要结合我们后面讲到的包装类才能体现出来。$独家见解:我们将程序中的out.close();语句注释掉后编译运行,在hello.txt文件中没有看到out.write语句写入的字符串,这可能就是我们前面谈到的缓冲区的原因,我们将out.close()改为out.flush后编译运行,在hello.txt文件中又能够看到out.write语句写入的字符串了,这更加证明了FileWri

40、ter使用了缓冲区。在使用FileOutputStream的例子程序中,我们同样注释掉out.close();语句,编译运行后,在hello.txt文件中能够看到out.write语句写入的字符串,这说明FileOutputStream没有使用缓冲区。7.3.5 PipedInputStream与PipedOutputStream一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道,PipedOutStream可以向管道中写入数据,PipedInputStream可以从管道中读取PipedOutputStream写入的数据。这两个类

41、主要用来完成线程之间的通信,一个线程的PipedInputStream 对象能够从另外一个线程的PipedOutputStream对象中读取数据。请看下面的例子:程序清单:PipeStreamTest.javaimport java.io.*;public class PipeStreamTest public static void main(String args) try Thread t1=new Sender(); Thread t2=new Receiver(); PipedOutputStream out = t1.getOutputStream(); PipedInputStr

42、eam in = t2.getInputStream(); out.connect(in); t1.start(); t2.start(); catch(IOException e) System.out.println(e.getMessage(); class Sender extends Thread private PipedOutputStream out=new PipedOutputStream(); public PipedOutputStream getOutputStream() return out; public void run() String s=new Stri

43、ng("hello,receiver ,how are you"); try out.write(s.getBytes(); out.close(); catch(IOException e) System.out.println(e.getMessage(); class Receiver extends Thread private PipedInputStream in=new PipedInputStream(); public PipedInputStream getInputStream() return in; public void run() String

44、 s=null; byte buf = new byte1024; try int len =in.read(buf); s = new String(buf,0,len); System.out.println("the following message comes from sender:n"+s); in.close(); catch(IOException e) System.out.println(e.getMessage(); 运行结果:the following message comes from sender:hello,receiver ,how ar

45、e youJDK还提供了PipedWriter和PipedReader这两个类来用于字符文本的管道通信,读者掌握了PipedOutputStream和PipedInputStream类,自然也就知道如何使用PipedWriter和PipedReader这两个类了。$独家见解:使用管道流类,可以实现各个程序模块之间的松耦合通信,我们可以灵活地将多个这样的模块的输出流与输入流相连接,以拼装成满足各种应用的程序,而不用对模块内部进行修改。就象家庭的供水系统一样,我们可以把进水表的出水管与净化过滤器的进水管连在一起,然后,把净化过滤器的出水管同水箱的进水管连在一起来拼凑成我们的供水管道系统。我们可以在

46、这个供水管道系统中增加其他的水处理装置,也可以更换一个更大的水箱,甚至可以将进水表与水箱直连,而不经过净化过滤器,这一切都只需要各个水处理装置带有标准输入输出管道。可见,使用管道流进行通信的模块具有“强内聚,弱耦合”的特点,一个模块被替换,或被拆卸不会影响其他模块。假设有一个使用了管道流的压缩或加密的模块,我们的调用程序只管向该模块的输入流中送入数据,从该模块的数据流中取得数据,就完成了我们数据的压缩或加密,这个模块完全就象黑匣子一样,我们根本不用去了解它的任何细节。7.3.6 ByteArrayInputStream与ByteArrayOutputStreamByteArrayInputSt

47、ream是输入流的一种实现,它有两个构造函数,每个构造函数都需要一个字节数组来作为数据源:ByteArrayInputStream(byte buf) ByteArrayInputStream(byte buf, int offset, int length)第二个构造函数指定仅使用数组buf中的从offset开始的length个元素作为数据源。ByteArrayOutputStream是输出流的一种实现,它也有两个构造函数。ByteArrayOutputStream()ByteArrayOutputStream(int)第一种形式的构造函数创建一个32字节的缓冲区,第二种形式则是根据参数指定

48、的大小创建缓冲区,缓冲区的大小在数据过多时能够自动增长。这两个流的作用在于,用IO流的方式来完成对字节数组内容的读写。爱思考的读者一定有过这样的疑问:对数组的读写非常简单,我们为什么不直接读写字节数组呢?我在什么情况下该使用这两个类呢?有的读者可能听说过内存虚拟文件或者是内存映像文件,它们是把一块内存虚拟成一个硬盘上的文件,原来该写到硬盘文件上的内容会被写到这个内存中,原来该从一个硬盘文件上读取内容可以改为从内存中直接读取。如果程序在运行过程中要产生一些临时文件,就可以用虚拟文件的方式来实现,我们不用访问硬盘,而是直接访问内存,会提高应用程序的效率。假设有一个别人已经写好了的压缩函数,这个函数

49、接收两个参数,一个输入流对象,一个输出流对象,它从输入流对象中读取数据,并将压缩后的结果写入输出流对象中。我们的程序要将一台计算机的屏幕图像通过网络不断地传送到另外的计算机上,为了节省网络带宽,我们需要对一副屏幕图像的像素数据进行压缩后,再通过网络发送出去的。如果没有内存虚拟文件,我们就必须先将一副屏幕图像的像素数据写入到硬盘上的一个临时文件,再以这个文件作为输入流对象去调用那个压缩函数,接着又从压缩函数生成的压缩文件中读取压缩后的数据,再通过网络发送出去,最后删除压缩前后所生成的两个临时文件。可见这样的效率是非常低的。我们要在程序分配一个存储数据的内存块,通常都用定义一个字节数组来实现的,J

50、DK中提供了ByteArrayInputStream和ByteArrayOutputStream这两个类可实现类似内存虚拟文件的功能,我们将抓取到的计算机屏幕图像的所有像素数据保存在一个数组中,然后根据这个数组创建一个ByteArrayInputStream流对象,同时创建一个用于保存压缩结果的ByteArrayOutputStream流对象,将这两个对象作为参数传递给压缩函数,最后从ByteArrayOutputStream流对象中返回包含有压缩结果的数组。我们通过下面的例子程序来模拟上面的过程,我们并没有真正压缩输入流中的内容,只是把输入流中的所有英文字母变成对应的大写字母写入到输出流中。

51、程序清单:ByteArrayTest.javaimport java.io.*;public class ByteArrayTestpublic static void main(String args) throws ExceptionString tmp="abcdefghijklmnopqrstuvwxyz"byte src =tmp.getBytes();/src为转换前的内存块ByteArrayInputStream input = new ByteArrayInputStream(src);ByteArrayOutputStream output = new B

52、yteArrayOutputStream();new ByteArrayTest().transform(input,output);byte result = output.toByteArray();/result为转换后的内存块System.out.println(new String(result);public void transform(InputStream in,OutputStream out)int c=0;trywhile(c=in.read()!=-1)/read在读到流的结尾处返回-1int C = (int)Character.toUpperCase(char)c

53、);out.write(C);catch(Exception e)e.printStackTrace();运行结果为:ABCDEFGHIJKLMNOPQRSTUVWXYZ与ByteArrayInputStream和ByteArrayOutputStream类对应的字符串读写类分别是StringReader和StringWriter。读者可以将上面的程序修改成由这两个类来完成,具体的程序代码就不在这里多说了。7.3.7 IO程序代码的复用由于没有编码为-1的字符,所以,操作系统就使用-1作为硬盘上的每个文件的结尾标记,对于文本文件,我们的程序只要从文件中读取到了一个-1的字符值时,就可以确定已经

54、到了这个文件结尾。注意,这种方式只能用于判断文本文件是否结束,不能判断一个二进制文件是否结束。尽管二进值文件的结尾标记也是-1,因为二进制文件中的每个字节可以是-128到127之间的任意取值,其中就包括-1,当程序读取到一个-1的字节是,就难以判定是文件结尾还是文件中的有效数据。对于标准的二进值文件,在文件开始部分,都有一个文件头指定文件的大小,程序就是凭借文件头中的这个大小来读取文件中的所有内容的。我本人曾经为二进制文件和文本文件的区别困惑过很久,后来发现许多有一定软件开发经验的人也没完全搞清楚两者的区别。我们知道内存中的一个字节中数据可以是-128到127之间的任意值,实际上是以二进制形式

55、存放的,文件就是一片内存的数据在硬盘上的另外一种存放形式,也都是二进制数据,所以,可以说每个文件都是二进制的。我们现在的每个字符由一个或多个字节组成,每个字节都是用的-128到127之间的部分数值来表示的,也就是说,-128到127之间还有一些数据没有对应任何字符的任何字节。如果一个文件中的每个字节中的内容都是可以表示成字符的数据,我们就可以称这个文件为文本文件,可见,文本文件只是二进制文件中的一种特例,为了与文本文件相区别,人们又把除了文本文件以外的文件称之为二进制文件。由于很难严格区分文本文件和二进制文件的概念,所以我们可以简单地认为,如果一个文件专用于存储文本字符的数据,没有包含字符之外的其他数据,我们就称之为文本文件,除此之外的文件就是二进制文件。为了支持标准输入输出设备,Java定义了两个特殊的流对象,System.in和System.out。System.in对应键盘,是InputStream类型的,程序使用System.in可以读取从键盘上输入的数据。System.out对应显示器,是PrintStream类型的,PrintStream是Out

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论