【Java没基础】I/O学习笔记(一)传统IO

来源:互联网 发布:基尔霍夫定律实验数据 编辑:程序博客网 时间:2024/06/06 08:54

Java I/O类库从加入到 Java 核心类库之后,已经经过了三个阶段

I/O since type descripe BIO 1.4版本之前 同步阻塞 一个链接一个线程 NIO 1.4版本之后 同步非阻塞 一个请求一个线程 AIO 1.7版本之后 异步非阻塞 一个有效请求一个线程


传统 I/O

File 类

File 既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。假设我们想查看一个目录列表,可以用如下两种方法来使用 File 对象
1、如果我们调用不带参数的 list() 方法,便可以获得此 File 对象包含的全部列表
2、如果我们想获得一个受限的列表,我们需要用到“目录过滤器” – FilenameFilter

FilenameFilter

这个接口中,最重要的是 accept() 方法。
创建一个类事项 FilenameFilter 接口,目的就是将 accept() 方法提供给 list() 使用,使 list 可以回调 accept() ,进而可以决定哪些文件包含在列表中,这是一个 策略模式 的例子。

// IO.File.DirTest1 代码来自 Thinking in Javapublic class DirTest {    // 采用匿名类的形式    public static FilenameFilter filter(final String regex) {        return new FilenameFilter(){            private Pattern pattern = Pattern.compile(regex);            public boolean accept(File dir, String name){                // 按照正则表达式过滤文件名                return pattern.matcher(name).matches();            }        };    }    public static void main(String[] args) {        File path = new File(".");        String[] list;        if (args.length == 0) {            list = path.list();        } else {            list = path.list(filter(args[0]));        }           //按照字母顺序进行排序        Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);        for (String dirItem : list) {            System.out.println(dirItem);        }    }}

list() 方法实现了基本的功能,而且按照 FilenameFilter 的形式提供了这个策略,以便完善 list() 在提供服务时所需的算法。list() 会为当前目录对象下的每个文件名称调用 accept() 方法,判断结果由 accept() 返回的布尔值表示。
上例中的 FilenameFilter 采用匿名类的形式提供服务,内部实现 accept() 方法:按照正则表达式过滤文件名。

对于 File 对象,我们可以使用 File 对象来创建新的目录或者尚不存在的整个目录路径。

输入和输出

在众多编程语言中,都设计 这个抽象概念来设计 I/O 类库, 代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象。 屏蔽了实际的 I/O 设备中处理数据的细节。

Java 类库将其 I/O 库分为了两部分:
A:通过继承,任何自 InputStream 或者 Reader 派生来的类,这些类都有基本方法 read(),用于读取单个字节或者字节数组。
B:通过继承,任何自 OutputStream 或者 Writer 派生来的类,这些类都有基本方法 write(),用于写单个字节或者字节数组。

InputStream 与 OutputStream

由Java 1.0 提供

InputStream 派生类

InputStream的作用是用来表示那些从不同数据源产生输入的类。

数据源 类名 构造参数 描述 字节数组 ByteArrayInputStream 缓冲区 允许将内存的缓冲区当做InputStream使用 String对象 StringBufferInputStream 字符串 将String转换成InputStream,底层实现采用 StringBuffer 文件 FileInputStream 字符串 用于从文件中读取信息 管道 PipedInputStream PipedOutputStream[管道化概念] 作为多线程中数据源,产生用于写入相关 PipedOutputStream的数据。 流序列 SequenceInputStream 两个InputStream对象/容纳多个流对象的容器Enumeration 将两个或多个InputStream对象转换为一个 其他数据源 FilterInputStream InputStream FilterInputStream也属于一种InputStream,为“装饰器”类提供基类

装饰器类必须具有和它装饰的对象相同的接口,尽管可以扩展,但是是个例。

OutputStream 派生类

该类决定输出所要去往的目标:字节数组(但不是 String,不过你当然可以使用字节数组自己创建)、文件或者管道。

类名 构造参数 描述 ByteArrayOutputStream 可选的初始化大小参数 在内存中创建缓冲区,所有送往“流”的数据都要放置在此缓冲区中 FileOutputStream 字符串 将数据写入文件 PipedOutputStream PipedOutputStream 实现“管道化”概念,多线程中数据目的地 FilterOutputStream OutputStream 抽象类,作为“装饰器”的接口。
FilterInputStream与FilterOutputStream

Java I/O 类库需要多种不同的功能组合,这是采用“装饰器模式”的理由所在(装饰器经常用于满足各种可能的组合)。这同时也是Java I/O 类库中存在 Filter(过滤器)的原因之一,抽象类 Filter 是所有装饰器类的基类。

装饰器类在我们编写程序代码时提供了相当多的灵活性(我们可以容易的匹配和混合属性),但是它也同时增加了代码的复杂性:我们有时需要创建许多类——核心 I/O 与其他装饰器类,他们共同成为了我们希望的单个 I/O 对象。

类 功能 如何使用 DataInputStream 按照可移植方式向流中读取基本类型数据 包含用于读取基本类型数据的接口 DataOutputStream 按照可移植方式向流中写入基本类型数据 包含用于写入基本类型数据的接口 BufferedInputStream “使用缓冲区”,防止每次操作读取都进行实际的读操作 本质上不提供接口,只不过是向进程中添加缓冲区所必须的属性 BufferedOutputStream “使用缓冲区”,防止每次操作读取都进行实际的写操作 本质上不提供接口,只不过是向进程中添加缓冲区所必须的属性 LineNumberInputStream 可跟踪行号的输入流 传入一个InputStream。仅增加了一个行号,因此可能要与接口对象搭配使用 PushbackInputStream 具有“能弹出一个字节的缓冲区”。可将读到的最后一个字符回退 通常作为编译器的扫描器,我们可能永远不会用到 PrintStream 用于产生格式化输出。DataOutputStream处理数据的存储,PrintStream处理显示 传入一个OutputStream,可以用boolean指示是否该在每次换行时清空缓冲区(可选)


Reader 与 Writer

Java 1.1对基本的I/O类库进行了重大的修改,在 InputStream 和 OutputStream 的基础上,增加了 Reader 与 Writer。InputStream 和 OutputStream 在以面向字节形式的I/O中仍可以提供极有价值的服务,Reader和Writer则提供兼容Unicode与面向字符的I/O功能。另外:
1.Java 1.1 向InputStream 和 OutputStream 继承层次结构中添加了一些新类
2.有时我们需要把来自于“字节”与“字符”层次结构的类结合使用,互相转化。为了实现这个目的,Java 类库为程序员们提供了“适配器(adapter)”类:InputStreamReader 可以把 InputStream 转换为 Reader,而OutputStreamWriter 可以把 OutputStream 转换为 Writer。

对于大部分 I/O 操作,都有可用的 Reader 与 Writer 类,但是有些面向“字节”的情况下,InputStream与OutputStream依然是正确的解决方案,比如 java.util.zip 类库就是面向“字节”的。

下面我们用两个表格来熟悉 Java I/O流I/O装饰器 ;两个方面的 Java 1.0 与 Java 1.1 的对应类与接口

Java I/O 流

大体上,我们会发现,这两个不同的继承层次结构中的接口即使不能完全相同,但也非常相似。

Java 1.0 Java 1.1 InuptStream Reader OutputStream Writer FileInputStream FileReader FileOutputStream FileWriter StringBufferInputStream(弃用) StringReader – StringWriter ByteArrayInputStream CharArrayReader ByteArrayOutputStream CharArrayWriter PipedInputStream PipedReader PipedOutputStream PipedWriter



I/O 装饰器

对于InputStream和OutputStream来说,我们会使用FilterInputStream和OutputStream的装饰器子类来修改“流”以满足特殊需要。Reader和Writer的类继承层次结构继续沿用相同的思想——尽管不完全相同

Java 1.0 Java 1.1 FilterInputStream FilterReader FilterOutputStream FilterWriter(抽象类) BufferedInputStream BufferedReader(readLine()) BufferedOutputStream BufferedWriter DataInputStream DataInputStream/BufferedReader PrintStream PrintWriter LineNumberInputStream(弃用) LineNumberReader StreamTokenizer StreamTokenizer(使用接受Reader的构造器) PushbackInputStream PushbackReader


标准 I/O

程序所有的输入都可以来自于标准输入 System.in,所有的输出都可以发送到标准输出System.out,所有的错误信息也都可以发送到标准错误 System.err

其中 System.out 与 System.err 都已经被包装为 PrintStream 对象,但是 System.in 却是一个没有被包装过的未经加工的 InputStream。这表示我们可以直接使用 System.out 与 System.err,但是在使用 System.in 进行数据输入之前必须对其进行包装。
比如:Scanner scan = new Scanner(System.in);

将 System.out 转换成 PrintWriter
System.out 是一个 PrintStream,PrintStream 是一个 OutputStream,而 PrintWriter 还有个接受 OutputStream 做参数的构造器。所以如有需要,我们可以将 System.out 转换成 PrintWriter

public static void main(String[] args) {    // 将PrintWriter构造器的第二个参数设计为true,开启自动清空    PrintWriter out = new PrintWriter(System.out, true);    out.println("Hello world!");}


标准 I/O 重定向

Java 的 System 类提供了一些简单的静态方法调用,以允许我们对标准输入、输出和错误 I/O 流进行重定向:

    setIn(InputStream)    setOut(PrintStream)    setErr(PrintStream)

I/O 重定向操作的是字节流,而不是字符流

后记

暂告一段落,后面分别为 NIO 与 AIO

扫一下吧,随便扫一下~

原创粉丝点击