第十八章 Java I/O系统


在这里插入图片描述

1.目录列表器
  1. 如果我们调用不带参数的list()方法,便可以获得此File对象包含的全部列表
  2. 如果想获得一个受限的列表,可以使用“目录过滤器”
  3. boolean accept(File dir,String name)
  • (1) dir:特定文件所在目录的File对象
  • (2) name:文件名
  1. list()方法会为此目录对象下的每隔文件名调用accept(),来判断该文件是否包含在内

  2. 目录使用工具
  • (1)
package Chapter18.Example01;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

public final class Directory {
    public static File[] local(File dir, final String regex){
        return dir.listFiles(new FilenameFilter() {
            private Pattern pattern = Pattern.compile(regex);
            @Override
            public boolean accept(File dir, String name) {
                return pattern.matcher(new File(name).getName()).matches();
            }
        });
    }
    public static File[] local(String path, final String regex){ //重载
        return local(new File(path), regex);
    }
    public static class TreeInfo implements Iterable<File>{
        public List<File> files = new ArrayList<>();
        public List<File> dirs = new ArrayList<>();
        public Iterator<File> iterator(){
            return files.iterator();
        }
        void addAll(TreeInfo other){
            files.addAll(other.files);
            dirs.addAll(other.dirs);
        }
        public String toString(){
            return "dirs: "+ dirs + "\n\nfiles: " +files;
        }
    }
    public static TreeInfo walk(String start, String regex){
        return recurseDir(new File(start), regex);
    }
    public static TreeInfo walk(File start, String regex){
        return recurseDir(start, regex);
    }
    public static TreeInfo walk(File start){
        return recurseDir(start, ".*");
    }
    public static TreeInfo walk(String start){
        return recurseDir(new File(start), ".*");
    }
    static TreeInfo recurseDir(File startDir, String regex){
        TreeInfo result =  new TreeInfo();
        for(File item : startDir.listFiles()){
            if(item.isDirectory()){
                result.dirs.add(item);
                result.addAll(recurseDir(item,regex));
            }else {
                if(item.getName().matches(regex))
                    result.files.add(item);
            }
        }
        return result;
    }

    public static void main(String[] args) {
        if(args.length == 0)
            System.out.println(walk("."));
        else
            for(String arg :args)
                System.out.println(walk(arg));
    }
}
  • (2) local()
    • local()方法产生本地目录中的文件构成的File对象数组
    • local()方法使用被称为listFile()的File.list()的变体来产生File数组
  • (3) walk()
    • walk()方法产生给定目录下的由整个目录树中的所有文件构成的List
    • walk()方法将开始目录的名字转换为File对象,然后调用recurseDirs(),该方法将递归地遍历目录,并在每次递归中都收集更多的信息
  1. 目录的检查及创建
  • (1) File类不仅仅只代表存在的文件或目录。也可以用File对象来创建新的目录或尚不存在的整个目录路径。
  • (2) 我们还可以查看文件的特性(如:大小,最后修改日期,读/写),检查某个File对昂代表的是一个文件还是一个目录,并可以删除文件
2.输入和输出
  1. 流:它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象。”流“屏蔽了实际的I/O设备中处理数据的细节

  2. 任何自InputStream或Reader派生而来的类都含有名为reader()的基本方法,用于读取单个字节或者字节数组

  3. 任何自OutputStream或Writer派生而来的类都含有名为write()的基本方法,用于读取单个字节或者字节数组

  4. InputStream类型
  • (1) InputStream的作用是用来表示那些从不同数据源产生输入的类
  • (2) 数据源包括:
    • i.字节数组
    • ii.String对象
    • iii.文件
    • iv.“管道”,工作方式与实际管道相似,即,从一端输入,从另一端输出
    • v.一个由其他种类的流组成的序列,以便我们可以把它们收集合并到一个流内
    • iv.其他数据源,如Internet连接等
  • (3) 数据源对应的InputStream子类:在这里插入图片描述
  1. OutputStream类型
  • (1) 该类别的类决定了输出所要去往的目标:字节数组(但不是String,不过你当然可以用字节数组自己创建)、文件或管道
  • (2) OutputStream类型:在这里插入图片描述
3.添加属性和有用的接口
  1. Java I/O类库里存在filter(过滤器)类的原因:抽象类filter是所有装饰器类的基类

  2. Java I/O类库操作不便的原因在于:我们必须创建许多类(“核心”I/O类型加上所有的装饰器),才能得到我们所希望的单个I/O对象

  3. 通过FilterInputStream从InputStream读取数据
  • (1) DataInputStream允许我们读取不同的基本类型数据以及String对象
  • (2) FilterInputStream:是否缓冲,是否保留它所读过的行(允许我们查询行数或设置行数),以及是否把单一字符推回输入流等等
  • (3) FilterInputStream类型在这里插入图片描述
  1. 通过FileOutputStream向OutputStream写入
  • (1) DataOutputStream:可以将各种基本数据类型以及String对象格式化输出到“流”中
  • (2) PrintStream:以可视化格式打印所有基本类型以及String对象
  • (3) BufferedOutputStream:对数据流使用缓冲技术,因此当每次向流写入时,不必每次都进行实际的物理写动作
  • (4) FilterOutputStream类型在这里插入图片描述
4.Reader和Writer
  1. 设计Reader和Writer继承层次结构主要是为了国际化。
  2. 数据的来源和去处
  • 尽量尝试使用Reader和Writer,一旦程序代码无法成功编译,我们就会发现自己不得不使用面向字节的类库
  1. 更改流的行为
  • (1) 无论我们何时使用readLine(),都不应该使用DataInputStream(这会遭到编译器的强烈反对),而应该使用BufferedReader
  • (2) DataInputStream是I/O类库的首选成员
  1. 未发生变化的类
  • 如果想以“可传输的”格式存储和检索数据,可以使用InputStream和OutputStream继承层次结构
5.自我独立的类:RandomAccessFile
  1. RandomAccessFile适用于由大小已知的记录组成的文件

  2. RandomAccessFile不是InputStream和OutputStream继承层次结构中的一部分

  3. RandomAccessFile方法
  • (1) 构造器的第二个参数只是我们只是“随机读”(r)还是“既读又写”(rw),它并不支持只写文件
  • (2) getFilePointer():查找当前所处的文件位置
  • (3) seek():将记录冲一处转移到另一处,然后读取或者修改记录
  • (4) length():用于判断文件最大尺寸
  1. JDK1.4RandomAccessFile的大部分功能被nio(存储映射文件)取代

  2. 存储和恢复数据
  • (1) 为了输出可供另一个“流”恢复的数据,我们需要用DataOutputStream写入数据,并用DataInputStream恢复数据
  • (2) writeDouble()将double类型的数字存储到流中,并用相应的readDouble()恢复它
  • (3) UTF-8是一种多字节格式,其编码长度根据实际使用的字符集会有所变化
  1. 读取随机访问文件
  • (1) RandomAccessFile实现了DataInput和DataOutput接口
  • (2) 应为double总是8字节长,所以用seek()查找第5个双精度值,就可以用5*8来产生查找位置
  1. 管道流
  • 管道流PipedInputStream、PipedOutputStream、PipedReader及PipedWriter在多线程中使用的较多,用于任务之间的通信
6. I/O流的典型使用方式
  1. 缓冲输入文件
  • (1) 如果想要打开有一个文件用于字符输入,可以使用以String或File对象作为文件名的FileInputReader
  • (2) 为了提高速度,我们希望对那个文件进行缓冲,那么我们将所产生的引用传给一个BufferedReader构造器
  • (3) BufferedReader也提供readLine()方法,当readLine()返回null时,你就达到了文件的末尾
  1. 从内存输入
  • (1) 在下面的示例中,从BufferedInputFile.read()读入的String结果被用来创建一个StringReader。然后调用read()每次读取一个字符,并把它发送给控制台
  • (2) 注意read()是以int形式返回下一字节,因此必须类型转换为char才能正确打印
  1. 格式化的内存输入
  • (1) 要读取格式化数据,可以使用DataInputStream,他还是面向字节的I/O类(不是面向字符的)
  • (2) 使用available()方法查看还有多少可供存取的字符
  1. 基本的文件输出
  • (1) FileWriter对象可以向文件写入数据
  • (2) 首先,创建一个与指定文件连接的FileWriter,再用BufferedWriter将其包装起来用以缓冲输出
7.文件读写的实用工具
  • 需要编写相当多的代码去执行读取文件到内存,修改,然后再写出
8.标准I/O
  1. “程序所使用的单一信息流”:我们可以很容易地把程序串联起来,一个程序的标准输出可以成为另一个程序的标准输入
  2. 从标准输入中读取
  • (1) Java提供了System.in、System.out和System.err
  • (2) System.out和System.err已经被包装成printStream对象
  • (3) System.in是一个没有被包装过的未经加工的InputStream。读取System.in之前必须对其进行包装
  1. 将System.out转换成PrintWriter
  • (1) PrintWriter(Writer out, boolean autoFlush)
  • (2) 使用这个构造器,可以把System.out转为PrintWriter
  • (3) out:接受OutputStream作为参数(System.out)
  • (4) autoFlush:设为true,开启自动清空
  1. 标准I/O重定向
  • (1) I/O重定向操纵的是字节流,而不是字符流;因此我们使用的是InputStream和OutputStream,而不是Reader和Writer
  • (2) static void setErr(PrintStream err) 重定向“标准”错误输出流
  • (3) static void setIn(InputStream in) 重定向“标准”输入流
  • (4) static void setOut(PrintStream out)重定向“标准”输出流
9.进程控制
// Run an operating system command
// and send the output to the console.
package Chapter18.Example02;
import net.mindview.util.OSExecuteException;

import java.io.*;

public class OSExecute {
  public static void command(String command) {
    boolean err = false;
    try {
      Process process =
        new ProcessBuilder(command.split(" ")).start();
      BufferedReader results = new BufferedReader(
        new InputStreamReader(process.getInputStream()));
      String s;
      while((s = results.readLine())!= null)
        System.out.println(s);
      BufferedReader errors = new BufferedReader(
        new InputStreamReader(process.getErrorStream()));
      // Report errors and return nonzero value
      // to calling process if there are problems:
      while((s = errors.readLine())!= null) {
        System.err.println(s);
        err = true;
      }
    } catch(Exception e) {
      // Compensate for Windows 2000, which throws an
      // exception for the default command line:
      if(!command.startsWith("CMD /C"))
        command("CMD /C " + command);
      else
        throw new RuntimeException(e);
    }
    if(err)
      throw new OSExecuteException("Errors executing " +
        command);
  }
} ///:~
package Chapter18.Example02;

public class OSExecuteDemo {
    public static void main(String[] args) {
        OSExecute.command("javap OSExecuteDemo");
    }
}
package Chapter18.Example02;

public class OSExecuteException extends RuntimeException {
  public OSExecuteException(String why) { super(why); }
} ///:~
  1. 你经常需要在Java内部执行其他操作系统的程序,并且要控制这些程序的输入和输出
  2. 要想运行一个程序,需要向OSException.command()传递一个command字符串,它与你在控制台上运行该程序所键入的命令相同
10.新 I/O
  1. 速度的提高来自于所使用的结构更接近于操作系统运行I/O的方式:通道和缓冲器

  2. 通道要么从缓冲器获得数据,要么想缓冲器发送数据

  3. ByteBuffer:可以存储未加工字节的缓冲器。用于以源氏的字节形式或基本数据类型输出和读取数据,没办法输出或读取对象,即使时字符串对象也不行

  4. 旧 I/O 类库被修改的类:FileInputStream、FileOutputStream、既读又写的RandAccessFile

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

  6. nio的目标:快速移动大量数据

  7. warp():将已存在的字节数组“包装到”ByteBuffer中

  8. allocate():分配ByteBuffer大小

  9. allocateDirect():产生一个与操作系统有更高耦合性的“直接”缓冲器

  10. flip():准备缓冲器以便它的信息可以由write()读取

  11. transferTo()和transferFrom()则允许我们将一个通道和另一个通道直接链接

  12. 转换数据
  • (1) 缓冲器容纳的是普通的字节,为了把它们转换成字符,我们要么在输入它们的时候对其将进行编码,要么在将其从缓冲器输出时对它们进行解码
  • (2) 可以用java.nio.charset.Charset类实现这些功能,该类提供了把数据编码成多种不同类型的字符集的工具
  1. 获取基本类型
  • (1) 向ByteBuffer插入基本类型数据的最简单的方法是:利用asCharBuffer()、asShortBuffer()等获得该缓冲器上的视图,然后使用视图的put()方法
  • (2) 使用asShortBuffer()的put()时需要类型转换,其他的不需要
  1. 视图缓冲器
  • (1) 视图缓冲器(view buffer)可以让我们通过某个特定的基本数据类型的视窗查看其底层的ByteBuffer
  • (2) ByteBuffer通过一个被”包装“过的8字节数组产生,然后通过各种不同的基本类型的视图缓冲器显示了出来
  • (3) 字节存放次序
    • i.不同的机器可能会使用不同的自己排序方法来存储数据
    • ii.“big endian”(高位优先)将最重要的字节放在地址最低的存储器单元
    • iii.“little endian”(低位优先)则将最重要的字节放在地址最高的存储器单元
    • iv.ByteBuffer是以高位优先的形式存储数据的,并且数据在网上传送时也常常使用高位优先的形式
  1. 用缓冲器操纵数据
  • 我们不能把基本类型的缓冲器转换成ByteBuffer
  1. 缓冲器的细节
  • (1) Buffer由数据和四个索引组成
  • (2) 四个索引是:mark(标记)、position(位置)、limit(界限)和capacity(容量)
  • (3) 方法
    • 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)
    • hasRemaing():若由介于position和limit之间的元素,则返回true
  1. 内存映射文件
  • (1) 内存映射文件允许我们创建和修改哪些因为太大而不能放入内存中的文件
  • (2) 有了内存映射文件,我们就可以假定整个文件都放在内存中,而且可以完全把它当作非常大的数组来访问
  • (3) 建立内存映射文件MappedByteBuffer由ByteBuffer继承而来
  1. 文件加锁
  • (1) 文件加锁机制,它允许我们同步访问某个作为共享资源的文件
  • (2) 竞争同一个文件的两个线程可能在不同的Java虚拟机上;或者一个是Java线程,另一个是操作系统中其他的某个本地线程
  • (3) 文件锁对其他的操作系统进程是可见的,因为Java的文件加锁直接映射到了本地操作系统弄的加锁工具
  • (4) tryLock()是非阻塞式的,它设法获取锁,若不能获取(其他一些进程有相同的锁,并不共享时),它直接从方法调用返回
  • (5) lock()是阻塞式的,它要阻塞进程直至锁可以获得,或调用lock()进程中断,或调用lock()的通道关闭
  • (6) 使用FileLock.release()可以释放锁
  • (7) 对一部分文件上锁
    • tryLock(long position,long size,boolean shared)
    • lock(long position,long size,boolean shared)
    • 加锁区域由 size - position 决定
    • shared:是否共享
  • (8) 锁的类型(共享锁,独占锁)可以通过FileLock.isShared()进行查询
  • (9) 对于巨大的文件进行部分加锁,以便其他进程可以修养该文件中未被加锁的部分
  • (10) 如果有Java虚拟机,它会自动释放锁,或者关闭加锁的通道
  1. Java档案文件
  • (1) Zip格式也被应用于JAR(Java Archive,Java档案文件)文件格式中
  • (2) 命令行 jar [option] destination [manifest] inputfile(s)
  • (3) [option]参数
    • c:创建一个新的或空的压缩文档
    • t:列出目录表
    • x:解压所有文件
    • x file:解压该文件
    • f:jar假设所有的输入都来自于标准输入;或者在创建一个文件时,输出对象也假设为标准输出
    • m:表示第一个参数将是用户自建的清单文件的名字
    • v:产生详细输出,描述jar所作的工作
    • O:只储存文件,不压缩文件
    • M:不自动创建文件清单
11.压缩
  1. 可以使用压缩类库对其他的I/O类进行封装,以提供压缩功能

  2. 压缩类库是按字节方式而不是字符方式

  3. 压缩类
  • (1) CheckInputStream:GetCheckSum为任何InputStream产生校验和(不仅解压缩)
  • (2) CheckOutputStream:GetCheckSum为任何OutputStream产生校验和(不仅压缩)
  • (3) DeflaterOutputStream:压缩类基类
  • (4) ZipOutputStream:将数据压缩成Zip
  • (5) GZipOutputStream:将数据压缩成GZip
  • (6) InflaterInputStream:解压缩类基类
  • (7) ZipInputStream:用于解压Zip文件格式数据
  • (8) GZipInputStream:用于解压GZip文件格式数据
  1. 用于解压Zip文件格式数据
  • 直接将输出流封装成GZipOutputStream或ZipOutputStream并将输入流封装成GZipInputStream或ZipInputStream
  1. 用Zip进行多文件保存
  • (1) Checksum类型:Adler32(快)和 CRC32(慢)
  • (2) 每一个加入压缩档案的文件,都必须调用putNExtEntry(),并将其传递给一个ZipEntry对象
  • (3) 解压文件的简单方法:利用ZipFile对象读取文件。该对象有一个entries()方法用来向ZipEntries返回一个Enumeration
  • (4) GZip或Zip库可以压缩任何东西,包括要通过网络发送的数据
12.对象序列化
  1. 当你创建对象时,只要你需要,它就会一直存在,但是在程序终止时,无论如何它都不会继续存在

  2. Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象

  3. “持久性”意味着一个对象的生存周期并不取决于程序是否正在执行;它可以生存于程序的调用之间

  4. 寻找类

  5. 序列化的控制
  • (1) 在特殊情况下,可通过使用Externalizable接口与——代替实现Serializble接口——来对序列化过程进行控制
  • (2) Externalizable添加了两个方法:writeExternal()和readExternal()
  • (3) Externalizable对象,调用所有普通默认构造器,然后调用readExteral()
  • (4) transient(瞬时)关键字
    • transient(瞬时)关键字
    • 例如:private transient String password//这样就不会保存到磁盘
    • Externalizable对象在默认情况下不保存它们的任何字段,所以transient关键字只能和Serializable对象一起使用
  • (5) Externaliizable的替代方法
    • 实现Serializable接口,并添加writeObject(ObjectOutputStream out)和readObject(ObjectInputStream in)
    • 如 书P579 方法同名“混乱”!
  1. 使用“持久性”
  • (1) 存储程序的一些状态,以便我们随后可以很容易地将程序恢复到当前状态
  • (2) 存储程序的一些状态,以便我们随后可以很容易地将程序恢复到当前状态
  • (3) 序列化static值,必须自己动手去实现
13.XML
  1. 对象序列化的一个重要限制是它只是Java的解决方案:只有Java程序才能反序列化这种对象
  2. 将数据转换为XML格式,可以使其被各种各样的平台和语言使用
  3. XOM类库(www.xom.nu):用Java产生和修改XML的方式
14.Preferences
  1. preferencesAPI与对象持久性更密切,因为它可以自动存储和读取信息
  2. 只能存储基本类型和字符串(不超8k)
  3. Preferences API用于存储和读取用户的偏好(preferences)以及程序配制项的设置
  4. Preferences不会产生本地文件,他利用不同系统的系统资源完成任务
  • 26
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 34
    评论
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进阶的小名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值