diff --git a/notes/Java IO.md b/notes/Java IO.md index 1e3aedcd..a14ea902 100644 --- a/notes/Java IO.md +++ b/notes/Java IO.md @@ -2,8 +2,17 @@ * [一、概览](#一概览) * [二、磁盘操作](#二磁盘操作) * [三、字节操作](#三字节操作) + * [实现文件复制](#实现文件复制) + * [装饰者模式](#装饰者模式) * [四、字符操作](#四字符操作) + * [编码与解码](#编码与解码) + * [String](#string) + * [Reader 与 Writer](#reader-与-writer) + * [实现逐行输出文本文件的内容](#实现逐行输出文本文件的内容) * [五、对象操作](#五对象操作) + * [序列化](#序列化) + * [Serializable](#serializable) + * [transient](#transient) * [六、网络操作](#六网络操作) * [InetAddress](#inetaddress) * [URL](#url) @@ -37,7 +46,7 @@ Java 的 I/O 大概可以分成以下几类: File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。 -递归地输出一个目录下所有文件: +递归地列出一个目录下所有文件: ```java public static void listAllFiles(File dir) { @@ -56,7 +65,7 @@ public static void listAllFiles(File dir) { # 三、字节操作 -使用字节流操作进行文件复制: +## 实现文件复制 ```java public static void copyFile(String src, String dist) throws IOException { @@ -77,13 +86,15 @@ public static void copyFile(String src, String dist) throws IOException { } ``` -

+## 装饰者模式 Java I/O 使用了装饰者模式来实现。以 InputStream 为例, - InputStream 是抽象组件; - FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作; -- FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 +- FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 + +

实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 @@ -96,27 +107,7 @@ DataInputStream 装饰者提供了对更多数据类型进行输入的操作, # 四、字符操作 -不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。 - -- InputStreamReader 实现从字节流解码成字符流; -- OutputStreamWriter 实现字符流编码成为字节流。 - -逐行输出文本文件的内容: - -```java -public static void readFileContent(String filePath) throws IOException { - FileReader fileReader = new FileReader(filePath); - BufferedReader bufferedReader = new BufferedReader(fileReader); - String line; - while ((line = bufferedReader.readLine()) != null) { - System.out.println(line); - } - // 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 - // 在调用 BufferedReader 的 close() 方法时会去调用 fileReader 的 close() 方法 - // 因此只要一个 close() 调用即可 - bufferedReader.close(); -} -``` +## 编码与解码 编码就是把字符转换为字节,而解码是把字节重新组合成字符。 @@ -130,6 +121,8 @@ UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF- Java 使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。 +## String + String 可以看成一个字符序列,可以指定一个编码方式将它编码为字节序列,也可以指定一个编码方式将一个字节序列解码为 String。 ```java @@ -145,13 +138,46 @@ System.out.println(str2); byte[] bytes = str1.getBytes(); ``` +## Reader 与 Writer + +不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。 + +- InputStreamReader 实现从字节流解码成字符流; +- OutputStreamWriter 实现字符流编码成为字节流。 + +## 实现逐行输出文本文件的内容 + +```java +public static void readFileContent(String filePath) throws IOException { + + FileReader fileReader = new FileReader(filePath); + BufferedReader bufferedReader = new BufferedReader(fileReader); + + String line; + while ((line = bufferedReader.readLine()) != null) { + System.out.println(line); + } + + // 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 + // 在调用 BufferedReader 的 close() 方法时会去调用 Reader 的 close() 方法 + // 因此只要一个 close() 调用即可 + bufferedReader.close(); +} +``` + # 五、对象操作 +## 序列化 + 序列化就是将一个对象转换成字节序列,方便存储和传输。 - 序列化:ObjectOutputStream.writeObject() - 反序列化:ObjectInputStream.readObject() +不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。 + +## Serializable + 序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现,但是如果不去实现它的话而进行序列化,会抛出异常。 ```java @@ -184,11 +210,11 @@ private static class A implements Serializable { } ``` -不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。 +## transient transient 关键字可以使一些属性不会被序列化。 -ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。 +ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。 ```java private transient Object[] elementData; @@ -249,8 +275,8 @@ public static void main(String[] args) throws IOException { ## Datagram -- DatagramPacket:数据包类 - DatagramSocket:通信类 +- DatagramPacket:数据包类 # 七、NIO