文章目錄
流的概念
按流的方向分類:
1. 輸入流:資料流向是資料來源到程式(以InputStream、Reader結尾的流)。
2. 輸出流:資料流向是程式到目的地(以OutPutStream、Writer結尾的流)。
按處理的資料單元分類:
- 位元組流:以位元組為單位獲取資料,命名上以
Stream
結尾的流一般是位元組流,如FileInputStream、FileOutputStream
。 - 字元流:以字元為單位獲取資料,命名上以
Reader/Writer
結尾的流一般是字元流,如FileReader、FileWriter
。
按處理物件不同分類:
- 節點流:可以直接從資料來源或目的地讀寫資料,如
FileInputStream、FileReader、DataInputStream
等。 - 處理流:不直接連線到資料來源或目的地,是”處理流的流”。通過對其他流的處理提高程式的效能,如
BufferedInputStream、BufferedReader
等。處理流也叫包裝流。
- 節點流處於IO操作的第一線,所有操作必須通過它們進行
- 處理流可以對節點流進行包裝,提高效能或提高程式的靈活性
IO 流類體系
InputStream / OutputStream
表示位元組輸入輸出流的所有類的父類。
子類:
FileInputStream / FileOutputStream
: 節點流,以位元組為單位 【直接】操作『檔案』ByteArrayInputStream / ByteArrayOutputStream
:節點流,…操作『位元組陣列物件』ObjectInputStream / ObjectOutputStream
:處理流,…『物件』DataInputStream / DataOutputStream
:處理流,…『基本資料型別與字串型別』
Reader / Writer
表示用於讀取/寫入的字元流抽象類,資料單位為字元.
子類:
FileReader / FileWriter
:節點流,以字元為單位直接操作『文字檔案』(只能讀寫文字檔案)BufferedReader / BufferedWriter
:處理流,將Reader/Writer物件進行包裝,增加快取功能,提升讀寫效率BufferedInputStream / BufferedOutputStream
:處理流,將InputStream/OutputStream物件包裝,…InputStreamReader / OutputStreamWriter
:處理流,將位元組流物件轉化為字元流物件PrintStream
:處理流,將OutputStream進行包裝,可以方便地輸出字元
檔案位元組流
fis = new FileInputStream("a.txt");
while ((tmp = fis.read()) != -1)
sb.append((char) tmp);
...
FileOutputStream fos = null;
String string = "i am writing.";
try {
fos = new FileOutputStream("a.txt", true); // true表示追加檔案末尾
byte[] bytes = string.getBytes();
fos.write(bytes); // 直接將字元陣列寫入檔案
檔案字元流
位元組流不能很好的處理Unicode字元,經常會出現“亂碼”現象,一般使用字元流處理文字檔案。
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("a.txt");
fw = new FileWriter("./c.txt");
char[] buffer = new char[1024]; // 快取陣列
int len = 0;
while ((len = fr.read(buffer)) != -1)
fw.write(buffer, 0, len);
} catch (Exception e) {...
緩衝位元組流
Java緩衝流本身並不具有IO流的讀取與寫入功能,只是在別的流(節點流或其他處理流)上加上緩衝功能提高效率,就像是把別的流包裝起來一樣,因此緩衝流是一種處理流(包裝流)。
因為緩衝流是先將資料快取起來,然後當快取區存滿後或者手動重新整理時再一次性的讀取到程式或寫入目的地。
// 使用緩衝流實現檔案的高效率複製
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
String srcPath = "/home/cenjw/Downloads/ideaIU-2019.3.5.tar.gz";
String destPath = "/home/cenjw/idea-package.tar.gz";
try {
fis = new FileInputStream(srcPath);
fos = new FileOutputStream(destPath);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
int t = 0;
long start = System.currentTimeMillis();
while ((t = bis.read()) != -1)
bos.write(t);
long end = System.currentTimeMillis();
System.out.println(end - start);
緩衝字元流
FileReader fr = null;
BufferedReader br = null;
FileWriter fw = null;
BufferedWriter bw = null;
try {
fr = new FileReader("a.txt");
fw = new FileWriter("./d.txt");
br = new BufferedReader(fr);
bw = new BufferedWriter(fw);
String tmpStr = "";
while ((tmpStr = br.readLine()) != null) {
bw.write(tmpStr);
bw.newLine();
}
-
readLine()方法是BufferedReader特有的方法,可以對文字檔案進行更加方便的讀取操作。
-
寫入一行後要記得使用newLine()方法換行。
位元組陣列流
ByteArrayInputStream和ByteArrayOutputStream經常用在需要流和陣列之間轉化的情況!
即,FileInputStream是把檔案當做資料來源。ByteArrayInputStream則是把記憶體中的”某個位元組陣列物件”當做資料來源。
// 字串轉變為位元組陣列
byte[] bytes = "hello world".getBytes();
ByteArrayInputStream bais = null;
StringBuilder sb = new StringBuilder();
try {
int tmp = 0;
int cnt = 0;
bais = new ByteArrayInputStream(bytes);
while ((tmp = bais.read()) != -1) {
sb.append((char) tmp);
cnt++;
}
資料流
資料流將**“基本資料型別與字串型別”作為資料來源,從而允許程式以與機器無關的方式從底層輸入輸出流中操作Java基本資料型別與字串型別。
DataInputStream
和DataOutputStream
是處理流,可以對其他節點流或處理流進行包裝,增加一些更靈活、更高效的功能。
轉換流
InputStreamReader/OutputStreamWriter
用來實現將位元組流轉化成字元流。
System.in
是位元組流物件,代表鍵盤的輸入,按行接收使用者的輸入時,必須用到緩衝字元流 BufferedReader
特有的方法readLine()
,但在建立BufferedReader
的構造方法的引數必須是一個Reader物件,因此需要用到轉換流InputStreamReader。
System.out
也是位元組流物件,代表輸出到顯示器,按行讀取使用者的輸入後,並且要將讀取的一行字串直接顯示到控制檯,就需要用到字元流的 write(String str)
方法,所以我們要使用OutputStreamWriter
將位元組流轉化為字元流。
// 建立字元輸入輸出流
BufferedReader br = null;
BufferedWriter bw = null;
try {
// 使用轉換流將 位元組流轉為字元流
br = new BufferedReader(new InputStreamReader(System.in));
bw = new BufferedWriter(new OutputStreamWriter(System.out));
String str = br.readLine();
while (! "exit".equals(str)) {
bw.write(str);
bw.newLine();
bw.flush();
str = br.readLine();
}
...
序列化與反序列化
把Java物件轉換為位元組序列的過程稱為物件的序列化。把位元組序列恢復為Java物件的過程稱為物件的反序列化。
物件序列化的作用:
- 持久化: 把物件的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中,比如:休眠的實現。以後伺服器session管理,hibernate將物件持久化實現。
- 網路通訊:在網路上傳送物件的位元組序列。比如:伺服器之間的資料通訊、物件傳遞。
序列化涉及的類和介面
ObjectOutputStream
代表物件輸出流,它的 writeObject(Object obj)
方法可對引數指定的 obj
物件進行序列化,把得到的位元組序列寫到一個目標輸出流中。ObjectInputStream
代表物件輸入流,它的readObject()
方法從一個源輸入流中讀取位元組序列,再把它們反序列化為一個物件,並將其返回。
只有實現了
Serializable
介面的類的物件才能被序列化。 Serializable介面是一個空介面,只起到標記作用。
FileOutputStream fos = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
FileInputStream fis = null;
try {
Person person = new Person(22, true, "jayvee");
// 通過ObjectOutputStream將Person物件的資料寫入到檔案,即序列化
fos = new FileOutputStream("a.txt");
oos = new ObjectOutputStream(fos);
oos.writeObject(person);
oos.flush();
// 反序列化
fis = new FileInputStream("a.txt");
ois = new ObjectInputStream(fis);
Person p = (Person) ois.readObject();
System.out.println(p);
...
// 實現Serializable介面後,Person物件才能被序列化
class Person implements Serializable {
// 新增序列化ID,它決定著是否能夠成功反序列化!
private static final long serialVersionUID = 1L;
裝飾器模式
筆記參考:https://www.sxt.cn/Java_jQuery_in_action/ten-javaio-streamclass.html
IO流體系圖:https://blog.csdn.net/u010145219/article/details/89792877