文章目錄
最後還是變成了標題黨,哈哈。第一次起這麼騷的標題。勿噴!
來,和我一起復習IO流這塊內容吧!這塊知識點其實算簡單,但是嚐嚐因爲太多而被繞暈了!這次目標是清清楚楚,清爽的八大鳥!
先上個圖:
這篇博客主要就是圍繞上圖。
1.字節流(InputStream、OutputStream)
這是最基礎的子一對輸入輸出流。屬於字節流。用法如下:
public class IsTest {
public static void main(String[] args) throws Exception{
File file = new File("C:/Users/Administrator/Desktop/123.txt");
byte[] bytes1 = new byte[20];
byte[] bytes2= new byte[]{'a','b','c'};
/*if (file.exists()){
InputStream inputStream = new FileInputStream(file);
System.out.println(inputStream.read(bytes1));
}*/
if (file.exists()){
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(bytes2);
}
}
}
2.字符流(Reader、Writer)
這一對相較於上一對的主要區別就在於每次讀取或者輸出的單位更大。字符肯定比字節大,所以也理所當然的效率更高!UTF-8環境下,1個英文或者符號對應1個字節,1箇中文字對應3個字節。
Reader:
public class Test2 {
public static void main(String[] args) throws Exception {
Reader reader = new FileReader("C:/Users/Administrator/Desktop/123.txt");
char[] chars = new char[8];
//offset
int length = reader.read(chars,2,3);
System.out.println("數據流的長度是"+length);
System.out.println("遍歷數組");
for (char aChar : chars) {
System.out.println(aChar);
}
}
Writer:
public class Test {
public static void main(String[] args) throws Exception {
Writer writer = new FileWriter("/Users/southwind/Desktop/copy.txt");
//writer.write("你好");
// char[] chars = {'你','好','世','界'};
// writer.write(chars,2,2);
String str = "Hello World,你好世界";
writer.write(str,10,6);
writer.flush();
writer.close();
}
}
字符流的基本操作就是這樣了,方法很多不一一寫了。
值得注意的是,字符流還是在字節流的基礎上操作的,並且我們已經在上述代碼中和處理流打上交道了。看一下FileWriter或FileReader源碼便一目瞭然:
下面要說的處理流InputStreamReader、OutputStreamWriter已經出現了。
同樣看字節流代碼中FileInputStream的源碼你會發現,這裏的不同就在於FileReader不是直接在繼承自Reader的,之間多了個處理流。而這個處理流,顧名思義,就是一個有把字節流變成字符流作用的處理流。
3.處理流InputStreamReader、OutputStreamWriter
2中其實已經提到了處理流,看源碼也不難發現,處理流是在操作字節流。從這裏也能直觀的看出節點流和處理流的區別:就是處理流是作用在流上的,不能直接作用在文件上。而像字節字符流這些節點流是可以直接作用在文件上的。
測試代碼:
InputStreamReader:
public class Test {
public static void main(String[] args) throws Exception {
//基礎管道
InputStream inputStream = new FileInputStream("C:/Users/Administrator/Desktop/123.txt");
//處理流
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
char[] chars = new char[1024];
int length = inputStreamReader.read(chars);
inputStreamReader.close();
String result = new String(chars,0,length);
System.out.println(result);
}
}
OutputStreamWriter:
public class Test2 {
public static void main(String[] args) throws Exception {
String str = "你好 世界";
OutputStream outputStream = new FileOutputStream("/Users/southwind/Desktop/copy.txt");
OutputStreamWriter writer = new OutputStreamWriter(outputStream);
writer.write(str,2,1);
//writer.flush();
writer.close();
}
}
4.上面三種流之間的關係
感覺這張圖還算清晰吧。他們之間的關係這麼記,我個人是覺得印象深刻!
5.緩衝流
⽆論是字節流還是字符流,使⽤的時候都會頻繁訪問硬盤,對硬盤是⼀種損傷,同時效率不⾼,緩衝流在這種背景下應運而生。
緩衝流⾃帶緩衝區,可以⼀次性從硬盤中讀取部分數據存⼊緩衝區,再寫⼊內存,這樣就可以有效減少對硬盤的直接訪問並且提高效率。
(1)字節緩衝流
字節輸⼊緩衝流
public class Test {
public static void main(String[] args) throws Exception {
//1、創建節點流
InputStream inputStream = new FileInputStream("/Users/southwind/Desktop/test.txt");
//2、創建緩衝流
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
// int temp = 0;
// while ((temp = bufferedInputStream.read())!=-1){
// System.out.println(temp);
// }
byte[] bytes = new byte[1024];
int length = bufferedInputStream.read(bytes,10,10);
System.out.println(length);
for (byte aByte : bytes) {
System.out.println(aByte);
}
bufferedInputStream.close();
inputStream.close();
}
}
就字節流來說,字節緩衝流的效果並不明顯。但是字符緩衝流就能明明顯的看出區別了。
(2)字符緩衝流
字符輸入緩衝流
public class Test2 {
public static void main(String[] args) throws Exception {
//1、創建字符流(節點流)
Reader reader = new FileReader("/Users/southwind/Desktop/test.txt");
//2、創建緩衝流(處理流)
BufferedReader bufferedReader = new BufferedReader(reader);
String str = null;
int num = 0;
System.out.println("***start***");
while ((str = bufferedReader.readLine())!=null){
System.out.println(str);
num++;
}
System.out.println("***end***,共讀取了"+num+"次");
bufferedReader.close();
reader.close();
}
}
字符輸入緩衝流的readline方法的效果就很明顯,一次性就讀取了一整行。
字符輸出緩衝流
public class Test2 {
public static void main(String[] args) throws Exception {
Writer writer = new FileWriter("/Users/southwind/Desktop/test2.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
// String str = "由於在開發語言時尚且不存在運行字節碼的硬件平臺,所以爲了在開發時可以對這種語言進行實驗研究,他們就在已有的硬件和軟件平臺基礎上,按照自己所指定的規範,用軟件建設了一個運行平臺,整個系統除了比C++更加簡單之外,沒有什麼大的區別。";
// bufferedWriter.write(str,5,10);
char[] chars = {'J','a','v','a'};
// bufferedWriter.write(chars,2,1);
bufferedWriter.write(22902);
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
}
}
6.序列化反序列化
序列化就是將內存中的對象輸出到硬盤⽂件中保存。
反序列化就是相反的操作,從⽂件中讀取數據並還原成內存中的對
象。
上面都還麼有涉及到實體類對象,讀寫實體類對象時我們就要用到序列化技術了。
(1)序列化
public class Test {
public static void main(String[] args) throws Exception {
User user = new User(1,"張三",22);
OutputStream outputStream = new FileOutputStream("/Users/southwind/Desktop/obj.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(user);
objectOutputStream.flush();
objectOutputStream.close();
outputStream.close();
}
}
(2)反序列化
public class Test2 {
public static void main(String[] args) throws Exception {
InputStream inputStream = new FileInputStream("/Users/southwind/Desktop/obj.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
User user = (User) objectInputStream.readObject();
System.out.println(user);
objectInputStream.close();
inputStream.close();
}
}
User
package com.southwind.entity;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public User(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
}
7.拓展
在看源碼中常常看見一些藉口,總結一下他們的作用。
Appendable 接⼝可以將 char 類型的數據讀⼊到數據緩衝區
Readable可以將數據以字符的形式讀⼊到緩衝區。
Flushable接口只有輸出流有。Flushable 是可刷新數據的目標地。調用 flush 方法將所有已緩衝輸出寫入底層流。 這是JDK的定義。我自己的理解就是,防止輸出有丟失,強制把所有數據都刷新到目標地。
經驗
- 文本類型(Excel、word、txt…)一般用字符流操作(當然也可以用字節流)
- 而非文本類型(圖片,音頻,視頻…)不能用字符流操作,只能用字節操作。