一、基本分类
- 按照读写数据的基本单位不同,分为 字节流 和 字符流。
其中字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件。
其中字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件。 - 按照读写数据的方向不同,分为 输入流 和 输出流(站在程序的角度)。
其中输入流主要指从文件中读取数据内容输入到程序中,也就是读文件。
其中输出流主要指将程序中的数据内容输出到文件中,也就是写文件。 - 按照流的角色不同分为节点流和处理流。
其中节点流主要指直接和输入输出源对接的流。
其中处理流主要指需要建立在节点流的基础之上的流。 - 缓冲字节/字符流比文件字节/字符流的效率更高。
数据读取和写入的流程:
-
数据从流写到文件过程
输出流---->缓冲流---->转化流---->文件流----->文件.
-
数据从文件到流的过程
文件---->文件流---->转化流---->缓冲流---->输入流.
二、体系结构
三、相关流的类
3.1 FileWriter类(重点)
java.io.FileWriter类主要用于将文本内容写入到文本文件。
常用的方法
方法声明 | 功能介绍 |
---|---|
FileWriter(String fileName) | 根据参数指定的文件名构造对象 |
FileWriter(String fileName, boolean append) | 以追加的方式根据参数指定的文件名来构造对象 |
void write(int c) | 写入单个字符 |
void write(char[] cbuf, int off, int len) | 将指定字符数组中从偏移量off开始的len个字符写入此文件输出流 |
void write(char[] cbuf) | 将cbuf.length个字符从指定字符数组写入此文件输出流中 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
3.2 FileReader类(重点)
java.io.FileReader类主要用于从文本文件读取文本数据内容。
常用的方法
方法声明 | 功能介绍 |
---|---|
FileReader(String fileName) | 根据参数指定的文件名构造对象 |
int read() | 读取单个字符的数据并返回,返回-1表示读取到末尾 |
int read(char[] cbuf, int offset, int length) | 从输入流中将最多len个字符的数据读入一个字符数组中,返回读取到的字符个数,返回-1表示读取到末尾 |
int read(char[] cbuf) | 从此输入流中将最多 cbuf.length 个字符的数据读入字符数组中,返回读取到的字符个数,返回-1表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
3.3 FileOutputStream类(重点)
java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入到输出流中。
常用的方法
方法声明 | 功能介绍 |
---|---|
FileOutputStream(String name) | 根据参数指定的文件名来构造对象 |
FileOutputStream(String name,boolean append) | 以追加的方式根据参数指定的文件名来构造对象 |
void write(int b) | 将指定字节写入此文件输出流 |
void write(byte[] b, int off, int len) | 将指定字节数组中从偏移量off开始的len个字节写入此文件输出流 |
void write(byte[] b) | 将 b.length 个字节从指定字节数组写入此文件输出流中 |
void flush() | 刷新此输出流并强制写出任何缓冲的输出字节 |
void close() | 关闭流对象并释放有关的资源 |
3.4 FileInputStream类(重点)
java.io.FileInputStream类主要用于从输入流中以字节流的方式读取图像数据等。
常用的方法
方法声明 | 功能介绍 |
---|---|
FileInputStream(String name) | 根据参数指定的文件路径名来构造对象 |
int read() | 从输入流中读取单个字节的数据并返回,返回-1表示读取到末尾 |
int read(byte[] b, int off, int len) | 从此输入流中将最多len个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾 |
int read(byte[] b) | 从此输入流中将最多 b.length 个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
int available() | 获取输入流所关联文件的大小 |
3.5 BufferedOutputStream类(重点)
java.io.BufferedOutputStream类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层
系统,是处理流。
常用的方法
方法声明 | 功能介绍 |
---|---|
BufferedOutputStream(OutputStream out) | 根据参数指定的引用来构造对象,OutputStream是抽象类 |
BufferedOutputStream(OutputStream out, int size) | 根据参数指定的引用和缓冲区大小来构造对象 |
void write(int b) | 写入单个字节 |
void write(byte[] b, int off, int len) | 写入字节数组中的一部分数据 |
void write(byte[] b) | 写入参数指定的整个字节数组 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
3.6 BufferedInputStream类(重点)
java.io.BufferedInputStream类主要用于描述缓冲输入流,是处理流。
常用的方法
方法声明 | 功能介绍 |
---|---|
BufferedInputStream(InputStream in) | 根据参数指定的引用构造对象,InputStream是抽象类 |
BufferedInputStream(InputStream in, int size) | 根据参数指定的引用和缓冲区大小构造对象 |
int read() | 读取单个字节 |
int read(byte[] b, int off, int len) | 读取len个字节 |
int read(byte[] b) | 读取b.length个字节 |
void close() | 关闭流对象并释放有关的资源 |
3.7 BufferedWriter类(重点)
java.io.BufferedWriter类主要用于写入单个字符、字符数组以及字符串到输出流中,是处理流。
常用的方法
方法声明 | 功能介绍 |
---|---|
BufferedWriter(Writer out) | 根据参数指定的引用来构造对象,Writer是抽象类 |
BufferedWriter(Writer out, int sz) | 根据参数指定的引用和缓冲区大小来构造对象 |
void write(int c) | 写入单个字符到输出流中 |
void write(char[] cbuf, int off, int len) | 将字符数组cbuf中从下标off开始的len个字符写入输出流中 |
void write(char[] cbuf) | 将字符串数组cbuf中所有内容写入输出流中 |
void write(String s, int off, int len) | 将参数s中下标从off开始的len个字符写入输出流中 |
void write(String str) | 将参数指定的字符串内容写入输出流中 |
void newLine() | 用于写入行分隔符到输出流中 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
3.8 BufferedReader类(重点)
java.io.BufferedReader类用于从输入流中读取单个字符、字符数组以及字符串,是处理流。
常用的方法
方法声明 | 功能介绍 |
---|---|
BufferedReader(Reader in) | 根据参数指定的引用来构造对象,Reader是抽象类 |
BufferedReader(Reader in, int sz) | 根据参数指定的引用和缓冲区大小来构造对象 |
int read() | 从输入流读取单个字符,读取到末尾则返回-1,否则返回实际读取到的字符内容 |
int read(char[] cbuf, int off, int len) | 从输入流中读取len个字符放入数组cbuf中下标从off开始的位置上,若读取到末尾则返回-1,否则返回实际读取到的字符个数 |
int read(char[] cbuf) | 从输入流中读满整个数组cbuf |
String readLine() | 读取一行字符串并返回,返回null表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
3.9 PrintStream类
java.io.PrintStream类主要用于更加方便地打印各种数据内容,是处理流。
详细可参考文章:
Java IO流之PrintStream分析
-
System.out.println()方法中的out就被包装成了PrintStream类;System.err也是PrintStream流;System.in是没有包装过的抽象类InputStream,所以不能直接使用。
-
PrintStream流永远不会抛出异常.因为做了try{}catch(){}会将异常捕获,出现异常情况会在内部设置标识,通过checkError()获取此标识.
-
PrintStream流有自动刷新机制,例如当向PrintStream流中写入一个字节数组后自动调用flush()方法.
-
PrintStream流中基本所有的print(Object obj)重载方法和println(Object obj)重载方法都是将对应数据先转换成字符串,使用BufferedWriter然后调用write()方法写到底层输出流中.
常用的方法
方法声明 | 功能介绍 |
---|---|
PrintStream(OutputStream out) | 根据参数指定的引用来构造对象,OutputStream是抽象类。除了向控制台打印,通过参数调用OutputStream抽象类的子类FileoutStream关联一个文件,可以实现向文件中打印 |
PrintStream(PrintStream out, boolean autoFlush) | 构造对象并设置是否自动刷新 |
PrintStream(PrintStream out, boolean autoFlush, String encoding) | 构造制定编码方式的对象,设置是否自动刷新 |
void print(String s) | 用于将参数指定的字符串内容打印出来 |
void println(String x) | 用于打印字符串后并终止该行 |
void flush() | 刷新流 |
void close() | 用于关闭输出流并释放有关的资源 |
3.10 PrintWriter类
java.io.PrintWriter类主要用于将对象的格式化形式打印到文本输出流,是处理流。
常用的方法
方法声明 | 功能介绍 |
---|---|
PrintWriter(Writer out) | 根据参数指定的引用来构造对象,Writer是抽象类 |
void print(String s) | 将参数指定的字符串内容打印出来 |
void println(String x) | 打印字符串后并终止该行 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
3.11 OutputStreamWriter类
java.io.OutputStreamWriter类主要用于实现从字符流到字节流的转换。
常用的方法
方法声明 | 功能介绍 |
---|---|
OutputStreamWriter(OutputStream out) | 根据参数指定的引用来构造对象 |
OutputStreamWriter(OutputStream out, String charsetName) | 根据参数指定的引用和编码构造对象 |
void write(String str) | 将参数指定的字符串写入 |
void flush() | 刷新流 |
void close() | 用于关闭输出流并释放有关的资源 |
3.12 InputStreamReader类
java.io.InputStreamReader类主要用于实现从字节流到字符流的转换。
常用的方法
方法声明 | 功能介绍 |
---|---|
InputStreamReader(InputStream in) | 根据参数指定的引用来构造对象 |
InputStreamReader(InputStream in, String charsetName) | 根据参数指定的引用和编码来构造对象 |
int read(char[] cbuf) | 读取字符数据到参数指定的数组 |
void close() | 用于关闭输出流并释放有关的资源 |
四、 字符编码
4.1 编码表的由来
- 计算机只能识别二进制数据,早期就是电信号。为了方便计算机可以识别各个国家的文字,就需要
- 将各个国家的文字采用数字编号的方式进行描述并建立对应的关系表,该表就叫做编码表。
4.2 常见的编码表
- ASCII:美国标准信息交换码, 使用一个字节的低7位二位进制进行表示。
- ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示。
- GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示。
- GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示。
- Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码。所有的文字都用两个字节16位二进制位来表示。
4.3 编码的发展
- 面向传输的众多 UTF(UCS Transfer Format)标准出现了,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。这是为传输而设计的编码并使编码无国界,这样就可以显示全世界上所
有文化的字符了。 - Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的Unicode编码是UTF-8和UTF-16。
4.4 DataOutputStream类(了解)
java.io.DataOutputStream类主要用于以适当的方式将基本数据类型写入输出流中。
常用的方法
方法声明 | 功能介绍 |
---|---|
DataOutputStream(OutputStream out) | 根据参数指定的引用构造对象 OutputStream类是个抽象类,实参需要传递子类对象 |
void writeInt(int v) | 用于将参数指定的整数一次性写入输出流,优先写入高字节 |
void close() | 用于关闭文件输出流并释放有关的资源 |
4.5 DataInputStream类(了解)
java.io.DataInputStream类主要用于从输入流中读取基本数据类型的数据。
常用的方法
方法声明 | 功能介绍 |
---|---|
DataInputStream(InputStream in) | 根据参数指定的引用来构造对象 InputStream类是抽象类,实参需要传递子类对象 |
int readInt() | 用于从输入流中一次性读取一个整数数据并返回 |
void close() | 用于关闭文件输出流并释放有关的资源 |
4.6 ObjectOutputStream类(重点)
- java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中。
- 只能将支持 java.io.Serializable 接口的对象写入流中。
- 类通过实现 java.io.Serializable 接口以启用其序列化功能。
- 所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程。
常用的方法
方法声明 | 功能介绍 |
---|---|
ObjectOutputStream(OutputStream out) | 根据参数指定的引用来构造对象 |
void writeObject(Object obj) | 用于将参数指定的对象整体写入到输出流中 |
void close() | 用于关闭输出流并释放有关的资源 |
4.7 ObjectInputStream类(重点)
- java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来。
- 所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程。
常用的方法
方法声明 | 功能介绍 |
---|---|
ObjectInputStream(InputStream in) | 根据参数指定的引用来构造对象 |
Object readObject() | 主要用于从输入流中读取一个对象并返回 无法通过返回值来判断是否读取到文件的末尾 |
void close() | 用于关闭输入流并释放有关的资源 |
-
序列化版本号
序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。 -
transient关键字
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。 -
经验的分享
当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,然后将集合这个整体看做一个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来,从而避免了通过返回值进行是否达到文件末尾的判断。
4.8 RandomAccessFile类
java.io.RandomAccessFile类主要支持对随机访问文件的读写操作。
常用的方法
方法声明 | 功能介绍 |
---|---|
RandomAccessFile(String name, String mode) | 根据参数指定的名称和模式构造对象(r: 以只读方式打开 rw:打开以便读取和写入 rwd:打开以便读取和写入,同步文件内容的更新 rws:打开以便读取和写入,同步文件内容和元数据的更新) |
int read() | 读取单个字节的数据 |
void seek(long pos) | 用于设置从此文件的开头开始测量的文件指针偏移量 |
void write(int b) | 将参数指定的单个字节写入 |
void close() | 用于关闭流并释放有关的资源 |
五、NIO(Java New IO)
进阶可以参考美团NIO技术分析
5.1 概念
- JDK1.4后JAVA提供了IO API,对高并发场景支持更好。
- 包括两套NIO,一套针对标准输入输出NIO(如文件读写),另一套是网络编程NIO。
5.2 比较
- NIO和IO又相同的作用和目的,只是实现方式不同。
- NIO和IO最大的区别是数据打包和传输方式;
- IO是面向流,而流是单向的;NIO是面向缓冲区的,并且通过管道实现了读写双向:
面向流的IO一次一个字节地处理数据,一个数据流产生一个字节,一个输出流消费一个字节;
面向缓冲区的NIO以块的形式处理数据,每个操作都在一步中产生活消费一个数据块,处理速度更快;
拿水龙头来比喻的话,流就像水龙头滴水,每次只有一滴;块就像水龙头往水壶放水,放满之后对一整个水壶的水进行操作;
- IO是阻塞IO,NIO是非阻塞IO;
- 在网络编程IO中,NIO新增了选择器(Selectors)的处理方式;
5.3 核心组件
NIO的核心组件有三个:
- 通道(Channel),配合缓冲区来处理数据;
- 缓冲区(Buffer),配合通道来处理数据;
- 选择器(Selector),支持NIO异步非阻塞模式
5.3.1 Buffer缓冲区
-
在NIO中,所有的缓冲区类型都继承于抽象类Buffer,8种JAVA基础数据类型中除了boolean,都有对应的Buffer子类,其中最常用的是ByteBuffer(二进制数据)。
-
-
buffer有四个主要属性:
- private int capacity; // 缓冲区中能容纳数据元素的最大容量,设置后不可变
- private int limit; // 缓冲区中一次可操作的数据大小,可变
- private int positon; // 下一个要被读写的位置
- private int mark; //上次的读写位置
import java.nio.ByteBuffer;
public class Demo
public static void main(String[] args) {
// 在堆中创建缓冲区: allocate(int capacity)
ByteBuffer buffer1 = ByteBuffer.allocate(5);
// 在系统内存中创建缓冲区: allocateDirect(int capacity)
ByteBuffer buffer2 = ByteBuffer.allocateDirect(5);
// 通过普通数据创建缓冲区: wrap(byte[] arr)
byte[]arr = {97,98,99};
ByteBuffer buffer3 = ByteBuffer.wrap(arr);
// =========buffter内容填充与获取)==============
buffer1.put((byte)10); // capacity = 5; limit = 5; position = 1
System.out.println(buffer.get(0)); // 10
buffer1.mark(); //做标记
buffer1.reset(); //还原上次读写位置
buffer1.flip(); // capacity = 5; limit = 1; position = 0 可以理解为写切读模式
byte[] bytes[] = new byte[buffer1.limit];
buffer1.get(bytes); // capacity = 5; limit = 1; position = 1
buffer.clean(); // capacity = 5; limit = 5; position = 0 可以理解为切为写模式,此时原来的数据还在缓冲区仍可get,但属于被遗忘的状态
}
}
5.3.2 channel通道
5.3.2.1 java.nio.channels.channel接口:
- FileChannel:本地资源处理。用户读取、写入、映射和操作文件的通道。
- DatagramChannel:网络资源的处理。通过UDP协议读写网络中的数据。
- SocketChannel:网络资源的处理。通过TCP协议读写网络中的数据。
- ServerSocketChannel:网络资源的处理。可以监听新进来的TCP连接,对每个连接建立一个SocketChannel。
FileInputStream fileInputStream = new FileInputStream(