编程思想--第18章--JAVA I/O系统

Java I/O系统

创建一个好的 输入/输出(I/O) 系统是一项艰难的任务。不仅存在各种I/O源端和想要与之通信的接收端(文件,控制台,网络链接等),而且还需要以多种不同的方式与它们进行通信(顺序,随机存取,缓冲,二进制,按字符,按行,按字等)。

Java类库的设计者通过创建大量的类解决这个难题。

一、File类

File这个名字既能代表一个特定的文件名称,又能代表一个目录下一组文件的名称。实际上FilePath对这个类来说是个更好的名字。

list()方法获取文件路径下的所有文件。及其它获取文件名称,路径等的方法。

二、输入和输出

编程语言的I/O类库中常使用流这个抽象概念,它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象。流屏蔽了实际的I/O设备中处理数据的细节。Java类库的I/O类分成输入和输出两部分。

任何自InputStream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或者任何字节数组。任何自OutPputStream或Writer派生而来的类都含有名为write()的基本方法,用于写单个字节或字节数组。

Java中流类库让人迷惑的主要原因在于:创建单一的结果流,却需要创建多个对象。

InputStream的作用是用来表示那些从不同数据源产生输入的类,数据源包括:1,字节数组,2,String对象,3,文件,4,管道,工作方式与实际管道相似,从一端输入,从另一端输出。5,一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内。6,其他数据源,如Internet等。

OutputStream的类决定了输出所要去往的目标:字节数组,文件或管道。

三、添加属性和有用的接口

装饰器模式在编写编程的时候给我们提供了相当多的灵活性,但是也增加了代码的复杂性。Java的I/O类库操作不便的原因在于:必须创建许多类“核心”I/O类型加上所有的装饰器,才能得到我们希望的单个I/O对象。

通过FilterInputStream从InputStream读取数据。DataInputStream允许我们读取不同的基本类型数据以及String对 象(所有方法以read开头)

通过FilterOutputStream向OutputStream写入。DataOutputStream可以将各种基本类型以及String对象格式化输出到流中(所有方法以writer开头)

四、Reader和Writer

Reader和Writer提供Unicode与面向字符的I/O功能。有时将字节和字符结合起来使用,实现这个目的要用到适配器。InputStreamReader可以把InputStream转换为Reader,而OutputStreamWriter可以把outputStream转换为Writer。设计Reader和Writer继承层次结构主要是为了国际化。老的I/O流继承层次结构仅支持八位字节流,并不能很好地处理16位的Unicode字符。

面向字节的InputStream和OutputStream,面向字符的Reader和Writer。两个继承层次结构中,信息的来源和去处如下:

五、自我独立的类:RandomAccessFile

RandomAccessFile适用于由已知的记录组成的文件,所以我们可以使用seek()将记录从一处转移到另一处,然后读取或者修改记录。RandomAccessFile拥有和别的I/O类型本质不同的行为,因为我们可以在一个文件内向前和向后移动。在任何情况下,它都自我独立的,直接从Object派生而来。

六、I/O流的典型使用方式

尽管可以通过不同方式组合I/O流类,但可能也就只用到其中的几种组合。

缓冲输入文件。对文件的字符输入的缓冲使用BufferedReader构造器,其提供了readLine()方法,这是最终的对象和进行读取的接口。

从内存输入。从BufferedInputFile.read()读入的String结果可以用来创建一个StringReader。并调用read()每次读取一个字符。

PipedInputStream,PipedOutputStream,PipedReader及PipedWriter的价值只有在理解多线程后才会出现。

七、文件读写的实用工具

一个很常见的程序化任务就是读取文件到内存,修改,然后再写出。Java的I/O类库的问题之一就是:它需要编写相当多的代码去执行这些常用操作--没有任何基本的帮助功能可以为我们做这一切。

八、标准I/O

标准I/O的意义在于:我们可以很容易把程序串联起来,一个程序的标准输出可以成为另一程序的标准输入。

标准I/O模型中,java提供了System.in,System.out和System.err。通常我们会用readLine()进行一次一行的输入。

java的System类提供了一些简单的静态方法调用,允许我们对比较标准输入,输出和错误I/O流进行重定向。setIn(InputStream),setOut(PrintStream),setErr(PrintStream);

九、进程控制

Java内部执行其他操作系统的程序,并且要控制这些程序的输入和输出,java类库提供了执行这些操作的类。

十、新I/O

速度的提高来自于所使用的结构更接近于操作系统执行I/O 的方式:通道和缓冲器。通道要么从缓冲器获得数据,要么向缓冲器发送数据。唯一与通道交互的缓冲器是ByteBuffer,可以存储未加工字节的缓冲器。

高位优先是将最重要的字节存放在地址最低的存储器单元,低位优先是将最重要的字节存放在地址最高的存储器单元。当存储量大于一个字节时,像int,float等就要考虑字节的顺序问题。

缓冲器容纳的是普通的字节,为了把它们转换成字符,我们要么在输入它们的时候对其进行编码,要么在将其从缓冲器输出时对它们进行解码。尽管ByteBuffer只能保存字节类型的数据,但是它具有可以从其所容纳的字节中产生出各种不同基本类型的值的方法。ByteBuffer是以高位优先的形式存储数据的,并且数据在网上传输时也常常使用高位优先的形式。

ByteBuffer是将数据移进移出通道的唯一方式,并且我们只能创建一个独立的基本类型缓冲器,或者使用as方法从ByteBuffer中获得,我们不能把基本类型的缓冲器转换成ByteBuffer。

Buffer由数据和可以高效地访问及操纵这些数据的四个索引组成,这四个索引是:Mark(标记),position(位置),limit(界限),capacity(容量)。

注意:底层操作系统的文件映射工具是用来最大化地提高性能。尽管旧的I/o流在用nio实现后性能有所提高,但是映射文件访问往往可以更加显著加快速度。

文件加锁机制:允许我们同步访问某个作为共享资源的文件。文件锁对其它的操作系统是可见的,因为java的文件加锁直接映射到本地操作系统的加锁工具。对于巨大的文件需要进行部分加锁,以便其它进程可以修改文件中未被加锁的部分。

十一、压缩

Java的I/O类库中的类支持读写压缩格式的数据流,你可以用它们对其他的I/O类进行封装,以提供压缩功能。属于InputStream和OutputStream继承层次结构的一部分。因为压缩是按字节方式而不是字符方式处理的。

GZIP压缩:适用于单个数据流进行压缩。ZIP压缩:更加全面,方便保存多个文件,有独立类,使得读取ZIP文件更加方便。GZIP或ZIP库的使用可以压缩任何东西,包括需要通过网络发送的数据。

ZIP格式也被应用于JAR文件格式中。这种文件格式像Zip一样,可以将一组文件压缩到单个压缩文件中。采用压缩技术,传输时间更短,请求一次,出于安全考虑,JAR文件中的每个条目都可以加上数字化签名。

十二、对象序列化

Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可以通过网络运行,这意味着序列化机制能够自动弥补不同操作系统之间的差异。

对象的序列化是非常有趣的,利用它可以实现轻量级持久性。序列化聪明的地方是它不仅保存了对象的全部信息,还能追踪对象内所包含的所有引用,并保存那些对象,接着又能对对象包含的每个这样的引用进行追踪,以此类推。

序列化的控制:默认的序列化机制不难控制,实现Externalizable接口代替Serializable接口对序列化过程进行控制。Externalizable接口继承了Serializable接口 ,添加了两个方法writeExternal()和readExternal()。这两个方法在序列化和反序列化还原的过程中被自动调用。

transient关键字只能和Serializable一起使用:对序列化进行控制时,某个特定子对象不想让java的序列化机制自动保存与恢复。如果操作的是一个Serializable对象,可以使用transient(瞬时)关键字逐个字段关闭序列化。

只要将任何对象序列化到单一流中,就可以恢复出与我们写出时一样的对象网,并且没有任何意外重复复制出的对象。

序列化static的值,必须自己手动去实现。序列化会将private数据保存下来。

十三、XML

对象序列化的一个重要限制是它只是Java的解决对象,只有Java程序才能反序列化这种对象。一种更具互操作性的解决方案是将数据转换为XML格式,还可以使其被各种各样的平台和语言使用。

十四、Preferences    

Preferences API与对象序列化相比,前者与对象持久性更密切,因为它可以自动存储和读取信息。不过,它只能用于小的,受限的数据集合--我们只能存储基本类型和字符串,并且每个字符串的存储长度不能超过8k。用于储存和读取用户的偏好以及程序配置项的设置。

Preferences 是一个键值集合,存储在一个节点层次结构中。

十五、总结

Java 的I/O流类库的确能满足我们的基本需求:我们可以通过控制台,文件,内存块,甚至因特进行读写。通过继承,我们可以创建新类型的输入和输出对象。并且通过重新定义toString()方法,我们甚至可以对流接受的对象类型进行简单扩充。

你可能感兴趣的:(编程思想--第18章--JAVA I/O系统)