Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 54 additions & 28 deletions notes/Java IO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@
* [一、概览](#一概览)
* [二、磁盘操作](#二磁盘操作)
* [三、字节操作](#三字节操作)
* [实现文件复制](#实现文件复制)
* [装饰者模式](#装饰者模式)
* [四、字符操作](#四字符操作)
* [编码与解码](#编码与解码)
* [String](#string)
* [Reader 与 Writer](#reader-与-writer)
* [实现逐行输出文本文件的内容](#实现逐行输出文本文件的内容)
* [五、对象操作](#五对象操作)
* [序列化](#序列化)
* [Serializable](#serializable)
* [transient](#transient)
* [六、网络操作](#六网络操作)
* [InetAddress](#inetaddress)
* [URL](#url)
Expand Down Expand Up @@ -37,7 +46,7 @@ Java 的 I/O 大概可以分成以下几类:

File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。

递归地输出一个目录下所有文件
递归地列出一个目录下所有文件

```java
public static void listAllFiles(File dir) {
Expand All @@ -56,7 +65,7 @@ public static void listAllFiles(File dir) {

# 三、字节操作

使用字节流操作进行文件复制:
## 实现文件复制

```java
public static void copyFile(String src, String dist) throws IOException {
Expand All @@ -77,13 +86,15 @@ public static void copyFile(String src, String dist) throws IOException {
}
```

<div align="center"> <img src="../pics//DP-Decorator-java.io.png" width="500"/> </div><br>
## 装饰者模式

Java I/O 使用了装饰者模式来实现。以 InputStream 为例,

- InputStream 是抽象组件;
- FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作;
- FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。
- FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。

<div align="center"> <img src="../pics//DP-Decorator-java.io.png" width="500"/> </div><br>

实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。

Expand All @@ -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();
}
```
## 编码与解码

编码就是把字符转换为字节,而解码是把字节重新组合成字符。

Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -184,11 +210,11 @@ private static class A implements Serializable {
}
```

不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。
## transient

transient 关键字可以使一些属性不会被序列化。

ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。

```java
private transient Object[] elementData;
Expand Down Expand Up @@ -249,8 +275,8 @@ public static void main(String[] args) throws IOException {

## Datagram

- DatagramPacket:数据包类
- DatagramSocket:通信类
- DatagramPacket:数据包类

# 七、NIO

Expand Down
Loading