
「JavaSE学习笔记07」IO流
¶Chapter 13. IO流
¶13.1 File类
java.io.File类是文件(file)和目录(文件夹)(directory)路径名(path)的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
一个
File对象,代表硬盘中实际存在的一个文件或者目录(文件夹)。
- 绝对路径:从盘符开始的路径,是完整的路径。eg:
D:\\aaa\\233.txt - 相对路径:相对于项目目录的路径,是便携的路径(开发常用)。eg:
233.txt
¶13.1.1 构造方法
无论该路径下是否存在文件或者目录,都不影响File对象的创建。
public File(String pathname):通过将给定的路径名字符串pathname转换为抽象路径名,以创建新的File实例。1
2String pathname = "D:\\aaa\\233.txt";
File myfile = new File(pathname);public File(String parent, String child):从父路径名字字符串和子路径名字符串,创建新的File实例。1
2
3String parent = "D:\\aaa";
String name = "233.txt";
File myfile = new File(parent, child);public File(File parent, String child):从父抽象路径名和子路径名字符串,创建新的File实例。1
2
3File parentDir = new File("D:\\aaa");
String child = "233.txt";
File myfile = new File(parentDir, child);
¶13.1.2 关于获取信息的方法
public String getAbsolutePath():返回此File的绝对路径名的字符串。1
2
3
4
5
6
7File f = new File("E:\\素材\\灵感\\GVLOGO2333332.png");
System.out.println("文件的绝对路径为:" + f.getAbsolutePath());
//输出结果为:文件的绝对路径为:E:\素材\灵感\GVLOGO2333332.png
File f_2 = new File("灵感\\GVLOGO2333332.png"); //传入相对路径
System.out.println("文件的绝对路径为:" + f_2.getAbsolutePath()); //自动找到绝对路径,但不一定与实际相符。
//输出结果为:E:\IDEA-Projects\basic-codes\灵感\GVLOGO2333332.pngpublic String getPath():将此File转换为路径名的字符串。1
2
3
4
5
6
7File f = new File("E:\\素材\\灵感\\GVLOGO2333332.png");
System.out.println("文件的构造路径为:" + f.getPath());\
//输出结果为:文件的构造路径为:E:\素材\灵感\GVLOGO2333332.png
File f_2 = new File("灵感\\GVLOGO2333332.png");
System.out.println("文件的构造路径为:" + f_2.getPath());
//输出结果为:文件的构造路径为:灵感\GVLOGO2333332.pngpublic String getName():返回由此File表示的文件或目录的名称字符串。public long length():返回由此File表示的文件的大小(以字节为单位)。
¶13.1.3 关于判断的方法
public boolean exists():此File表示的文件或目录是否实际存在。public boolean isDirectory():此File表示的是否为目录。public boolean isFile():此File表示的是否为文件。
¶13.1.4 关于创建或删除的方法
public boolean createNewFile():当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。当该文件在实际当中并不存在时,返回true(表示创建文件);否则,返回false(不会再创建)。1
2
3
4File f = new File("233.txt");
System.out.println("是否存在:" + f.exists()); //false
System.out.println("是否创建:" + f.createNewFile()); //注意,该方法需要对IO异常进行处理
System.out.println("是否存在:" + f.exists()); //true注意,此方法只能创建文件,不能创建文件夹。且要保证创建文件的路径必须存在,否则抛出异常。
public boolean mkdir():创建由此File表示的单级空目录。1
2
3
4File f = new File("newDir2333");
System.out.println("是否存在:" + f.exists());
System.out.println("是否创建:" + f.mkdir());
System.out.println("是否存在:" + f.exists());public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录。也就说,既可创建单级空文件夹,也可创建多级文件夹。其中创建文件夹的路径和名称在构造方法中给出(即构造方法的参数)。当文件夹不存在时,方法会创建文件夹并返回
true;当文件夹在实际中已经存在,则不会创建并返回false;当构造方法中给出的路径不存在,返回false。1
2
3
4File f = new File("newDir666\\newDir668");
System.out.println("是否创建:" + f.mkdirs()); //true
File f2 = new File("newDir666\\newDir668");
System.out.println("是否存在:" + f2.mkdirs()); //false, 无需创建public boolean delete():删除由此File表示的文件或目录。当文件或目录删除成功则返回true;当文件夹中有内容,则不会删除,且返回false;当构造方法中路径在实际中并不存在,返回false。1
2System.out.println(f.delete()); //true
System.out.println(f2.delete()); //false
¶13.1.5 目录的遍历
public String[] list():返回一个String数组,表示该File目录中所有子文件或目录的名称字符串。public File[] listFiles():返回一个File数组,表示该File目录中所有子文件或目录的File对象(具有更多信息)。1
2
3
4
5
6
7File dir = new File("E:\\素材\\灵感");
//获取当前目录下的文件/文件夹名称
String[] names = dir.list();
for(String name : names) System.out.println(name);
//获取当前目录下的文件/文件夹对象
File[] files = dir.listFiles();
for(File file : files) System.out.println(file);注意,调用上面两种方法的
File对象,表示的必须是实际存在的目录,否则会抛出空指针异常。
文件搜索的案例:
搜索
D:\aaa目录中的.java文件。
1 | |
¶13.1.6 文件过滤器的优化
java.io.FileFilter,是一个接口,是File的过滤器。该接口的对象,只有一个方法,可以传递给File类的listFiles(FileFilter)作为参数:
boolean accept(File pathname)测试
pathname是否应该包含在当前File目录中,保留住则返回true,过滤掉则返回false。其保留规则:要么是.java文件;要么是目录,用于继续遍历。接口作为参数,则需传递子类对象,并覆写其中方法,我们选择匿名内部类方式较为方便。
通过过滤器作业,
listFiles(FileFilter)返回的数组元素中,子文件对象都是符合条件的,可直接打印。
1 | |
¶13.2 I/O流概述
Java中I/O操作主要指java.io包下的内容,进行输入、输出操作。
¶13.2.1 IO的分类
根据数据的流向分为:输入流和输出流。
- 输入流:将数据从其他设备上读取到内存中的流。
- 输出流:将数据从内存中写出到其他设备上的流。
根据数据的类型分为:字节流和字符流。
- 字节流:以字节为单位,读写数据的流。
- 字符流:以字符为单位,读写数据的流。
| 输入流 | 输出流 | |
|---|---|---|
| 字节流 | 字节输入流(InputStream) | 字节输出流(OutputStream) |
| 字符流 | 字符输入流(Reader) | 字符输出流(Writer) |

由于简单演示,在介绍字节流、字符流时,我们暂且将IO异常抛出。实际开发中,针对IO异常,应使用try...catch代码块进行处理。
¶13.3 字节流
¶13.3.1 字节输出流(OutputStream)
java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的几个基本方法:
public void close():关闭此输出流并释放与此流相关联的任何系统资源。public void flush():刷新此输出流并强制写出所有缓冲的输出字节。public void write(byte[] b):将b.length个字节从指定的b字节数组写出此输出流。public void write(byte[] b, int off, int len):将指定的b字节数组中从偏移量off开始的len个字节写出此输出流。public abstract void write(int b):写出一个字节的数据
¶13.3.2 FileOutputStream 类
java.io.FileOutputStream 类 是 文件输出流,它作为OutputStream中的一个子类,用于将数据写出到文件。
一、构造方法
public FileOutputStream(File file):创建文件输出流以写入由指定的File对象表示的文件。public FileOutputStream(String name):创建文件输出流写入以指定名称name的文件。
当你创建一个流对象时,必须传入一个文件路径。若该路径下没有这个文件,则会自动创建;若该文件已存在,则会清空该文件中数据。举例如下:
1 | |

二、写出字节数据
写出字节:调用
write(int b)方法,每次可以写出一个字节数据。注意,尽管参数为int类型的四个字节,但只会保留一个字节的信息写出。1
2
3
4
5
6
7
8
9
10public class JustTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("233.txt");
fos.write(97); //写入第一个字节,转换为字符即为'a'
fos.write(98); //写入第二个字节,转换为字符即为'b'
fos.write(99);
fos.close(); //流操作完毕后必须调用close方法以释放系统资源
}
}
// 233.txt文件中打印为:abc写出字节数组:调用
write(byte[] b)方法,每次可以写出数组中的数据。1
2
3
4
5
6
7
8
9public class JustTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("233.txt");
byte[] b = "国奖我来了".getBytes(); //字符串转化为字节数组
fos.write(b); //写出字节数组数据
fos.close(); //关闭资源
}
}
// 233.txt文件中打印为:国奖我来了写出指定长度的字节数组:调用
write(byte[] b, int off, int len),每次写出时从off索引开始,以len个字节。1
2
3
4
5
6
7
8
9public class JustTest {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("233.txt");
byte[] b = "abcdefgh".getBytes(); //字符串转化为字节数组
fos.write(b, 3, 4); //写出字节数组数据
fos.close(); //关闭资源
}
}
// 233.txt文件中打印为:defg
三、数据的追加续写
对于第二点中的方法,每次程序运行,创建输出流对象时,都会清空目标文件中的数据。为了保留目标文件中的数据,可以使用下面的构造方法(相比于第一点的构造方法,需要多传入一个append参数):
public FileOutputStream(File file, boolean append)public FileOutputStream(File file, boolean append)
当传入的append参数为true时表示追加数据,false表示清空原有的数据。
1 | |
【附】写出换行
- 回车符
\r:回到一行的开头(return) - 换行符
\n:下一行(newline)
对于系统中的换行:
- 系统,每行结尾为:
\r\n(回车+换行) - 系统,每行结尾为:
\n(换行) - 系统,每行结尾为:
\r(回车),从开始与统一。
¶13.3.3 字节输入流(InputStream)
java.io.InputStream 抽象类 是表示字节输入流所有类的超类,可以读取字节信息至内存中。它定义了字节输入流的几个基本方法:
public void close():关闭此输入流并释放与此流相关联的任何系统资源。public abstract int read():从数据中读入一个字节,并返回该字节,该方法在碰到流的结尾时返回-1public int read(byte[] b):读入一个字节数组,并返回实际读入的字节数,或者在碰到流的结尾时返回-1。注意,该方法最多读入b.length个字节。public int read(byte[] b, int off, int len)
¶13.3.4 FileInputStream 类
java.io.FileInpurStream 类 是文件输入流,从文件中读入字节。
一、构造方法
FileInputStream(File file):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的File对象file命名。FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名。
注意,在创建一个流对象时,必须传入一个文件路径,该路径下如果没有该文件,会抛出FileNotFoundException。
1 | |
二、读取字节数据
读取字节:调用
read()方法,每次可读取一个字节数据,并提升为int类型,读取到文件末尾时,返回-1。(注意,文件中含有中文时,读入后会变成乱码!)1
2
3
4
5
6
7
8
9public class JustTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("read.txt");
int b; //临时变量,保存读入一个字节数据转化为的int数据
while( (b = fis.read()) != -1)
System.out.println((char)b);
fis.close();
}
}使用字节数组读取:调用
read(byte[] b),每次读入b.length个字节数至数组中,并返回读取到的有效字节个数。同样地,当读取到末尾时,返回-1。使用数组读入,使得每次读入多个字节,从而减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class JustTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("read.txt");
int len;
byte[] b = new byte[2]; //定义2个字节长度的字节数组
while((len = fis.read(b)) != -1){ //每次读取后,将数组的有效字节部分,转换为字符串并打印
System.out.println(new String(b, 0, len)); //len表示每次读取的有效字节个数!(想想边界条件)
}
fis.close();//关闭资源
}
}
/*
输出结果为
ab
cd
e
*/
¶13.3.5 字节流应用:图片复制
复制原理:从已有文件中读入字节,将该字节写出到另一个文件中。
1 | |
¶13.4 字符流
在存储文本字符串时,需要考虑字符编码(character encoding)方式,Java提供字符流类,以字符为单位读写数据,专门用于处理文本文件(如中文字符)。
字符流,只能操作文本文件,不能操作图片、视频等非文本文件。当我们只希望 读或写 文本文件时,使用字符流;其他情况使用字节流。
¶13.4.1 字符输入类(Reader)
java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可读入字符信息至内存中。它定义了字符输入流的几个基本方法:
与字节输入类
InputStream的基本方法一致,只不过传入的是字符数组而不是字节数组
public void close():关闭此流并释放与此流相关联的任何系统资源。public int read():从输入流读入一个字符。public int read(char[] cbuf):从输入流中读取一些字符,并将他们存储到字符数组cbuf中。
¶13.4.2 FileReader 类
java.io.FileReader 类是读取字符文件的便利类,构造时使用系统默认的字符编码和默认字节缓冲区。其构造方法的实现就是使用了 FileInputStream (文件字节输入流)来实现
字符编码:字节与字符的对应规则,系统的中文编码默认为编码表(一个汉字占用2个字节,简体中文),而IDEA中为****(一个汉字占用3个字节,8位UniCode转换格式)
字节缓冲区:一个字节数组,用于临时存储字节数据。
一、构造方法:
FileReader(File file):给定要读取的File对象以创建一个新的FileReader。FileReader(String fileName):给定要读取的文件的名称fileName以创建一个新的FileReader。1
2
3
4
5
6
7public class JustTest {
public static void main(String[] args) throws IOException {
File file = new File("233.txt");
FileReader fr = new FileReader(file); //使用File对象创建流对象
FileReader fr_2 = new FileReader("666.txt"); //使用文件名称创建流对象
}
}
二、读取字符数据
读取字符:调用
read方法,每次可读入一个字符的数据,并提升为int类型,读取到文件末尾时返回-1。1
2
3
4
5
6
7
8
9
10public class JustTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("233.txt");
int b;
while((b = fr.read()) != -1){
System.out.println((char)b);
}
fr.close();
}
}使用字符数组读入:调用
read(char[] cbuf)方法,每次读入cbuf.length个字符数至数组中,并返回读取到的有效字符个数。同样地,当读取到末尾时,返回-1。1
2
3
4
5
6
7
8
9
10
11public class JustTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("233.txt");
int len;
char[] cbuf = new char[2];
while((len = fr.read(cbuf)) != -1){
System.out.println(new String(cbuf, 0, len)); //要获取有效字符
}
fr.close();
}
}
¶13.4.3 字符输出流(Writer)
java.io.Writer 抽象类 是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地,它定义了字符输出流的几个基本方法:
void write(int c):写入单个字符。void write(char[] cbuf):写入字符数组。abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分,以off作为开始索引,len个需写入的字符个数。void write(String str):写入字符串。void write(String str, int off, int len):写入字符串的某一部分,以off作为开始索引,len个需写入的字符个数。void flush():刷新该流的缓冲。void close():关闭此输出流但需要先刷新它。
¶13.4.4 FileWriter 类
java.io.FileWriter类是写出字符至文件的便利类,构造时使用默认的字符编码和默认字节缓冲区。
一、构造方法
FileWriter(File file):给定要读入的File对象,创建一个新的FileWriter。FileWriter(String fileName):给定要读入的文件名称,创建一个新的FileWriter。1
2
3
4
5
6
7public class JustTest {
public static void main(String[] args) throws IOException {
File file = new File("233.txt");
FileWriter fw = new FileWriter(file); //使用File对象创建流对象
FileWriter fw_2 = new FileWriter("666.txt"); //使用文件名称创建流对象
}
}
二、写出字符数据
写出字符:调用
write(int b)方法,每次可写出一个字符数据(至内存缓冲区,是字符转换为字节的过程)。关于关闭和刷新:关闭一个输出流的同时,也会冲刷用于该输出流的缓冲区:所有被临时置于缓冲区中,以使用更大的包的形式传递的字符在关闭输出流时都将被送出。
当数据不足以填充缓冲区,而又需要往客户端传数据,为了解决这个问题,便有了
flush的概念,将缓冲区的数据“强迫发送”。此外,如果既想写出数据,又想继续使用流,也需要
flush方法。flush:刷新缓冲区,但流对象仍可继续使用。close:先刷新缓冲区,然后通知系统释放资源,此时流对象不可再被使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class JustTest {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("233.txt");
fw.write('刷');
fw.flush();
fw.write('新');
fw.write("\r\n"); //写出换行!
fw.flush();
fw.write('关');
fw.close();
/*输出结果为:
刷新
关
*/
//fw.write('闭'); //关闭后再写入会抛出异常
}
}写出字符数组:调用
write(char[] cbuf)或write(char[] cbuf, int off, int len),每次可以写出字符数组中的数据。1
2
3
4
5
6
7
8
9public class JustTest {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("233.txt");
char[] arr = "你好打工人".toCharArray();
fw.write(arr); //你好打工人
fw.write(arr, 2, 3); //打工人
fw.close();
}
}写出字符串:调用
write(String str)或write(String str, int off, int len),每次可写出字符串中的数据。1
2
3
4
5
6
7
8
9public class JustTest {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("233.txt");
String msg = "你好打工人";
fw.write(msg); //你好打工人
fw.write(msg, 2, 3); //打工人
fw.close();
}
}
¶13.5 IO 异常的处理
¶13.5.1 JDK7前的处理
1 | |
¶13.5.2 JDK7的处理
可以使用JDK7优化后的try-with-resource语句,该语句确保了每个资源在语句结束时关闭。所谓的资源(resource),指在程序完成后必须关闭的对象。
格式为:
1 | |
在try括号内被引用的对象,能够自动地关闭,无需手动close
使用举例:
1 | |
¶13.6 属性集
java.util.Properties继承于Hashtable,以表示一个持久的属性集,它使用键-值结果存储数据,每个键及其对应值默认是字符串.
Properties类 被许多Java类使用,如获取系统属性时,System.getProperties方法即为返回一个Properties对象。
一、构造方法
public Properties():创建一个空的属性列表。
二、基本的存储方法
public Object setProperty(String key, String value):保存一对属性。public String getProperty(String key):使用此属性列表中指定的键key搜索属性值。public Set<String> stringPropertyNames():获取由所有键的名称组成的集合1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class JustTest {
public static void main(String[] args) {
Properties mypro = new Properties();
//添加键值对元素
mypro.setProperty("filename", "233.txt");
mypro.setProperty("length", "998244353");
mypro.setProperty("location", "D:\\233.txt");
System.out.println(mypro); //打印属性集对象
//通过键获取属性值
System.out.println(mypro.getProperty("filename"));
System.out.println(mypro.getProperty("length"));
//遍历属性值,获取由所有键组成的集合
Set<String> mystr = mypro.stringPropertyNames();
for(String key : mystr){
System.out.println(key + " : " + mypro.getProperty(key));
}
}
}
三、与流相关的方法
public void load(InputStream inStream):从字节输入流inStream中读入键值对。通过流对象,可关联至硬盘中保存的某文件中,读取到集合中使用。public void load(Reader reader):与上同理,传入的是字符输入流。注意:
- 文本中的数据,必须是键值对形式,可以使用空格、等号、冒号(英文)等符号作为分隔符。
- 可以使用
#进行注释,被注释的键值对不会被读入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/* 233.txt文件中 */
#save data
#Sun Nov 15 11:26:58 CST 2020
姓名:打工人
年龄=19岁
居住地 广州
#学校:TSH
/* JustTest.java文件中 */
public class JustTest {
public static void main(String[] args) throws IOException {
Properties mypro = new Properties();
mypro.load(new FileReader("233.txt")); //字符输入流
Set<String> myset = mypro.stringPropertyNames();
for(String key : myset){
System.out.println(key + "----" + mypro.getProperty(key));
}
}
}
/* 输出结果 */
姓名----打工人
居住地----广州
年龄----20岁
¶BeanUtils 工具类
该工具类,用于简化封装 JavaBean,使用前,需要导入 jar 包(以 commons-beanutils-1.8.0.jar 为例)。
在 IDEA 中导入类时,注意是选择 apache 的:

相关方法:
setProperty()getProperty()populate(Object obj, Map map):将 Map 集合的键值对信息,封装到对应的 JavaBean 对象中。
代码举例如下:(已提前设计好JavaBean:User)
1 | |
¶13.7 缓冲流
缓冲流,也叫高效流,是对4个基本的FileXxx流的增强。其基本原理即是,在创建流对象时,创建一个内置的默认大小的缓冲区数组,通过缓冲区的读写,减少系统IO次数,从而提高读写效率。
按数据类型分类有:
- 字节缓冲流:
BufferedInputStream,BufferedOutputStream - 字符缓冲流:
BufferedReader,BufferedWriter
¶13.7.1 字节缓冲流
构造方法:
public BufferedInputStream(InputStream in):创建一个新的缓冲输入流。public BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流。
使用举例:
1 | |
¶13.7.2 字符缓冲流
构造方法:
public BufferedReader(Reader in):创建一个新的缓冲输入流。public BufferedWriter(Writer out):创建一个新的缓冲输出流。
字符缓冲流的基本方法与普通字符流调用方式一致,但也具备特有的方法:
对于
BufferedReader,public String readLine():读一行文字。1
2
3
4
5
6
7
8
9
10
11public class JustTest {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("E:\\IDEA-Projects\\basic-codes\\233.txt"));
String myline = null; //保存读入的一行字符
while((myline = br.readLine()) != null){ //读取【每】一行的字符
System.out.println(myline);
System.out.println("------------");
}
br.close();
}
}对于
BufferedWriter,public void newLine():写出一行 行分隔符,由系统属性定义符号。1
2
3
4
5
6
7
8
9
10
11
12
13
14public class JustTest {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("666.txt"));
bw.write("早安");
bw.newLine(); //换行
bw.write("打工人");
bw.write("好起来了!");
bw.close();
}
}
/*输出结果:
早安
打工人好起来了!
*/
¶13.7.3 缓冲流应用:文本排序
将下列文本信息按段编号恢复顺序:

1 | |
¶13.8 转换流
¶13.8.1 字符编码和字符集
字符编码(Character Encoding):一套自然语言的字符与二进制数之间的对应规则。按其相应规则,将字符存储到计算机中,称为编码;将存储在计算机中的二进制数按照相应规则解析显示出来,称为解码。
字符集(Charset):也称编码表,是一个系统支持的所有字符的集合,包括各国文字、标点符号、图形符号、数字等。当指定其常见的字符集有:ASCII字符集、GBK字符集、Unicode字符集等。

当指定了编码,它所对应的字符集也即指定下来。
ASCII 字符集 :用于显示现代英文,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字、西文符号)。
- 基本ASCII字符集使用7位表示一个字符(共128个)
- 拓展字符集使用8位表示一个字符(共256个)。
ISO-8859-1 字符集:用于显示欧洲使用的语言。
GBxxx 字符集:用于显示中文而设计的一套字符集
- GBK:最常用的中文码表。
- GB18030:最新的中文码表。每个字可由1个、2个或4个字节组成。
Unicode 字符集:也称统一码、标准万国码,为表达任意语言的任意字符而设计。最多使用4个字节的数字来表达每个字母、符号或者文字。
- UTF-8编码:最常用,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。其编码规则有:128个US-ASCII字符,只需一个字节编码;拉丁文等字符,需要二个字节编码;大部分常用字(含中文),使用三个字节编码;其他极少使用的Unicode辅助字符,使用四字节编码。
- UTF-16
- UTF-32
转换流即是字节与字符间的桥梁。

¶13.8.2 InputStreamReader 类
转换流java.io.InputStreamReader,是Reader子类,是从字节流到字符流的桥梁,利用它读入字节,并使用指定的字符集(由名称指定,可接受平台默认字符集),将其字节解码为字符。
构造方法:
InputStreamReader(InputStream in, String charsetName):创建一个指定字符集的字符流。

使用举例:
1 | |
¶13.8.3 OutputStreamWriter 类
转换流java.io.OutputStreamWriter,是Writer子类,是从字符流至字节流的桥梁,使用指定的字符集,将字符编码为字节。
构造方法:
OutputStreamWriter(OutputStream in, String charsetName):创建一个指定字符集的字节流。

使用举例:
1 | |
¶13.8.4 转换流应用:转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
1 | |
¶13.9 序列化
Java 提供了一种对象序列化的机制,用一个字节序列表示一个对象,该字节序列包含该「对象的数据」、「对象的类型」和「对象中存储的数据」信息。当字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

该字节序列还可从文件中读入,重构对象,对其进行反序列化。
¶13.9.1 ObjectOutputStream 类
java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出至文件中,实现对象的持久存储。
构造方法:
public ObjectOutputStream(OutputStream out):创建一个指定OutputStream的ObjectOutputStream。
序列化操作:
一个对象若想要序列化,必须同时满足两个条件:
该类必须实现
java.io.Serializable接口(该接口是一个标记接口)。若不实现此接口的类,则不会使任何状态序列化或反序列化,抛出NotSerializableException。该类所有属性必须是可序列化的,若有一个属性不需要可序列化,则该属性必须注明是瞬态的,使用
transient关键字修饰。1
2
3
4
5
6
7
8public class Student implements java.io.Serializable{ //实现Serializable的接口
public String name;
public String No;
public transient String address; //transient瞬态修饰成员,不会被序列化
public void nameCheck(){
System.out.println("学号为" + No + "的学生姓名为:" + name);
}
} //为演示方便,部分方法暂忽略
写出对象的方法
public final void writeObject(Object obj):将指定对象写出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class JustTest {
public static void main(String[] args) {
Student a = new Student();
a.name = "Luffy"; a.No = "23333"; a.address = "Brazil";
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\IDEA-Projects\\666.txt"));
oos.writeObject(a); //写出对象,但注意该方法会抛出IO异常
oos.close();
System.out.println("Serialized data is saved successfully!");
} catch (IOException i){ //捕获IO异常
i.printStackTrace();
}
}
}
¶13.9.2 ObjectInputStream 类
ObjectInputStream反序列流,就之前使用的ObjectOutputStream序列化的原始数据恢复为对象。
构造方法:
public ObjectInputStream(InputStream in):创建一个指定InputStream的ObjectInputStream。
反序列化操作:
若能找到一个对象的.class文件,即可进行反序列化操作,调用ObjectInputStream 读入对象的方法:
public final Object readObject():读入一个对象。注意,JVM反序列化对象时,必须能够找到传入的.class文件的类,否则抛出ClassNotFoundException异常。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class JustTest {
public static void main(String[] args) {
Student a = null;
try{
FileInputStream fileIn = new FileInputStream("E:\\IDEA-Projects\\666.txt");
ObjectInputStream ois = new ObjectInputStream(fileIn); //传入字节输入流参数
a = (Student) ois.readObject(); //读入对象,返回Object类需强转一下
ois.close();
fileIn.close();
} catch (IOException i){
i.printStackTrace();
return;
} catch (ClassNotFoundException c){ //readObject会抛出这类异常
System.out.println("Student class can't be found!");
c.printStackTrace();
return;
}
System.out.println("Name:" + a.name + " Address:" + a.address); //若无异常则直接打印输出。
}
}尽管JVM反序列化对象时能够找到
.class,但注意.class文件在序列化对象之后发生了修改,则反序列化操作有可能失败,抛出个InvalidClassException异常。发生该异常的原因如下:- 该类的序列版本号与从流中读入的类描述符的版本号不匹配。
- 该类包含未知数据类型。
- 该类没有可访问的无参构造方法。
Serializable接口给需要序列化的类提供一个默认的序列版本号serialVersionUID,该版本号目的在于验证序列化的对象和对应类是否版本匹配。当然,也可以自定义版本号,从而避免序列化后再修改.class文件所发生的冲突异常。1
2
3
4
5
6
7
8
9
10public class Student implements java.io.Serializable{ //实现Serializable的接口
public String name;
public String No;
public transient String address; //transient瞬态修饰成员,不会被序列化
private static final long serialVersionUID = 1L; //加入序列版本号
public int eid; //假定我序列化之后再添加该属性再编译,是能够反序列化的(前提是你不能使用默认的序列版本号),该属性赋为默认值。
public void nameCheck(){
System.out.println("学号为" + No + "的学生姓名为:" + name);
}
}
¶13.9.3 序列化集合
练习:将存有多个自定义对象的集合进行序列化,并保存至文件中。然后再反序列化该文件,遍历集合打印对象信息。
1 | |
执行结果:

¶13.10 打印流
控制台打印输出即调用了print()/println()方法等,这些方法均来自于java.io.PrintStream 类,该类能够方便地打印各种数据类型的值。
¶13.10.1 PrintStream 类
java.io.PrintStream继承自父类OutputStream,故也有close()、write()、flush()等共性方法。
特点:
- 只负责数据的输出,不负责数据的读取。
- 与其他输出流不同,它永远不会抛出
IOException。(但还是能够抛出FileNotFoundException异常)
构造方法:
创建打印流PrintStream对象,其构造方法需传入 要输出的目的地(必须存在) 作为参数。
public PrintStream(File file):输出目的地为一个文件。public PrintStream(OutputStream out):输出目的地为一个字节输出流。
特有方法:
void print(任意类型)void println(任意类型):输出包括换行若使用继承自父类的
write方法写出数据,它会查询对应的编码表(如97->‘a’)并将其解码输出;若使用自己特有的方法print()或println(),写出的数据原样输出(如97->97)
另外,通过调用System.setOut()方法,可以将输出语句的目的地,改变为参数中传递的打印流的目的地(默认情况下,是控制台输出的)
static void setOut(PrintStream out):重新分配“标准”输出流。1
2
3
4
5
6
7
8
9public class Hello {
public static void main(String[] args) throws FileNotFoundException {
System.out.println("我在控制台输出!");
PrintStream ps = new PrintStream("C:\\Users\\admin\\IdeaProjects\\MyIdeaProjects\\666.txt");
System.out.println(ps);
System.out.println("我在打印流的目的地中输出!");
ps.close();
}
}