# 第 15 章:随堂复习与企业真题(File 类与 IO 流)
# 一、随堂复习
# 1. File 类的使用
- File 类的一个实例对应着
磁盘上的一个文件或文件目录
。 ----> “万事万物皆对象” - (熟悉)File 的实例化、常用的方法
- File 类中
只有新建、删除、获取路径等方法,不包含读写文件的方法,此时需要使用IO流
# 2. IO 流的概述
- IO 流的分类
- 流向:输入流、输出流
- 处理数据单位:字节流、字符流
- 流的角色:
节点流
、处理流
- IO 的 4 个抽象基类:
InputStream
\OutputStream
\Reader
\Writer
# 3. 节点流之:文件流
FileInputStream
\FileOutputStream
\FileReader
\FileWriter
- (掌握)读写数据的过程。
- 步骤 1:创建 File 类的对象,作为读取或写出数据的端点
- 步骤 2:创建相关的流的对象
- 步骤 3:读取、写出数据的过程
- 步骤 4:关闭流资源
# 4. 处理流之一:缓冲流
BufferedInputStream
\BufferedOutputStream
\BufferedReader
\BufferedWriter
- 作用:实现更高效的读写数据的操作
# 5. 处理流之二:转换流
- 层次 1:熟悉转换流的使用
InputStreamReader
、OutputStreamWriter
- 层次 2:(掌握)字符的编码和解码的过程、常用的字符集
- 解决相关的问题:读写字符出现乱码!本质问题:使用的解码集与编码集不一致。
# 6. 处理流之三:对象流
- 层次 1:熟悉对象流的使用
ObjectInputStream
:反序列化时需要使用的 apiObjectOutputStream
:序列化时需要使用的 api
- 层次 2:对象的序列化机制
- 使用场景:不同的进程之间通信、客户端(或浏览器端)与服务器端传输数据
- 自定义类要想实现序列化机制需要满足的要求及注意点。
# 7. 其它流的使用
- 了解:数据流:DataInputStream 、DataOutputStream
- 了解:标准的输入流、标准的输出流:
System.in
、System.out
- 了解:打印流:
PrintStream
、PrintWriter
# 二、企业真题
# 2.1 IO 流概述
# 1. 谈谈 Java IO 里面的常用类,字节流,字符流 (银 * 数据)
按 操作数据单位
的不同分为:
- ** 字节流(8bit)** 用于处理二进制数据,常用的类有
InputStream
和OutputStream
。- 常用的类有
FileInputStream
和FileOutputStream
,它们分别用于从文件中读取和写入字节
数据。
- 常用的类有
- ** 字符流(16bit)** 用于处理文本数据,常用的类有
Reader
和Writer
。- 常用的类有
FileReader
和FileWriter
,它们分别用于从文件中读取和写入字符
数据。
- 常用的类有
根据 IO流的角色
不同分为:节点流和处理流。
节点流:直接从数据源或目的地读写数据
处理流:不直接连接到数据源或目的地,而是“连接” 在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
缓冲流(
BufferedInputStream
,BufferedOutputStream
,BufferedReader
,BufferedWriter
)数据流(
DataInputStream
,DataOutputStream
)等
# 2. Java 中有几种类型的流?JDK 为每种类型的流提供一些抽象类以供继承,请说出他们分别是哪些类?(上海 * 厦 * 联网、极 * 科技)
在 Java 中,有四种类型的流:字节输入流、字节输出流、字符输入流和字符输出流。JDK 为每种类型的流都提供了抽象类以供继承。
字节输入流的抽象类是 InputStream
,它定义了读取字节数据的方法。字节输出流的抽象类是 OutputStream
,它定义了写入字节数据的方法。
字符输入流的抽象类是 Reader
,它定义了读取字符数据的方法。字符输出流的抽象类是 Writer
,它定义了写入字符数据的方法。
# 3. 流一般需不需要关闭?如果关闭的话用什么方法?处理流是怎么关闭的?(银 * 数据)
是的,流在使用完毕后通常 需要关闭
。这样可以释放系统资源, 防止资源泄漏
。可以使用 close()
方法来关闭流。
在处理流时,只需要 关闭最外层的流即可
。当最外层的流被关闭时,它内部包装的所有流都会被自动关闭。
例如,如果我们使用 BufferedReader
来包装 FileReader
,那么只需要调用 BufferedReader
的 close()
方法即可,它会自动关闭内部的 FileReader
。
BufferedReader br = new BufferedReader(new FileReader("file.txt")); | |
// ... | |
br.close(); // 关闭 BufferedReader,同时也会关闭内部的 FileReader |
在 Java 7 及以上版本中,还可以使用 try-with-resources
语句来自动关闭流。只需将流的声明放在 try 语句的括号中,当 try 语句块执行完毕后,流会被自动关闭。
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { | |
// ... | |
} // 当 try 语句块执行完毕后,br 会被自动关闭 |
# 4. OutputStream 里面的 write () 是什么意思?(君 * 科技)
OutputStream
类中的 write()
方法用于将指定的字节数据写出到输出流。它有三种重载形式:
write(int b)
:将指定的字节(b
)写出到输出流。write(byte[] b)
:将指定字节数组(b
)中的所有字节写出到输出流。write(byte[] b, int off, int len)
:将指定字节数组(b
)中从偏移量(off
)开始的len
个字节写出到输出流。
例如,下面的代码演示了如何使用 FileOutputStream
将字符串写入文件:
String str = "Hello, world!"; | |
try (FileOutputStream fos = new FileOutputStream("file.txt")) { | |
fos.write(str.getBytes()); | |
} |
# 2.2 缓冲流
# 1. BufferedReader 属于哪种流?他主要是用来做什么的?(国 * 电网)
属于 字符输入流
,它继承自 Reader
类。它的主要作用是为其他字符输入流(如 FileReader
)提供缓冲功能,以提高读取效率。
当我们从 BufferedReader
中读取数据时,它会一次性从底层输入流中读取多个字符并存储在 内部缓冲区
中。这样,当我们再次读取数据时,就可以直接从缓冲区中获取,而不需要再次访问底层输入流。
此外, BufferedReader
还提供了一些方便的方法,如 readLine()
,用于读取一行文本,不包括换行符。
例如,下面的代码演示了如何使用 BufferedReader
来读取文件中的文本:
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { | |
String line; | |
while ((line = br.readLine()) != null) { | |
System.out.println(line); | |
} | |
} |
# 2. 什么是缓冲区?有什么作用?(北京中油 **)
内部缓冲区是指缓冲流类(如 BufferedInputStream
、 BufferedOutputStream
和 BufferedReader
、 BufferedWriter
)内部用于临时存储数据的数组,类型为 byte[]
或 char[]
,大小默认是 8192
,当达到一定程度时,集中性的写出。它的作用是减少与磁盘的交互次数,从而提高读写效率。
例如,在使用 BufferedReader
读取数据时,它会一次性从底层输入流中读取多个字符(char)并存储在内部缓冲区中。这样,当我们再次读取数据时,就可以直接从缓冲区中获取,而不需要再次访问底层输入流。这样可以减少对底层输入流的访问次数,从而提高读取效率。
同理,在使用 BufferedWriter
写入数据时,它会先将数据写入内部缓冲区。当缓冲区满时,才会将数据一次性写入底层输出流。这样可以减少对底层输出流的访问次数,从而提高写入效率。
# 2.3 转换流
# 1. 字节流和字符流是什么?怎么转换?(北京蓝 *、* 海 * 供应链管理)
字节流用于处理 二进制数据
,常用的类有 InputStream
和 OutputStream
。
字符流用于处理 文本数据
,常用的类有 Reader
和 Writer
。
在某些情况下,我们需要将字节流和字符流进行转换。例如,当我们从网络套接字( Socket
)中读取文本数据时,需要将套接字的输入流(字节流)转换为字符流。
可以使用处理流中的转换流来实现,常用的类有 InputStreamReader
和 OutputStreamWriter
,用于实现字节流和字符流之间的转换。这两个类分别继承自 Reader
和 Writer
,它们可以将字节流转换为字符流,或将字符流转换为字节流。
例如,下面的代码演示了如何使用 InputStreamReader
将套接字的输入流(字节流)转换为字符流:
Socket socket = ...; | |
try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { | |
String line; | |
while ((line = br.readLine()) != null) { | |
System.out.println(line); | |
} | |
} |
# 2.4 序列化
# 1. 什么是 Java 序列化,如何实现 (君 * 科技、上海 * 厦物联网)
** 对象序列化机制
** 允许把内存中的 Java 对象转换成平台无关的二进制流(字节序列),以便将其存储在文件中或通过网络传输。与之相反的过程称为反序列化,即从字节序列中恢复对象。当其它程序获取了这种二进制流,就可以恢复成原来的 Java 对象。
要实现 Java 序列化,需要满足以下条件:
- 对象所属的类必须实现
Serializable
接口。这个接口是一个标记接口
,没有任何方法,只是用来标识一个类是否支持序列化。 - 对象中所有需要序列化的字段都必须是可序列化的。如果有不可序列化的字段,则需要将其声明为
transient
,以便在序列化时跳过该字段。
可以使用 ObjectOutputStream
类来实现对象的序列化。它继承自 OutputStream
,提供了 writeObject()
方法用于将对象写入输出流。
例如,下面的代码演示了如何使用 ObjectOutputStream
将一个对象序列化并写入文件:
MyObject obj = new MyObject(); | |
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file.ser"))) { | |
oos.writeObject(obj); | |
} |
与之相反,可以使用 ObjectInputStream
类来实现对象的反序列化。它继承自 InputStream
,提供了 readObject()
方法用于从输入流中读取对象。
例如,下面的代码演示了如何使用 ObjectInputStream
从文件中读取并反序列化一个对象:
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file.ser"))) { | |
MyObject obj = (MyObject) ois.readObject(); | |
} |
序列化机制的实现步骤如下:
自定义类需要实现
java.io.Serializable标识接口
,否则在序列化时报错NotSerializableException
自定义类需要显示声明一个静态常量:
static final long serialVersionUID
,用来唯一标识当前类,值可以任意指定如果类没有显示定义 serialVersionUID,它的值是 Java 运行时环境根据类的内部细节
自动生成
的。若序列化后,类的实例变量做了修改, serialVersionUID可能发生变化
,在反序列化时会因为序列版本号不匹配,导致反序列化时报错InvalidClassException
。如果声明了 serialVersionUID ,即使在序列化完成之后修改了类,导致类重新编译,则原来的数据也能正常反序列化,只是新增的字段值是默认值而已。
因此,建议显式声明 serialVersionUID 。
自定义类的各个属性如果也要序列化的话
- 对于基本数据类型,默认是可序列化的
- 对于引用数据类型,要求实现 Serializable 接口,否则报错
NotSerializableException
如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰静态(
static
)变量的值不会序列化,因为静态变量的值不属于某个对象
# 2. Java 有些类中为什么需要实现 Serializable 接口?(阿 * 校招)
便于此类的对象实现序列化操作。
当一个类需要支持序列化时,它必须实现 Serializable
接口。序列化是指将对象转换为字节序列的过程,以便将其存储在文件中或通过网络传输。只有实现了 Serializable
接口的类才能被序列化。
Serializable
接口是一个标记接口,没有任何方法。它只是用来标识一个类是否支持序列化。当我们试图序列化一个未实现 Serializable
接口的对象时,会抛出 NotSerializableException
异常。
许多 Java 类都实现了 Serializable
接口,以便支持序列化。例如,Java 集合框架中的许多类(如 ArrayList
, HashMap
等)都实现了这个接口。