
1. IO流概述
- File:表示系统中的文件或者文件夹的路径。
- 注意:File类只能对文件本身进行操作,不能读写文件里面存储的数据。
- IO流:读写文件/网络中的数据。
- 读=input=将文件/网络中的数据,读进内存
- 写=output=将内存中的数据,写进文件/网络
- IO流的分类:
- 按照流的方向
- 输入流:读取
- 输出流:写出
- 按照操作文件类型
- 字节流:图片,视频,音频,文本...所有类型文件
- 字符流:纯文本文件(.txt, .md)
- 按照流的方向
2. 字节输出流OutputStream
以字节为单位,一个字节一个字节输出。 OutputStream是一个接口,下面有很多个实现类:
- FileOutputStream
- BufferedOutputStream
2.1 基本语法
FileOutputStream: 操作本地文件的字节输出流,可以把程序中的数据写到本地文件中。
import java.io.FileOutputStream;
import java.io.IOException;public class MyIODemo {public static void main(String[] args) throws IOException {// 1. 创建对象FileOutputStream fos = new FileOutputStream("src/com/gtc/data/a.txt");// 2. 写出数据fos.write(97);// 3. 释放资源fos.close();}
}
- 1.创建对象
- 参数是字符串表示的路径或者File对象
- 如果文件不存在会创建一个新的文件,但是要保证父级路径存在
- 如果文件存在,则会清空文件
- 2.写数据
- write方法的参数是整数,但是实际上写到本地文件是对应的ASCII字符
- 97对应a,98对应b,57对应9,55对应7
- 3.释放资源
- 每次使用完流之后,都要释放资源
- 如果不释放资源,别的程序就无法对该文件进行操作
- 目的:解决资源的占用
2.2 字节输出流写出数据的三种方式

public class MyIODemo {public static void main(String[] args) throws IOException {MyIODemo demo = new MyIODemo();demo.test01();demo.test02();demo.test03();}public void test01() throws IOException {// 1. 创建对象OutputStream os = new FileOutputStream("src/com/gtc/data/a.txt");// 2. 一次写一个字节数据os.write(97);// 3. 释放资源os.close();}public void test02() throws IOException {// 1. 创建对象OutputStream os = new FileOutputStream("src/com/gtc/data/b.txt");// 2. 一次写一个字节数组数据byte[] bytes = {97,98,99,100};os.write(bytes);// 3. 释放资源os.close();}public void test03() throws IOException {// 1. 创建对象OutputStream os = new FileOutputStream("src/com/gtc/data/c.txt");// 2. 一次写一个字节数组的部分数据byte[] bytes = {97,98,99,100};os.write(bytes,1,2); // 从索引1开始写,长度为2// 3. 释放资源os.close();}
}
2.3 换行和续写
- Windows的换行符: \r\n
- Linux的换行符: \n
- MacOS的换行符: \r
- 注意:在Windows操作系统中,Java对回车进行了优化。虽然完整的是\r\n,但是我们写其中一个\r或者\n,Java也可以实现换行,因为Java在底层会补全。
- 续写:如果想要续写,打开续写开关即可。开关位置在创建对象的第二个参数,默认为false,表示关闭续写。

第二个参数为true表示打开了续写开关,不会清空源文件。
3. 字节输入流InputStream
3.1 基本语法
public class MyIODemo {public static void main(String[] args) throws IOException {// 1. 创建对象InputStream is = new FileInputStream("src/com/gtc/data/c.txt");// is.read()一次读取一个字节, 读到末尾返回-1while (true) {int c = is.read();if (c == -1) {break;}System.out.print((char) c);}is.close();}
}
- 创建输入流对象
- 如果文件不存在,就直接报错
3.2 文件拷贝
通过InputStream读取文件字节到内存,通过OutputStream将字节流写到另一个文件夹。


public class MyIODemo {public static void main(String[] args) throws IOException {// 1. 创建对象InputStream in = new FileInputStream("movie/demo.mp4");OutputStream out = new FileOutputStream("movie/copy.mp4");// 2. 读一个字节,写一个字节int b;while ((b = in.read()) != -1) {out.write(b);}// 3. 释放资源out.close();in.close();}
}
3.3 文件拷贝的弊端
一次读取一个字节,太慢了。因为IO很费时间。

大文件拷贝:创建数组5M~10M。
5M = 1024 byte * 1024 * 5
public class MyIODemo {public static void main(String[] args) throws IOException {// 1. 创建对象InputStream in = new FileInputStream("movie/demo.mp4");OutputStream out = new FileOutputStream("movie/copy.mp4");// 2. 一次读取5*1024*1024字节(5MB)byte[] buf = new byte[ 5 * 1024 * 1024]; // 5MB的bufint len; // 返回值: 本次读取到了多少个字节数据while ((len = in.read(buf)) != -1) {out.write(buf, 0, len);}// 3. 释放资源out.close();in.close();}
}
4. 字符集
确定一个文本文件里面保存的什么内存,总结就两点:
- 确定字符集(例如:Unicode字符集)
- 确定编码解码规则(例如:utf-8编码解码规则)
Unicode字符集 = {'字符1': 码点1,'字符2': 码点2,.......}
utf-8编码规则 = {'码点1': 编码1,'码点2': 编码2,.......}
utf-8解码规则 = {编码1: 码点1',编码2:'码点2',.......}
4.1 ASCII字符集
一个字节可以表示256 种不同的值,范围是从 0 到 255。而ASCII表只定义了128个字符(从 0 到 127),这意味着使用一个字节来存储ASCII字符时,实际上有一半的可能值(128到255)并未被标准ASCII所使用。

ASCII表给英文国家用够用了。但是其他国家不够用。
计算机最小的存储单位是字节。ASCII编码的第一位是0

4.2 GBK字符集
GBK=ASCII+其他
GB: 国标
K: 扩展

"ANSI 编码" 是微软 Windows 系统中的一个术语,并不是某个具体的字符集标准(如 ASCII 或 UTF-8),而是指:
当前操作系统区域设置(Locale)所使用的默认编码方式。
也就是说,它不是一个固定的编码,而是一个“变量”,取决于你操作系统的语言和地区设置。
我们使用的是简体中文Windows操作系统,在大陆,ANSI默认编码规则是GBK。
GBK完全兼容ASCII。
GBK的英文编码解码:
同ASCII编码解码规则(一个字节)

GBK的中文编码解码:
- 一个汉字用两个字节存储
- 高位字节二进制一定以1开头,转成十进制之后是一个负数

4.3 Unicode字符集
国际标准字符集,它将世界各种语言的每一个字符定义一个唯一的编码,以满足跨语言、跨平台文本信息转换。
- UTF-8编码规则:用1-4个字节保存
- UTF-16编码规则:用2-4个字节保存
- UTF-32编码规则:固定用4个字节保存
Unicode是字符集,UTF-8/16/32是Unicode字符的编码解码方式。
UTF-8 = ASCII + 其他

UTF-8的英文编码解码:
一个字节表示,和ASCII编码解码一样。
UTF-8的中文编码解码:


4.4 为什么会有乱码?

4.5 如何不产生乱码?
- 不要用字节流读取文本文件
- 编码解码使用同一个码表,同一个编码方式
4.6 Java中编码和解码的方法

public class MyIODemo {public static void main(String[] args) throws IOException {// 1. 默认方式编码String str = "你好aaabbb";byte[] bytes = str.getBytes(); // IDEA默认使用utf-8编码(一个中文三个字节,一个字母一个字节)System.out.println(Arrays.toString(bytes));// 2. 指定方式编码String str2 = "你好aaabbb";byte[] bytes2 = str.getBytes("GBK"); // GBK一个中文2个字节,一个字母一个字节)System.out.println(Arrays.toString(bytes2));// 3. 默认解码String destr = new String(bytes);System.out.println(destr);// 4. 指定方式解码String destr2 = new String(bytes,"GBK");System.out.println(destr2);}
}
4.7 码点
Unicode字符集某个字符的码点 不等于 utf-8编码后的数据
5. 字符输入流Reader
字符流=字节流+字符集
- 输入流:一次读取一个字节,遇到中文时,一次读取多个字节。
- 输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中。

5.1 为什么Java中的char能表示utf-8编码的汉字?

磁盘中存储的是 UTF-8 编码的字节(3 字节),Java 在读取时会将它们解码为 Unicode 码点,并用 char(2 字节)来表示这个字符。
1. 磁盘存储:UTF-8 编码的字节
- 文件中的内容是以某种编码格式(如 UTF-8)写入的。
- 比如中文字符
'汉'的 Unicode 码点是U+6C49,在 UTF-8 中被编码为 3 个字节:E6 B1 89 (十六进制)
2. 读取文件:加载 3 字节数据到内存
- 使用
FileInputStream或Reader读取文件时,系统会从磁盘读取原始字节。 - 对于
InputStreamReader或FileReader,它会根据指定或默认的编码(如 UTF-8)对这些字节进行解码。
3. 解码:识别这是一个中文字符
- 解码器(如 UTF-8 解码器)会分析这 3 个字节的结构,判断出它是一个完整的 UTF-8 字符。
- 然后将其转换为对应的 Unicode 码点:
U+6C49(十进制 27721)。
4. 内存存储:使用 char 表示该字符
- Java 中的
char是一个 16 位无符号整数,正好能容纳0 ~ 65535范围内的所有 Unicode 码点(即 BMP 平面)。 - 所以
'汉'的码点U+6C49(十进制 27721)可以直接用一个char表示:char ch = '汉'; // 实际上存储的是 0x6C49(十进制 27721)
| 阶段 | 存储形式 | 占用空间 | 描述 |
|---|---|---|---|
| 磁盘 | UTF-8 字节 | 3 字节 | '汉' 在 UTF-8 中是 E6 B1 89 |
| 内存 | Unicode 码点 | 2 字节 | 用 char 表示,值为 0x6C49(27721) |
5.2 基本语法
public class MyIODemo {public static void main(String[] args) throws IOException {Reader reader = new FileReader("src/com/gtc/data/d.txt");int ch; // int四个字节,能够表示Unicode字符集的所有码点while ((ch = reader.read()) != -1) {System.out.println((char) ch); // 根据码点,找字符}reader.close();}
}
-
read方法不是直接读取原二进制数据到内存,而是先读取,再解码。
5.3 字符输入流原理
本质:一次读取8192字节到内存,然后一个字符一个字符读取,避免频繁的磁盘IO。
- 最初读进内存的是编码之后的字节数据(即文件在磁盘上存储的形式)
- 然后这些字节被解码成字符数据(char),供程序使用。
第一次调用fr.read(),并不是读一个字符到内存,而是读取8192字节数据到内存。



6. 字符输出流Writer
6.1 基本语法
FileWriter类的构造方法:

FileWrite类的成员方法:

public class MyIODemo {public static void main(String[] args) throws IOException {Writer writer = new FileWriter("src/com/gtc/data/test.txt", true);writer.write("你好hello");writer.close();}
}
6.2 字符流输出原理


