20201013 王维

学习总结

1. Stream(流)

1.1 概述

File 类的实例用于表示文件或目录的 路径名 。虽然我们可以通过 File 实例来访问文件或目录的元数据,甚至可以创建、删除文件或目录,但是,我们却不能通过 File 实例来访问文件中存储的内容,访问修改文件中存储的内容主要是通过流来实现。

为了能够 读取文件中的内容 或者 向文件中写入内容 ,就需要用到文件输入流或文件输出流,本节将系 统讲解通过流完成对文件内容的读取和写入操作。

但千万不要认为我们只能从文件中读取数据或向文件中写入数据,在网络编程部分我们将会学习如何通 过 Java 程序从网络上读取数据和向网络发送数据。

1.1.1 流

在 Java 语言中,将够 读取数据 或者 写出数据 的对象抽象为 流。

流 类似于生活当中的 水管 , 水 可以在 水管 中 定向移动 ,正如 数据 可以在 流 中定向移动。

我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为 输入 input 和 输出 output ,即流向内存是输入流,流出内存的输出流。

Java中 I/O 操作主要是指使用 java.io 包下的内容,进行输入、输出操作。输入也叫做读取数据,输出 也叫做作写出数据。

Java 传统的 IO 体系中,所有的 流 对应的类型都扩展自四个抽象类:

字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

其中的 InputStreamOutputStream 的子类表示字节流, ReaderWriter 的子类则表示字符 流。

注意:这里所提及的 Stream 一词均表示流,请不要与 java.util.stream.Stream 接口混为一谈。

1.1.2 分类

Java中传统的I/O体系可以按照不同的方式对流进行分类:

按照流中数据的流向来分类,可以分为 输入流 和 输出流

  • 用于实现从外部读取数据到当前程序的流被称作输入流
  • 用于实现从当前程序中向外部输出数据的流被称作输出流

按照流处理的数据单元来分类,可以划分为 字节流 和 字符流

  • 如果某个流在读取或写入数据时以字节为单位,则该流属于字节流

  • 如果某个流在读取或写入数据时以字符为单位,则该流属于字符流

    注意:通过字符流读写字符数据时,一个字符未必就占两个字节 一个字符有可能是1个字节,也可能是2个、3个、4个字节,这取决于字符编码

按照流的功能来分类,可以分为 节点流 和 包装流

  • 直接数据节点中读取数据或向数据节点中写入数据的流被称作节点流

    数据节点可以是磁盘上的文件或其它数据源(比如键盘输入)

    节点流都比较原始,仅用于实现数据的读取和写入,通常不具备其它功能

  • 用于从其它流中读取数据,并实现其它流所不具备的功能的流,被称作包装流

    包装流也被称作 转换流 或 处理流 或 过滤流

    包装流通常会提供它内部所包装的流不具备的功能

    包装流内部所处理的流(也称作所过滤的流)被称作底层支持流

节点流 和 包装流 的关系如下图所示:

image-20201013231754285

1.1.3 一切皆字节

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那 么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使 用什么样的流对象,底层传输的始终为二进制数据。

1.2 字节流

字节流 是以 字节 为单位读写数据的输入流或输出流。字节流 体系种涉及的类很多。

1.2.1 Closeable

java.io.Closeable 接口,实现此接口的都是可以关闭的数据的源或目的地。都会实现 close() 方 法。如: Scanner , InputStream , OutputStream

public interface Closeable extends AutoCloseable

该类继承了java.lang.AutoCloseableAutoCloseable接口提供了自动关闭的能力。

1.2.2 字节输入流

1.2.2.1 InputStream

InputStream类是所有表示字节输入流的类的父类,它是个抽象类,因此不能被实例化。

public abstract class InputStream implements Closeable

InputStream 类是所有字节输入流的最顶层父类,掌握该类的使用即可从根本上把握其它字节输入流 的使用。

image-20201013232321078

1.2.2.1.1 构造方法

InputStream类仅有一个无参构造供子类调用:

public InputStream()
1.2.2.1.2 实例方法
方法 描述
public int available() 返回此输入流下一个方法调用可以不受阻塞的从此输入流读取(或跳过)的估计字符
public abstract int read() 从此输入流中读取数据的下一个字节(所有非抽象子类必须实现该方法)
public int read(byte[] bytes) 从此输入流中读取一定数量的字节,并将其存储在缓冲区数组 bytes
public int read(byte[] bytes , int offset , int length) 将输入流中最多 length 个数据流读入 bytes 数组(offset位置开始放入)
public long skip(long n) 跳过和丢弃此输入流中数据的n个字节(并返回实际跳过和丢弃的字节数)
public boolean markSupported() 测试此输入流是否支持 mark 和 reset方法(当某个流支持 markreset 操作时返回 true
public void mark( int readlimit) 在此输入流中标记当时的位置(对于大部分流而言参数 readlimit 毫无意义)
public void reset() 将此流重新定位到最后一次对此输入流调用 mark方法时的位置
public void close() 关闭此输入流并释放与该类相关联的所有系统资源(该方法来自Closeable

在所有方法中,仅 read() 方法是抽象方法,因此 InputStream 类的所有的子类都需要实现该方法。 因为 InputStream 类是个抽象类,因此必须借助于其 非抽子类 来完成实例化。

主要学习 FileInputStreamBufferedInputStream

1.2.2.2 FileInputStream

FileInputStream用于从文件系统中的某个文件中读取内容(以字节为单位)。 FileInputStream 用于读取诸如图像数据之类的原始字节流。 如需读取字符流,可以考虑使用 InputStreamReaderFileReader

1.2.2.2.1 构造方法
1.2.2.2.2 实例方法

FileInputStream 类重写了 InputStream 类中所有的 public 方法,但除了以下方法

public void reset() throws IOException
public void mark(int readlimit) throws IOException
public boolean markSupported() throws IOException

上述方法未被重写外,其它方法均已被 FileInputStream 类所重写,重写的方法包括:

public int available() throws IOException
public int read() throws IOException
public int read(byte[] bytes) throws IOException
public int read(byte[] bytes, int offset, int length) throws IOException
public long skip(long n) throws IOExcepti

这里需要注意,直接在 Windows 系统中创建的 文本文档 ,其默认编码可能为 GBK (每个汉字占用两个字 节)。(UTF-8占三个字节)

1.2.2.2.3 读取单个字节

InputStream 类中定义的抽象方法是所有子类所必须实现的:

public abstract int read() throws IOException

该方法用于从字节输入流中读取单个字节;如果到达流的末尾,则返回 -1 。

而另一个方法 skip 则用于跳过指定的字节数

public long skip(long n) throws IOException

该方法的参数 n 表示期望跳过的字节数,而返回值则表示实际所跳过的字节数。

同时,InputStream类提供了获取流中剩余字节数的方法:

public int available() throws IOException

则用于获取可以不受阻塞地从此输入流读取(或跳过)的估计字节数;如果到达输入流末尾,则返回 0 。每次从流中读取一个字节后,剩余字节数就会减少一个,可以直至全部读完。

汉字可能不会正常输出,因为汉字在磁盘上存储时会占多个字节,而如果每次仅读取一个字 节,因此从 文件中读取汉字时,每个汉字被拆分成多次读取,每次又将读取到 的字节转换成 char 类型输出到了控制台中,所以肯定会发生中文乱码。

1.2.2.2.4 读取多个字节

InputStream 类中定义了两个用于从输入流中批量读取字节的方法:

public int read(byte[] b) throws IOException
public int read(byte[] b, int offset, int length) throws IOException

其中最为常用的是:

public int read(byte[] b) throws IOException

该方法可以从字节输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 并以整数形式返回实际读取的字节数,当到达流末尾时返回 -1 。

1.2.2.2.5 读取所有字节

从 Java 9 开始, InputStream 类中提供了用于读取流中所有字节的方法:

public byte[] readAllBytes() throws IOException

该方法可以读取任意字节输入流中所有字节并返回一个字节数组。 (可以使用 JDK 9 及之后的版本测试,建议使用 JDK11 。因为该版本是长期支持版 (LTS) )

1.2.2.3 BufferedInputStream
1.2.2.3.1 概述

BufferedInputStream 类的实例表示带有缓冲功能的字节输入流,它是一种包装流。

BufferedInputStream 类的定义如下:

 public class BufferedInputStream extends FilterInputStream

从 BufferedInputStream 类的定义中可以知道,该类继承了 FilterInputStream 类。 FilterInputStream 的定义如下:

public class FilterInputStream extends InputStream {
protected volatile InputStream in ; // 用来保存被过滤的字节输入流的引用 ( 底层支
持流 )
protected FilterInputStream(InputStream in) {
this.in = in;
}
// 省略其它所有代码
}

BufferedInputStream 类的实例并不能直接读取数据节点中的数据,而是需要从另外一个流中读取数 据。

因此它是一种包装流 (也称作 转换流 或 处理流 或 过滤流 ),而被它所处理的流被称作底层支持流。

4.2.2.3.2 字段(属性/成员变量)

成员变量 包括 类变量 和 实例变量 。( 类变量 就是常说的 静态变量 )

在 BufferedInputStream 类中声明了两个 类变量 :

用来表示缓冲区默认大小的 类变量

 private static int DEFAULT_BUFFER_SIZE = 8192;

用来表示缓冲区最大容量的 类变量

private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;

在 BufferedInputStream 类中声明了五个 实例变量 :

protected volatile byte[] buf; // 存储数据的内部缓冲区数组(即字节缓冲区)
protected int count ; // 有效字节数
protected int pos ; // 缓冲区中的当前位置
protected int markpos = -1 ; // 做标记的位置 (即最后一次调用 mark 方法时 pos 字段的
值 )
protected int marklimit ; // 调用mark方法后,在后续调用reset方法失败之前所允许的最大
提前读取量。当pos和markpos之间的差值超过marklimit时,可以通过将markpos设置为-1来删除标
记。

其中前四个变量之间的关系如图所示:

image-20201013235010028

4.2.2.3.3 构造方法

BufferedInputStream 类定义了两个构造方法:

根据给定的字节输入流创建一个具有指定缓冲大小的 BufferedInputStream 实例

public BufferedInputStream(InputStream in , int size) {
super(in); // 调用父类构造 ( 将参数传入的字节输入流缓存起来 )
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size]; // 创建字节缓冲区
}

注意其中的 super(in) 是调用父类 FilterInputStream 的构造方法。

根据给定的字节输入流创建一个具有默认缓冲大小的 BufferedInputStream 实例

public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE); // 调用本类中另一个构造方法
}

其中的 DEFAULT_BUFFER_SIZE 是本类中声明的类变量(默认值是 8192 ) 。

4.2.2.3.4 实例方法
4.2.2.3.5 标记/重置
4.2.2.3.6 理解缓冲功能

1.2.3 字节输出流

心得体会

今天学习的很多,多使用,有很多地方还没有进行到练习,可能存在很多理解上的差别,下去还有自己多练习。

标签

评论