Java I/O 系统 --think in java 第十八章

File类:
它既能代表一个特定的文件,又能代表一个目录下的一组文件名称。如果它指一个文件集,就可以对此文件结合调用list()方法,这个方法返回一个代表文件名的字符串数组。
FilenameFilter是一个只有一个accept()方法的接口,带参的list()方法可以通过该接口的对象来筛选要返回的文件名。
File对象比文件名更有用,因为File对象包含更多的信息。listFiles()返回文件数组。它也有带筛选接口的重载。
getPath()得到的文件构造时参数中给出的路径。
getAbsolutePath()返回的是文件的绝地路径。
getCanonicalPath()也是返回文件的绝对路径,但会去除[..]这样的符号,即返回的是标准的绝地路径。
renameTo()方法把一个文件重命名(或移动)到由参数所指示的另一个完全不同的新路径(也就是另一个File对象)下面。
mkdirs()方法一般是用来建立“多级”文件夹目录的,(当你不知道此文件夹是否存在,也不知道父文件夹存在不存在),就可用此方法,它建立文件夹的原则是:如果父文件夹不存在并且最后一级子文件夹不存在,它就自动新建所有路经里写的文件夹;如果父文件夹存在,那么它就会直接在已经存在的父文件夹下新建子文件夹。
如果你想在已经存在的文件夹yyy下建立新的文件夹(zzz文件夹),就可以用File.mkdir()方法。此方法不能在不存在的文件夹下建立新的文件夹。
file.mkdir() 和file.mkdirs()它们只是用来创建文件夹目录,如果想创建普通文件要用file.createNewFile()方法
输入和输出:
流这个抽象概念代表任何有能力产生输出数据的对象或有能力接收数据的接收端对象。
java类库中的IO类分成输入和输出两个部分。任何自InputStream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或者字节数组。
任何自OutputStream或Writer派生而来的类都含有名为write()的基本方法,用于写单个字节或字节数组。
我们通常很少使用单一的类来创建流对象,而是通过叠合多个对象来提供所期望的功能。这也是java流类库让人迷惑的地方,即创建单一目的流却需要创建多个对象。
与输入有关的所有类都应该从InputStream继承,而与输出有关的类都应该从OutputStream继承。
InputStream类型:
InputStream的作用是用来表示那些从不同数据源产生输入的类。这些数据包括:
1)字节数组
2)String对象
3)文件。
4)“管道”,工作方式与实际的管道类似,即从一端输入,从另一端输出。
5)一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内。
6)其他数据源。
每一个数据源都有相应的InputStream子类。另外,FilterInputStream也属于一种InputStream,为装饰器类提供基类,其中,“装饰器”类可以把属性或有用的接口与输入流连接在一起。

ByteArrayInputStream  功能:允许把内存的缓冲区当作InputStream使用。 构造器参数:缓冲区,字节将从中取出。 如何使用:作为一种数据源,将其与FilterInputStream对象相连以提供有用的接口。

StringBufferInputStream 功能:将String转换成InputStream  构造器参数:字符串,底层实现使用StringBuffer 如何使用:作为一种数据源,将其与FilterInputStream对象相连以提供
有用接口。

FileInputStream 功能:从文件中读取信息 构造器参数:文件名字符串,文件,或者FileDescriptor对象 如何使用:作为一种数据源,将其与FilterInputStream对象相连以提供有用接口

PipedInputStream 功能:产生用于写入相关PipedOutputStream的数据。实现管道化概念。 构造器参数:PipedOutputStram 如何使用:作为一种数据源,将其与FilterInputStream对象相连以提供有用接口

SequenceInputStram 功能:将两个或多个InputStream对象转换成单一InputStram 构造器参数:两个InputStream对象或一个容纳InputStream对象的容器的Enumeration  如何使用:作为一种数据源将其与FilterInputStream对象相连以提供有用接口

FilterInputStream 功能:抽象类,作为“装饰器”的接口。其中,“装饰器”为其他的InputStream类提供有用功能

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

ByteArrayOutputStream 功能:在内存中创建缓冲区,所有要送往“流”的数据都要放置在此缓冲区 构造器参数:缓冲区初始化尺寸 如何使用:用于指定数据的目的地,将其与FilterOutputStream对象相连以提供有用接口

FileOutputStream 功能:用于将信息写至文件 构造器参数:表示文件名的字符串件或者FileDescriptor对象 如何使用:指定数据的目的地,将其与FilterOutputStream对象相连以提供用接口

PipedOutputStream 功能:任何写入其中的信息都会自动作为相关PipedInputStream的输出。实现管道化概念。构造器参数:PipedInputStream 如何使用:指定用于多线程的数据的目的地,将其与FilterOutputStream对象相连以提供有用接口。

FilterOutputStream 抽象类,作为“装饰器”的接口。其中装饰器为其他OutputStream提供有用功能

添加属性和有用的接口:
java I/O 类库需要多种不同功能的组合,这正是使用装饰器模式的理由所在。这也是java I/O类库里存在filter过滤器类的原因。抽象类filter是所有装饰器类的基类。装饰器必须具有和它所装饰的对象相同的接口,但它也可以扩展接口,这种情况只发生在个别filter类中。装饰器类同样也增加了代码的复杂性。必须创建核心IO再包裹上所有的装饰器,才能得到所希望的单个IO对象。

FilterInputStream和FilterOutputStream是用来提供装饰器类接口以控制特定输入流和输出流的两个类,它们的名字并不是很直观。FilterInputStream和FilterOutputStream是用来提供装饰器类接口以控制特定输入流和输出流的两个类。FilterInputStream和FilterOutputStream分别自IO类库中的基类InputStream和OutputStream派生而来,这两个类是装饰器的必要条件(以便能为所有正在被修饰的对象提供通用接口)

通过FilterInputStream从InputStream读取数据:
Filter类能够完成两件完全不同的事情:
1)DataInputStream允许我们读取基本类型的数据以及Stream对象(所有读取方法均为readXXX的形式)。搭配相应的DataOutputStream就可以通过数据流将基本类型的数据从一个地方迁移到另一个地方。
2)其他FilterInputStream类则在内部修改InputStream的行为方式:是否缓冲,是否保留它所读过的行,以及是否把单一字符推回输入流等等。

FilterInputStream类型:

DataInputStream 功能:与DataOutputStream搭配使用,因此我们可以按照可移植方式从流读取基本数据类型。构造器参数:InputStream 如何使用:包含用于读取基本类型数据的全部接口。
BufferedInputStream 功能:使用它可以防止每次读取时都得进行实际写操作。代表使用缓冲区。构造器参数:InputStream 如何使用:本质上不提供接口,只不过是向进程中添加缓冲区所必须的。与接口对象搭配。

LineNumberInputStream 功能:跟踪输入流中的行号,可以调用getLineNumber()和setLineNumber(int) 构造器参数:InputStream 如何使用:仅增加了行号,因此可能要与接口对象搭配使用。

PushbackInputStream 功能:具有“能弹出一个字符的缓冲区”。因此可以将读到的最后一个字符退回 构造器参数:InputStream 如何使用:通常作为编译器的扫描器,之所以包含在内是因为Java编译器的需要,可能永远都不会用到。

FilterOutputStream类型及功能如下:

DataOutputStream  功能:与DataInputStream搭配使用,因此可以按照可移植方式向流中写入基本数据类型。 构造器参数:OutputStream 如何使用:包含用于写入基本类型数据的全部接口

PrintStream 用于产生格式化输出。其中DataOutputStream处理主句的存储,PrintStream处理显示。 构造器参数:OutputStream 如何使用:可以用boolean值指示是否在每次换行时清空缓冲区(可选的),应该是对OutputSTream对象的“final”封装。可能经常用到它。

BufferedOutputStream 功能:使用它以避免每次发送数据时都要进行实际的写操作,代表使用缓冲区,可以调用flush()清空缓冲区。 构造器参数:OutputStream(可以指定缓冲区大小), 如何使用:本质上并不提供接口,只不过是向进程中添加缓冲区所必须的。与接口对象搭配。

Reader和Writer:

InputStream和OutputStream在以面向字节形式的IO中提供有价值的功能,Reader和Writer则提供兼容Unicode与面向字符的IO功能。

1)java1.1向InputStream和OutputStream继承层次结构中添加了一些新类,所以显然这两个类不会被取代。
2)有时我们必须把来自于“字节”层次结构中的类和“字符”层次结构中的类结合起来使用。为了实现这个目的,要用到“适配器”类:InputStreamReader可以把InputStream转换为Reader,而OutputStreamWriter可以把OutputStream转化成Writer。

由于Unicode用于字符国际化(Java本身的char也是16位的Unicode),所以添加Reader和Writer继承层次结构就是为了在所有的IO操作中都支持Unicode。

数据的来源和去处:
几乎所有原始的Java IO 流类都有相应的Reader和Writer类来提供天然的Unicode操作。然而在某些场合,面向字节的InputStream和OutputStream才是正确的解决方案,特别是java.util.zip类库就是面向字节的而不是面向字符的。因此,最明智的选择就是尽量尝试使用Reader和Writer,一旦程序代码无法编译成功再考虑面向字节的类。

两个继承层次结构中的对应关系:
java1.0类                                           相应的java1.1类
InputStream                        Reader
                            适配器:InputStreamReader
OutputStream                    Writer
                            适配器:OutputStreamWriter
FileInputStream                    FileReader
FileOutputStream                    FileWriter
StringBufferInputStream(已弃用)            StringReader
无相应的类                        StringWriter
ByteArrayInputStream                CharArrayReader
ByteArrayOutputStream                CharArrayWriter
PipedInputStream                    PipedReader
PipedOutputStream                    PipedWriter

更改流的行为:
对于InputStream和OutputStream来说,我们会使用FilterInputStream和FilterOutputStream的装饰器子类来修改流以满足特殊需求。Reader和Writer沿用相同的思想但是不完全相同。
尽管BufferedOutputStream是FilterOutputStream的子类但是BufferedWriter却不是FilterWriter的子类(尽管FilterWriter是抽象类,没有任何子类,把它放在那里也只是把它作为一个占位符,或仅仅让我们不会对它所在的地方产生疑惑)

java1.0类                        相应的java1.1类
FilterInputStream                    FilterReader
FilterOutputStream                FilterWriter(抽象类,没有子类)
BufferedInputStream                BufferedReader(也有readLine())
BufferedOutputStream                BufferedWriter
DataInputStream                    使用DataInputStream(除了当需
                            要使用readLine()时以外应
                            用BufferedReader)
PrintStream                        PrintWriter
LineNumberInputStream(已弃用)            LineNumberReader
StreamTokenizer                    StreamTokenizer
PushbackInputStream                PushbackReader

注意:无论何时我们使用readLine(),都不应该使用DataInputStream,而应使用BufferedReader,除了这一点DataInputStream仍是IO类库的首选成员。readLine()会删除换行符
为了更容易的使用PrintWriter,它提供了一个既能接受Writer对象又能接受OutputStream对象的构造器。

java1.0中的在java1.1中没有相应类的类:
DataOutputStream File RandomAccessFile SequenceInputStream

自我独立的类:RandomAccessFile
RandomAccessFile适用于由大小已知的记录组成的文件,所以我们可以使用seek(),将记录从一处转移到另一处,然后读取或者修改记录。RandomAccessFile不是InputStream或者OutputStream继承层次结构中的一部分。除了实现DataInput和DataOutput接口之外,它和这两个继承层次结构没有任何关联。它甚至不使用InputStream和OutputStream类中已有的任何功能。它是一个完全独立的类,从头开始编写其所有方法。这么做是因为RandomAccessFile拥有和别的IO类本质不同的行为,因为我们可以在一个文件内向前和向后移动。它是独立的,直接从Object继承而来。
RandomAccessFile的工作方式类似于把DataInputStream和DataOutputStream组合起来使用,还添加了一些方法。其中方法getFilePointer()用于查找当前所处的文件位置,seek()用于在文件内移至新的位置,length()用于判断文件的最大尺寸。其构造器还需要第二个参数用来指示是“随机读(r)”还是"既读又写(rw)",不支持只写。
在jdk1.4中RandomAccessFile的大多数功能由nio存储映射文件所取代

IO流的典型使用方式:
    缓冲输入文件:BufferedReader in=new BufferedReader(new FileReader(filename));
    从内存输入:StringReader in=new StringReader(BufferedInputFile.read("MemoryInput.java"));
    格式化的内存输入:DataInputStream in=new DataInputStream(new ByteArrayInputStream(BufferedInputFile.read("FormattedMemoryInput.java").getbytes()));如果从DataInputStream用readByte()一次一个字节的读取数据,那么任何字节的值都是合法的,因此返回值不能检测输入是否结束,可以用availiable()方法查看还有多少可供提取的字节。
    基本的文件输出:BufferedReader in=new BufferedReader(new StringReader(BufferedInputFile.read("BasicFileOutput.java")));
            PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter(file)));
    文本文件输出的快捷方式:PrintWriter中添加了一个辅助构造器,使得你不必在每次希望创建文本文件并向其中写入时,都去执行所有的装饰工作。
    PrintWriter out=new PrintWriter(file);
    仍旧进行了缓存,只是不必自己去实现。
    数据存储和恢复:PrintWriter可以对数据进行格式化,以便阅读。但是为为了输出可以供另一个“流”恢复,需要使用DataOutputStream写入数据,并用DataInputStream恢复数据。
    DataOutputStream out=new DataOutputStream(new BufferedOutputStream(
new FileOutputStream("Data.txt")));
    DataInputStream in=new DataInputStream(new BufferedInputStream(new    FileInputStream("Data.txt")));
    当使用DataOutputStream时,写字符串并且让DataInputStream能够恢复它的唯一可靠的做法就是使用UTF-8编码,(使用writeUTF()和readUTF()实现),但是writeUTF()和readUTF()使用的是适合于java的UTF-8变体,当非java程序读取writeUTF()所写的字符串时,必须编写一些特殊代码才能正确读取字符串。
    读写随机访问文件:使用RandomAccessFile,类似于组合使用了DataInputStream和DataOutputStream,利用seek(跳过字节数)可以在文件中到处移动,并修改文件中的某个值。RandomAccessFile拥有读取基本数据类型和UTF-8字符串的各种具体方法。
    RandomAccessFile rf=new RandomAccessFile(file,"r");
    管道流:PipedInputStream,PipedOutputStream,PipedReader,PipedWriter的价值在开始理解多线程时才会显现,因为管道流用于任务之间的通信。


标准IO:
    标准IO这个术语参考的是Unix中“程序所使用的单一信息流”的概念。程序的所有输入都可以来自标准输入,它的所有输出也都可以发送到标准输出,以及所有的错误信息也都可以发送到标准错误,它的意义在于可以很容易的把程序串联起来,一个程序的标准输出可以成为另一个程序的标准输入。

    从标准输入中读取:按照标准IO模型,java提供了System.in,System.out和System.err。System.out已经事先被包装成了一个PrintStream,System.err也是,但是System.in却是一个没有被包装过的未经加工的InputStream。这意味着可以立即使用System.out
和System.err但在使用System.in之前必须对其进行包装。通常希望调用readLine()一次一行地读取输入,为此,将System.in包装成BufferedReader来使用。这就要用到InputStream把System.in转换成Reader。
    BufferedReader stdin=new BufferedReader(new InputStreamReader(System.in));
    将System.out转换成PrintWriterSystem.out是一个PrintStream,而PrintStream是一个OutputStream,PrintWriter有一个可以接受OutputStream的构造器.
    PrintWriter out=new PrintWriter(System.out,true)//true开启自动清空功能
    标准IO重定向:java的System类提供了一些简单的静态方法调用,以允许对标准输入,输出,错误IO进行重定向。
    setIn(InputStream)
    setOut(PrintStream)
    setErr(PrintStream)
    IO重定向操作的是字节流而不是字符流。


进程控制:
经常会需要在java内部执行其它操作系统的程序,并且要控制这些程序的输入和输出。java类库提供了执行这些操作的类。
    java.lang.ProcessBuilder的构造器接受一个字符串,它与你想要运行程序时在控制台输入的命令相同,所产生的ProcessBuilder对象调用start()来启动程序。start()返回的抽象类型Process可以调用getInputStream,getOutputStream,getErrorStream来获得程序的输出,提供给程序输入,和读取错误信息。

新I/O
JDK1.4的java.nio.*包中引入了新的java I/O类库,其目的在于提高速度。实际上旧的IO包已经使用nio重新实现过,以便充分利用这种速度提高。
速度的提高来自于所使用的结构更接近于操作系统执行IO的方式:通道和缓冲器。并不是直接和通道交互,只是和缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器获得数据,要么向缓冲器发送数据。
唯一直接与通道交互的缓冲器是ByteBuffer,也就是说,可以存储未加工字节的缓冲器。java.nio.ByteBuffer是非常基础的类:通过告知分配多少存储空间来创建一个ByteBuffer对象,并且还有一个方法选择集,用于以原始的字节形式或基本数据类型输出和读取数据。但是没办法输出或读取对象,即使字符串对象也不行。
旧IO类库的三个类被修改了,用以产生FileChannel,这三个被修改的类是:FileInputStream,FileOutputStream以及用于既读又写的RandomAccessFile。这些是字节操纵流,和底层nio性质一致。Reader和Writer这种字符模式的类不能用于产生通道,但是java.nio.channels.Channels类提供了实用方法,用以在通道中产生Reader和Writer。对于以上三种流,调用getChannel()将会产生对应XXXChannel。
通道是一种相当基础的东西:可以向它传送用于读写的ByteBuffer,并且可以锁定文件的某些区域用于独占式访问。
将字节存放于Byteffer的方法之一是:使用一种put方法直接对他们进行填充,填入一个或多个字节,或基本数据类型。也可以用wrap()方法将已存在的字节数组包装到ByteBuffer中,一旦如此,就不再复制底层的数组,而是把它作为Bytebuffer的存储器,称之为数组支持的Bytebuffer。
对于只读访问,可以调用Bytebuffer静态的allocate方法来分配ByteBuffer。方法allocateDirect速度更快,它会产生一个与操作系统高耦合的直接缓冲器,但开支会很大。

一旦调用read来告知FileChannel向ByteBuffer存储字节,就必须调用缓冲器上的flip()让它做好让别人读取字节的准备。如果打算使用缓冲器进一步执行read操作就必须调用clear来为每个read做好准备。

特殊方法transferTo()和transferFrom()允许将一个通道和另一个通道直接相连:
FileChannel in=new FileInputStream(args[0]).getChannel(),
        out=new FileOutStream(args[1]).getChannel();
in.transferTo(0,in.size(),out);
//或 out.transferFrom(in,0,in.size());

    转换数据:
    java.nio.CharBuffer类有一个toString()方法,可以返回一个包含缓冲器中所有字符的字符串,而ByteBuffer的asCharBuffer()方法可以得到一个CharBuffer视图。
    缓冲器中容纳的是普通字节,为了把他们转换成字符,要么在输入时进行编码,要么在将其从缓冲器输出时解码。可以利用java.nio.charset.Charset类时向这些功能。
    对缓冲器ByteBuffer调用rewind方法返回到数据开始部分,然后使用平台默认字符集对数据进行decode(),作为结果的CharBuffer可以很好的输出。
    可以使用System.getProperty("file.encoding")获取默认字符集,它会产生代表字符集名称的字符串,把该字符串传递给Charset.forName()用以产生Charset对象,可以用它对字符串进行解码。
    也可以在读文件时使用能够产生可打印的输出的字符集进行encode()
    获取基本类型:尽管ByteBuffer只能保存字节类型的数据,但是它具有可以从其所容纳的字节中产生出各种不同基本类型值的方法。(asXXXBuffer方法)
    在分配一个ByteBuffer之后,内容全部置0。limit()返回可容纳最大字节数。
    向ByteBuffer插入基本数据类型的最简单方法是:利用asCharBuffer,asShortBuffer()等获得该缓冲器上的视图,然后使用视图的put()方法。只有使用ShortBuffer的put方法时需要使用强制类型转换。其他所有视图缓冲器在使用put方法时不需要进行类型转换。

 

视图缓冲器:

视图缓冲器可以让我们通过某个特定的基本数据类型的视窗查看底层ByteBuffer,ByteBuffer依然是实际存储数据的地方,“支持着”视图,因此,对视图的任何修改都会映射成为对Bytebuffer中数据的修改。这使向ByteBuffer插入数据变得很方便。视图还允许一次一个或成批地(放入数组中)读取基本数据类型值。
    一旦底层的ByteBuffer通过视图缓冲器填满了整数或者其他基本数据类型时,就可以直接被写入通道中了。使用特定的视图缓冲器可以把任何数据都转化成某一特定的基本类型。
    在将ByteBuffer中的数据以不同视图缓冲器输出时的结果跟字节存放次序有关。


字节存放次序

大端是高位优先,把高位字节存放在地址较低的存储单元,小端则是低位优先,将低位字节存放在地址较低的位置。
    ByteBuffer是以高位优先的方式存储的,并且数据在网上传送时也常常使用高位优先的形式。可以调用order(ByteOrder.BIG_ENDIAN/.LITTLE_ENDIAN)方法来改变排序方式。
    ByteBuffer的array()方法可以返回视图底层的字节数组,但是只能对由数组支持的缓冲器调用此方法,否则会抛出UnsupportedOperationException

 

用缓冲器操纵数据:

下面的图阐明了nio类之间的关系,便于理解怎么移动和转换数据。例如:如果想把一个字节数组写到文件中去,就应使用ByteBuffer.wrap()方法把字节数组包装起来,然后用getChannel方法在FileOutputStream上打开一个通道,接着将来自于ByteBuffer的数据写到FileChannel中。

                                                                 Java I/O 系统 --think in java 第十八章                                             
    注意:ByteBuffer是将数据移进通道的方式,并且只能创建一个独立的基本类型缓冲器,或者使用“as”方法从ByteBuffer中获得。不能把基本数据类型缓冲器转换成ByteBuffer,但是可以经由视图缓冲器将基本数据类型移进移出ByteBuffer
    缓冲器的细节:ByteBuffer由数据和可以高效的访问和操纵这些数据的四个索引组成,这四个索引是:mark(标记),position(位置),limit(界限),和capacity(容量)。下面是用于设置和复位索引以及它们查询值的方法:
    capacity():返回缓冲区容量
    clear():清空缓冲区,将position设置为0,limit设置为容量。调用此方法以覆盖缓冲区。
    flip():将limit设置为position,position设置为0。用于准备从缓冲区读取已写入的数据    
    limit():设置limit值
    limit(int lim):设置limit值
    mark():将mark设置为position
    position():返回position值
    position(int pos):设置position值
    remaining():返回(limit-position)
    hasRemaining():若有介于position和limit之间的元素,则返回true


    内存映射文件:
    内存映射文件允许创建和修改那些因为太大而不能放入内存的文件。
    对通道调用map()方法可以返回MappedByteBuffer,它是一种特殊类型的直接缓冲器。必须指定映射文件的初始位置和映射区域的长度,这意味着可以映射某个大文件的较小部分。
    MappedByteBuffer由ByteBuffer继承而来,因此具有ByteBuffer的所有方法。
    性能:尽管旧IO流在用nio实现后性能有所提高,但是"映射文件访问"往往可以更加显著的加快速度。
    MappedByteBuffer.slice()生成的是DirectByteBuffer,对该buffer写操作会被直接写到文件里面

 

    文件加锁:
    jdk1.4引入了文件加锁机制,它允许同步访问某个作为共享资源的文件。文件锁对其他的操作系统进程是可见的,因为java的文件加锁机制直接映射到了本地操作系统的加锁工具。
    通过对FileChannel调用tryLock()或者lock(),就可以获得整个文件的FileLock。(SocketChannel,DatagramChannel,和ServerSocketChannel不需要加锁,因为他们是从单进程体继承而来,通常不需要在两个进程间共享socket)。tryLock()是非阻塞式的,它设法获得锁,但如果不能获得,它将直接从方法调用返回。lock()是阻塞式的,它要阻塞直至锁可获得,或者嗲用lock()的线程中断,或者调用lock()的通道关闭,使用FileLock.release()可以释放锁。
    也可以使用下面的重载方法对文件的一部分上锁:
    tryLock(long position,long size,boolean shared)
    lock(long position,long size,boolean shared)
    加锁区域由size和position决定,第三个参数指定是否共享锁。
    无参的加锁方法将根据文件尺寸的变化而变化(将整个文件锁定),但是具有固定尺寸的锁不随文件尺寸的变化而变化。
    对独占锁或者共享锁的支持必须由底层的操作系统提供支持。如果操作系统不支持共享锁,那么获得的将会是独占锁,锁的类型可以通过FileLock.isShared()进行查询。

    压缩:
    java I/O类库中的类支持读写压缩格式的数据流。可以对用它们对其他IO类进行封装,以提供压缩功能。
    这些类属于InputStream和OutputStream继承层次结构的一部分,这样做是因为压缩类库是按字节方式而不是字符方式处理的。
    压缩类                    功能
CheckedInputStream    为任何InputStream产生校验和
CheckedOutputStream    为任何OutputStream产生校验和
DeflaterOutputStream    压缩类的基类
ZipOutputStream        一个DeflaterOutputStream,用于将数据压缩成Zip格式
GZIPOutputStream        一个DeflaterOutputStream,用于将数据压缩成GZIP格式
InflaterInputStream    解压缩类的基类
ZipInputStream        一个InflaterInputStream,用于解压缩Zip文件格式的数据
GZIPInputStream        一个InfalterInputStream,用于解压缩GZIP文件格式的数据

    用GZIP进行简单压缩:
    写:
    BufferedOutputStream out=new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream("tar.gz")));
    读:
    BufferedReader in=new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream("test.gz"))));

    用Zip进行多文件保存:
    这个类库使用的是标准的Zip格式,所以能与当前那些可通过因特网下载的压缩工具很好的写作。
    一共有两种Checksum,Alder32(快一些)和CRC32(慢一些,但更精准)
    写:
    FileOutputStream f=new FileOutputStream("test.zip");    
    CheckedOutputStream csum=new CheckedOutputStrean(f,new Adler32());
    ZipOutputStream zos=new ZipOutputStream(csum);
    BufferedOutputStream out=new BufferedOutputStream(zos);

    读:
    FileInputStream fi=new FileInputStream("test.zip");
    CheckedInputStream csumi=new CheckedInputStream(new Adler32());
    ZipInputStream in=new ZipInputStream(csumi);
    BufferedInputStream bis=new BufferedInputStream(in);
    
    对于每一个要加入压缩档案的文件,都必须调用ZipOutputStream的putNextEntry(),并且向其传递给一个ZipEntry对象。ZipEntry对象包含了一个功能很广泛的接口,允许你获取和设置Zip文件内该特定项上所有可用数据。java的Zip类库并不提供设置密码的支持,尽管Zip格式提供了设置密码的方式。ZipEntry只有一个支持CRC校验的接口。
    ZipInputStream的getNextEntry()返回下一个ZipEntry。
    解压缩有一个更简便的方法,利用ZipFile对象读取文件,该对象有一个entries()方法用来向ZipEntries返回一个Enumeration
    Zip流的setComment()方法可以在写文件时写注释,但却没有任何方法恢复ZipInputStream内的注释。只能通过ZipEntry才能逐条完全获取注释
    
    Java档案文件:
    Zip格式也被应用于JAR(Java ARchive,java档案文件)格式中。这种文件格式就像Zip一样,可以将一组文件压缩到单个压缩文件中,同java中其他东西一样,JAR文件也是跨平台的。声音和图像可以像类文件一样被包含其中。JAR文件中每个条目都可以加上数字化签名,一个JAR文件由一组,压缩文件构成,同时还有一张描述了所有这些文件的"文件清单"(可以自行创建也可以由jar程序生成)


    对象序列化:
    Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可以通过网络进行,这意味着序列化机制能够弥补不同操作系统之间的差异。
    要序列化一个对象,首先要创建某些OutputStream对象,然后将其封装在一个ObjectOutputStream对象内,这时,只需要调用writeObject()既可将对象序列化,并将其发送给OutputStream。要将一个序列还原为一个对象,需要将一个InputStream封装在ObjectInputStream中,然后调用readObject()。获得的是一个Object引用,必须将其向下转型才能使用。
    对象序列化不仅保存了对象的"全景图",而且还能追踪对象所包含的所有引用,并保存那些对象,接着又能对对象内包含的每个引用进行追踪,形成对象网。
    可以对String调用writeObject(),可以用与DataOutputStream()相同的方法写入所有基本数据类型。可以用序列花将对象读写到任何数据输入流和数据输出流,包括网络。
    序列化必须要保证能够找到与相关的.class文件才能恢复对象。否则会抛出ClassNotFoundException异常。
    
    序列化的控制:
    如果你不希望对象的某一部分被序列化,又或者一个对象被还原后某个子对象需要重建,从而不必序列化该子对象,那么可以通过实现Externalizable接口代替实现Serializable接口来对序列化过程进行控制。它新增了两个方法:writeExternal()和readExternal(),这两个方法会在序列化和反序列化的过程中被自动调用。
    因为对于恢复一个Externallizable对象,默认构造器会被调用(包括在定义字段时的初始化),所以默认构造器必须是公共的,否则会抛出异常。为了正常运行,不仅需要在writeExternal(ObjectOutput)方法中将来自对象的信息写入,还必须在readExternal(ObjectInput)方法中将数据恢复,Externalizable对象的调用默认构造的行为并不完成存储与恢复操作。
    transient(瞬时)关键字:
    除了使用Externalizable有选择序列化对象外,还可以在Serializable类内将某个字段声明为transient来关闭对其的序列化。
    
    Externalizable的替代方法
    不使用Externalizable接口而实现Serializable接口并添加writeObject()和readObjec()方法,那么一旦对象被序列化或反序列化,就会分别自动调用这两个方法(只要提供了这两个方法就会使用他们,默认的序列化机制不会起作用)
    这两个方法必须有准确的方法签名:
    private void writeObject(ObjectOutputStream) throws IOException;
    private void readObject(ObjectInputStream)throws IOException,ClassNotFoundException
    它们不是Serializable接口的方法,与外部Object流对象的同名方法也没什么关系,似乎是用了反射机制。
    在它们内部还可以调用defaultWrite/readObject()来执行默认的序列化行为。
    外部对象流的readXXX(Object)和writeXXX(Object)会检查对象是否有自己的write/readObject()方法
    使用持久性:
    可以通过一个字节数组来使用对象序列化,从而实现对任何Serializable的深度复制--复制整个对象网。
    每次运行,对象地址将会不同,但指向同一对象的多个引用的地址是一样的,序列化会分清对象与引用的关系。
    static字段序列化问题:Class是Seriazable的,因此只要将Class对象序列化,就可以很容易的保存static字段,但是却不会按照预期的方式运行。必须手动实现。