Java I/O系统(1)

1、File类

  • File对象调用带不带参数的list()方法,便可以获得此File对象包含的全部列表。

  • 获得一个受限的列表,就要用到“目录过滤器”。“目录过滤器”类实现了FilenameFilter接口,FilenameFilter接口很简单,如下:

          public interface FilenameFilter{

              boolean accept(File dir,String name);

          }

    “目录过滤器”类存在的唯一原因:将accept()方法提供给list()使用,使list()可以回调accept(),进而以决定哪些文件包含在列表中。accept()方法接受一个代表某个特定文件所在目录的File对象,以及包含了那个文件名的一个String,记住一点:list()方法会为此目录对象下的每个文件调用accept(),来判断该文件是否包含在内,判断结果accept()返回的布尔值表示。accept()会使用一个正则表达式的matcher对象,来查看此正则表达式regex是否匹配这个文件的名字。

  • File对象调用listFiles()或listFiles(FilenameFilter filter)产生File数组。

  • 常用方法:

          创建

          boolean createNewFile():当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。

          boolean mkdirs():该方法的作用是创建文件夹,如果当前路径中包含的父目录不存在时,也会自动根据需要创建。

          删除

          boolean delete():删除此抽象路径名表示的文件或目录。

          void deleteOnExit():在虚拟机终止(程序退出)时,请求删除此抽象路径名表示的文件或目录。

          判断

          boolean exists():测试此抽象路径名表示的文件或目录是否存在。

          boolean isAbsolute():测试此抽象路径名是否为绝对路径名。 

          boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录。 

              boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。 

              boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。

              获取信息

              String getName():返回由此抽象路径名表示的文件或目录的名称。

              String getPath():将此抽象路径名转换为一个路径名字符串。

              File getAbsoluteFile():获取绝对路径封装成文件对象。

              String getAbsolutPath():获取绝对路径。

              String getParent():返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。

              long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。

              long length():该方法的作用是返回文件存储时占用的字节数。该数值获得的是文件的实际大小,而不是文件在存储时占用的空间数。

              renameTo():该方法的作用是修改文件名。在修改文件名时不能改变文件路径,如果该路径下已有该文件,则会修改失败。

              boolean setReadOnly():该方法的作用是设置当前文件或文件夹为只读。

2、输入和输出

  • InputStream类型

    Java I/O系统(1)

  • OutputStream类型

    Java I/O系统(1)

  • 添加属性和有用的接口

           FilterInputStream和FilterOutputStream是用来提供装饰器类接口以控制特定输入流(InputStream)和输出流(OutputStream)的。FilterInputStream和FilterOutputStream分别自I/O类库中的基类InputStream和OutputStream派生而来,这两个类是装饰器的必要条件。

           FilterInputStream类型

    Java I/O系统(1)

           FilterOutputStream类型

    Java I/O系统(1)

4、Reader和Writer

  • InputStream和OutputStream在以面向字节形式的I/O中仍可以提供极有价值的功能,Reader和Writer则提供兼容Unicode与面向字符的I/O功能。设计Reader和Writer继承层次结构主要是为了国际化,Unicode用于字符国际化,老的I/O流继承层次结构仅支持8位字节流,并不能很好地处理16位的Unicode字符。

  • 有时必须把来自于“字节”层次结构中的类和“字符”层次结构中的类结合起来使用。为了实现这个目的,要用到“适配器(adapter)”类:InputStreamReader可把InputStream转换为Reader,而OutputStreamWriter可把OutputStream转换为Writer。

  • 有时面向字节的InputStream和OutputStream才是正确的解决方案,特别是java.util.zip类库就是面向字节的而不是面向字符的。最明智的做法是尽量尝试使用Reader和Writer,一旦程序代码无法成功编译,就不得不使用面向字节的类库。

  • 信息来源和去处

    Java I/O系统(1)

  • 更改流的行为

    Java I/O系统(1)

    注:无论何时使用readLine(),都不应该使用DataInputStream,而应该使用BufferedReader。除了这一点,DataInputStream仍是I/O类库的首选成员。

  • 未发生变化的类

    Java I/O系统(1)

  • 注:对于存储和检索可传输格式的数据,可以使用InputStream和OutputStream继承层次结构。

5、自我独立的类:RandomAccessFile

  • RandomAccessFile适用于由已知大小的记录组成的文件,所以可使用seek()将记录从一处移到另一处,然后读取或修改记录。文件中记录的大小不一定相同,只要能够确定那些记录的大小以及它们在文件中的位置即可。

  • RandomAccessFile不是InputStream或OutputStream继承层次结构中的一部分,除了实现DataInput和DataOutput之外,它和这两个继承层次结构没有任何关联。

  • RandomAccessFile的工作方式类似于把DataInputStream和DataOutputStream组合起来使用,还添加了一些方法。其中方法getFilePointer()用于查找当前所处的文件位置,seek()用于在文件内移至新的位置,length()用于判断文件的最大尺寸。另外,其构造器还需要第二个参数用来指示只是“随机读”(r)还是“既读又写”(rw)。

  • 只有RandomAccessFile支持搜寻方法,并且只适用于文件。

  • RandomAccessFile的大多数功能由nio存储映射文件所取代。

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

  • 缓冲输入文件。打开一个文件用于字符输入,可使用以String或File对象作为文件名的FileReader。为了提高速度,希望对那个文件进行缓冲,那将所产生的引用传递给一个BufferedReader构造器。由于BufferedReader也提供readLine()方法,这就是最终对象和进行读取的接口。

  • 格式化的内存输入。要读取格式化数据,可使用DataInputStream,它是一个面向字节的I/O类。提供字节数组所产生的ByteArrayInputStream是一个适合传递给DataInputStream的InputStream。从DataInputStream用readByte()一次一个字节地读取字符,那么任何字节的值都是合法的结果,因此返回值不能用来检测输入是否结束。但是,可以使用available()方法查看还有多少可供存取的字符。

  • 基本的文件输出。FileWriter对象可以向文件写入数据,首先创建一个与指定文件连接的FileWriter,通常BufferedWriter将其包装起来用以缓冲输出。另外如果想提供格式化机制,它可被装饰成PrintWriter。按这种方式创建的数据文件可作为普通文本文件读取。

  • 存储和恢复数据。PrintWriter可以对数据进行格式化,以便人们的阅读。但为了输出可供另外一个流恢复数据,需要用DataOutputStream写入数据,并用DataInputStream恢复数据。当使用DataOutputStream时,写字符串并让DataInputStream能够恢复它的唯一可靠做法是使用UTF-8编码,可以使用writeUTF()和readUTF()来实现。

  • 读写随机访问文件。使用RandomAccessFile,类似于组合使用DataInputStream和DataOutputStream,另外利用seek()可以在文件中到处移动,并修改文件中的某个值。RandomAccessFile拥有读取基本类型和UTF-8字符串的各种具体方法。可能会考虑使用“内存映射文件”来替代RandomAccessFile。

  • 管道流。PipedInputStream、PipedOutputStream、PipedReader及PipedWriter,它们的价值只有在开始理解多线程之后才会显现,因为管道流用于任务间的通信。

7、标准I/O

  • 从标准输入中读取。按照标准I/O模型,java提供各类System.in、System.out和System.err。其中System.out已事先被包装成PrintStream对象,System.err同样是PrintStream,但System.in却是一个没有包装过的未经加工的InputStream。这意味着可以立即使用System.out和System.err,但是在读取System.in之前必须对其进行包装。譬如,new BufferedReader(new InputStreamReader (System.in));

  • 将System.out转换成PrintWriter。System.out是一个PrintStream,而PrintStream是一个OutputStream,PrintWriter有一个可接受OutputStream作为参数的构造器,另外一个参数设为true,开启自动清空功能,否则看不到输出。

  • Java的System类提供了一些简单的静态方法调用,以允许对标准输入、输出和错误I/O流进行重定向:setIn(InputStream)、setOut(PrintStream)、setErr(PrintStream)。I/O重定向操纵的是字节流,而不是字符流。

8、新的I/O

  • Java.nio.*包中引入新的Java I/O类库,其目的在于提高速度。速度的提高来自于所使用的结构更接近于操作系统执行I/O的方式:通道(channels)和缓冲器(buffers)。我们并没有直接和通道交互,只是和缓冲器交互,并把缓冲器派送到通道,通道要么从缓冲器获得数据,要么向缓冲器发送数据。

  • 唯一直接与通道交互的缓冲器是ByteBuffer——可以存储未加工字节的缓冲器。ByteBuffer是相当基础的类:通过告知分配多少存储空间来创建一个ByteBuffer对象,并还有一个方法选择集,用于以原始的字节形式或基本数据类型输出和读取数据。但,没办法输出或读取对象,即使是字符串对象也不行。

  • 旧I/O类库中有三个类被修改,用以产生FileChannel(getChannel()方法)。这三个类是FileInputStream、FileOutputStream以及用于既读又写的RandomAccessFile,注意这些是字节操纵流。Reader和Writer不能用于产生通道,但java.nio.channels.Channels类提供了实用方法,用以在通道中产生Reader和Writer。

  • 通道是一种相当基础的东西:可以向它传送用于读写的ByteBuffer,并可以锁定文件的某些区域用于独占式访问。

  • 将字节放于ByteBuffer的方法:一种是put()方法对它们进行填充,填入一个或多个字节,或基本数据类型的值;一种是使用wrap()方法将已存在的字节数组“包装”到ByteBuffer中。

  • 可以在文件内随处移动FileChannel,用到方法position()。

  • 对于只读访问,必须显式地使用静态方法allocate()来分配ByteBuffer。nio的目标是快速移动大量数据,因此ByteBuffer的大小就显得尤为重要。达到更高的速度也可能是使用方法allocateDirect()而不是allocate(),以产生一个与操作系统有更高耦合性的直接缓冲器,但这种分配开支会更大,并且具体实现也随操作系统的不同而不同。

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

  • 特殊方法transferTo()和transferFrom()允许讲一个通道和另一个通道直接相连。

  • 尽管ByteBuffer只能保存字节类型的数据,但是它具有可以从其所容纳的字节中产生各种不同基本数据类型的方法。向ByteBuffer插入基本数据类型的最简单方法是:利用asCharBuffer()、asShortBuffer()等获得该缓冲上的视图,然后使用视图的put()方法。注意使用ShortBuffer视图的put()方法时,需要进行类型转换。

  • 视图缓冲器可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer。ByteBuffer依然是实际存储数据的地方,支持着前面的视图,因此对视图的任何修改都会映射成为对ByteBuffer中数据的修改。

  • 字节存放次序。“big endian”(高位优先)将最重要的字节存放在地址最低的存储单元,而“little endian”(低位优先)则是将最重要的字节放在地址最高的存储器单元。ByteBuffer是以高位优先的形式存储数据的,并且数据在网上传送时也常常使用高位优先的形式。可以使用带有参数ByteOrder.BIG_ENDIAN和ByteOrder.LITTLE_ENDIAN的order()方法改变ByteBuffer的字节排序方式。如果以short(ByteBuffer.asShortBuffer())形式读取数据得到的数字是97(二进制形式为00000000 01100001),但如果将ByteBuffer更改为低位优先形式,仍以short形式读取数据,得到的数字确是24832(二进制形式为01100001 00000000)。array()方法显示视图底层字节,该方法是可选的,只能对由数组支持的缓冲器调用此方法。

  • 缓冲器操纵数据

    Java I/O系统(1)

  • 缓冲器的细节。Buffer由数据和可以高效地访问及操作这些数据的四个索引组成,这三个索引是:mark(标记)、position(位置)、limit(界限)和capacity(容量)。

    Java I/O系统(1)

    reset()方法把position的值设置为mark的值;

    rewind()方法把position设置到缓冲器的开始位置。

转载于:https://my.oschina.net/90liusq/blog/370354