IO流

Stream,指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。

一般来说流的特性有以下几点:

  1. 先进先出:最先写入输出流的数据最先被输入流读取到
  2. 顺序存取:可以一个接一个地往流中写入一串字节,读取时也将按写入顺序读取一串字节,不能随机访问中间的数据(RandomAccessFile除外)
  3. 只读或只写:每个流都只能是输入流或输出流中的一种,不能同时具备两种能力,输入流只能进行读操作,输出流只能进行写操作

IO流分类

主要分类方式有三种:

  1. 按数据流的方向分为输入流和输出流

    输入和输出是对于应用程序而言的,拿文件读写来说吧,写入文件的其实是输出流,读取文件是输入流,很容易搞混

  2. 按处理数据单位分为字节流和字符流

    字节流操作的单元是数据单元为8位的字节,而字符流则是16位的字符

  3. 按功能分为节点流和处理流

    节点流:直接操作数据读写的流类,比如FileInputStream

    处理流:对一个已经存在的流的链接和封装,通过对数据进行处理为程序提供功能强大和灵活的读写功能,比如BufferedInputStream

    image.png

缓冲流是众多处理流中非常重要的,我们这里细讲一下

程序与磁盘的交互相对于内存运算是很慢的,尽可能减少程序与磁盘间的交互次数可以有效提升程序效率。缓冲流就是这种思路,普通流每次只读写一个字节,而缓冲流在内存中设置了缓冲区,缓冲区先存储足够的待操作数据后再与内存或者磁盘进行交互,减少了交互次数

完整的IO分类图如下:(看一眼得了)

image.png

IO流对象

我们了解一些比较重要的类就行

File类

File类是用来操作文件的类(操作的是文件本身而非文件中的数据),比如对文件和目录的创建删除重命名等等,实现了Serializable,Comparable,支持序列化和排序

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.File;

public class FileExample{
public staticnvoid main(String[] args) {
//通过文件路径字符串创建File对象 File(String pathname)
File file1 = new File("D:/testFile1.txt");

//通过父路径和子路径字符串创建File对象 File(String parent, String child)
File file2 = new File("D:/test","testFile2.txt");

//通过父File对象和子路径字符串创建File对象 File(File parent, String child)
File parent = new File("D:/test");
File file3 = new File(parent, "testFile3");

//通过URI来创建File对象 File(URI uri)
URI uri = new URI("file:///D:/testFile4.txt");
File file4 = new File(uri);
//URI即统一资源标识符(Uniform Resource Identifier),用于标识抽象或物理资源的字符串
//上方使用的uri必须是file方案(scheme),即必须以file://开头,表示本地文件系统中的资源
}
}

常用方法

File类的常用方法有创建File对象,检查存在性,创建文件和目录,获取基本信息,列出目录内容,删除文件和目录等等,其中创建对象已经由构造方法介绍,这里不再赘述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import java.io.File;
import java.io.IOException;

public class FileExample{
public static void main(String[] args){
String filePath = "example.txt";
String directoryPath = "exampleDirectory";

File file = new File(filePath);
File directory = new File(directoryPath);

//检查文件和目录的存在性 boolean exists()
System.out.println("文件是否存在:" + file.exists());
System.out.println("目录是否存在:" + directory.exists());

//创建新文件 boolean createNewFile() 尝试创建一个新的空文件,由调用它的File对象所表示,创建的位置由File对象的路径决定
try {
if (file.createNreFile()) {
System.out.println("文件创建成功:" + file.getAbsolutePath());
} else {
System.out.println("文件已存在:" + file.getAbsolutePath());
}
} catch (IOException e) {
System.err.println("创建文件时出错:" + e.getMessage());
}

//创建新目录 boolean mkdir() 尝试在文件系统中创建一个新的目录,由调用它的File对象表示
if (directory.mkdir()) {
System.out.println("目录创建成功:" + directory.getAbsolutePath());
} else {
System.out.println("目录已存在或创建失败:" + directory.getAbsolutePath());
}

//获取文件的基本信息,其中绝对路径指的是从文件系统的根目录开始到该文件所在位置的完整且唯一的路径表示
System.out.println("文件名称: " + file.getName());
System.out.println("文件路径: " + file.getPath());
System.out.println("文件绝对路径: " + file.getAbsolutePath());
System.out.println("文件长度: " + file.length() + " 字节");
System.out.println("是否为文件: " + file.isFile());
System.out.println("是否为目录: " + file.isDirectory());

//获取目录的基本信息
System.out.println("目录名称: " + directory.getName());
System.out.println("目录路径: " + directory.getPath());
System.out.println("目录绝对路径: " + directory.getAbsolutePath());
System.out.println("是否为文件: " + directory.isFile());
System.out.println("是否为目录: " + directory.isDirectory());

//列出目录中的文件和子目录
if (directory.isDirectory()) {
String[] contents = directory.list();
if (contents != null) {
System.out.println("目录内容:");
for (String content : contents) {
System.out.println(content);
}
}
}

//删除文件和目录
if (file.delete()) {
System.out.println("文件删除成功:" + file.getAbsolutePath());
} else {
System.out.println("文件删除失败:" + file.getAbsolutePath());
}

if (directory.delete()) {
System.out.println("目录删除成功:" + directory.getAbsolutePath());
} else {
System.out.println("目录删除失败:" + directory.getAbsolutePath());
}
}
}

字节流

字节流的基类为InputStream和OutputStream两个抽象类,所有具体的字节流都分别继承了这两个类

字节输入流InputStream

它是所有字节输入流类的基类,定义类从数据源读取字节的基本方法。

常用方法 解释
int read() 从输入流读取一个字节的数据,并返回该字节的整数表示(0,255)。如果到达流的末尾,即读取完所有数据,则返回-1
int read(byte[] b) 读取一定量的字节数据,储存到缓冲区数组b中,返回实际读取的字节数,如果到达流的末尾则返回-1
int read(byte[] b, int off, int length) 读取最多length个字节数据储存至缓冲区数组b中,从数组的off位置开始存储并返回实际读取的字节数,若到达流的末尾则返回-1
void close() throws IOException 关闭输入流,释放与该流关联的所有系统资源

InputStream类中有很多实现子类,下方列举了一些比较常用的类型

InputStream

常用类 解释
ObjectInputStream 对象输入流,主要用于实现Java的对象反序列化功能,即将字节流重新转化为Java对象
FilterInputStream 装饰(过滤)输入流,是一个抽象类,它允许我们在不改变底层输入流的基础上添加额外的处理逻辑比如缓冲,加密等等
BufferedInputStream 缓冲输入流,继承自FilterInputStream,为节点流添加缓冲功能,从而提升效率
DataInputStream 数据输入流,同样继承自FilterInputStream,可以将输入流中的字节数据转换为Java中的基本数据类型,使得数据的读取和处理更为方便
FileInputStream 文件输入流,用于对文件的读取操作,允许程序读取原始的字节数据
ByteArrayInputStream 字节数组输入流,主要用于从字节数组中读取数据,将字节数组当作输入流的数据源
PipedInputStream 管道输入流,主要用于不同线程间的管道通信

字节输出流OutputStream

它是所有字节输出流的基类,定义了例如向流中写入字节数据等的基本操作

常用方法 解释
void write(int a) 向输入流写入一个字节的数据,该字节数据为参数a的低八位(在计算机中,数据以二进制形式存储和处理,一个int类型在Java中占32位,所谓的低八位就是最右边的八位)
void write(byte[] a) 将字节数组a中的所有字节写入输入流
void write(byte[] a, int off, int len) 将字节数组a从偏移量off开始的len个字节写入输入流
void flush() 刷新此输出流并强制写出所有缓冲的输出字节
void close() throws IOException 关闭此输出流并释放与此流相关的所有系统资源

OutputStream类中有很多实现子类,下方列举了一些比较常用的类型

OutputStream

这里我们略去了与上方InputStream中相似的内容,仅介绍一下PrintStream

常用类 解释
PrintStream 打印流,继承自FilterOutputStream,提供了方便的打印功能,可用于输出各种数据类型的值,并且能够自动进行数据类型的转换,还支持格式化输出

字符流

字符流主要处理Unicode字符,以字符为单位进行读写操作。字节流处理字节数据,适合处理二进制文件,而字符流能自动处理字符编码和解码,所以在处理文本文件时更加方便。其最主要的抽象基类为Reader和Writer。

字符输入流Reader

它是所有字符输入流的基类,该类提供了读取字符数据的基本功能和通用接口,用于从各种数据源(如文件、网络连接等)读取字符信息

常用方法 解释
int read() 读取单个字符,返回读取字符的Unicode码值(0~65535),若到达流的末尾,则返回-1
int read(char[] cbuf) 将字符读入指定的字符数组中,返回实际读取的字符数,若到达流的末尾,则返回-1
int read(char[] cbuf, int off, int len) 将最多len个字符读入字符数组cbuf中,从数组的off位置开始存储,返回实际读取的字符数,若到达流的末尾则返回-1
long skip(long n) 跳过n个字符不读,返回实际跳过的字符数
boolean ready() 判断该流是否已经准备好被读取,若返回true则调用read()方法时不会被阻塞
void close() throws IOException 关闭该流并释放与之关联的所有系统资源

字符流的实现子类基本是跟字节流贯通的,不再赘述

字符输出流Writer

它是所有字符输出流的基类,该类提供了向各种目标写入字符数据的通用方法和统一接口

常用方法 解释
void write(int c) ~
void write(char[] cbuf) ~
void write(char[] cbuf, int off, int len) ~
void write(char[] str) ~
void write(char[] str, int off, int len) ~
void flush() ~
void close() throws IOException ~

序列化

序列化是指把对象输入到文件当中的过程

把对象从文件输入到程序的过程叫做反序列化,反序列化时也会生成一个新的对象