流(黑馬)
字節輸出流 (OutputStream)
java.io.OutputStream
最頂層的抽象類,定義了所有字節流的所有方法。
定義了子類共性的方法。 close(),flush(),write(**)*3
文件字節輸出流(FileOutputStream)
繼承了 OutputStream
將內容寫入到硬盤中
- 構造方法:
- FileOutputStream(String name): 創建一個具有指定名稱的文件中寫入輸出文件流。
- FileOutputStream(File file): 創建一個像指定File 對象表示文件中寫入數據的文件輸出流
- 參數:
- name:目的地是文件的路徑
- file:目的地是一個文件
- 作用:
- 創建一個FileOutputStream 對象
- 根據傳遞的參數,創建一個空文件
- 會把輸出流對象指向創建好的文件
寫入數據的原理:
由內存到硬盤: java程序 --> JVM --> OS --> 調用寫數據的方法(OS) --> 寫入文件
字節輸出流寫入步驟:
1. 創建一個對象(傳遞寫入數據的路徑)
2. 調用對象中的write 方法,寫入到文件中
3. 釋放資源 (提高程序的效率)
// 1. 創建一個對象(傳遞寫入數據的路徑) (相對路徑)
FileOutputStream fos = new FileOutputStream("09\\a.txt");
//2. 調用對象中的write 方法,寫入到文件中
fos.write(97); //十進制 -> 二進制 (1100001)
//3. 釋放資源 (提高程序的效率)
fos.close();
寫入:
-
public void write(int a):
記事本打開文件時查詢編碼表 字節 -> 字符
0 - 127 : ASCII 表
其他:系統默認編碼表(中文系統GBK表) -
public void write(byte[] b):
- 如果寫入第一個字節是整數(0-127) ,那麼查詢 ASCII表。
- 如果寫入的是負數 ,第一個字節會和第二個字節組成一箇中文顯示,查詢系統默認表。
-
public void write(byte [] b,int off, int len):
- 部分寫入
- off 開始,len:長度
數據的追加寫和換行寫
追加/續寫:
FileOutputStream(String name ,boolean append)
FileOutputStream(File file ,boolean append)
- append : 追加寫開關 false:創建新文件,覆蓋源文件
換行:
windows : \r\n
linux : \n
mac:: \r
字節輸入流(InputStream)
也是抽象類,定義了輸入流子類共性的方法。
read() * 2 , close()
文件字節輸入流 (FileInputStream)
- 構造方法:
- FileInputStream(String name)
- FileInputStream(File file)
- 作用:
- 創建一個FileInputStream 對象
- 把對象指向要讀取的文件
- 讀取數據的原理: 硬盤 -> 內存
java程序 -> JVM -> OS -> 讀取方法 ->讀取文件
- 步驟:
- 創建一個FileInputStream 對象,綁定數據源
- 調用read方法讀取文件
- 釋放資源
FileInputStream fin = new FileInputStream("src/liu/a.txt");
int x;
//讀取一個字節
x = fin.read();
System.out.println(x);
//文件結束 返回 -1
x = fin.read();
System.out.println(x);
/**
* 1. fin.read() : 讀取一個字符
* 2. x = fin.read() 賦值
* 3. 判斷 x 是否等於 -1
*/
while((x = fin.read()) != -1){
System.out.println((char)x);
}
fin.close();
多字節讀取
- int read(byte [] b)
- 參數:byte[] 存放的數組(字節) 一般大小爲 或者1024的整數倍
- 返回值:int 返回讀取的字節數
- String 構造方法
- String(byte [] b): 字符數組轉換成 字符串
- String (byte [] b,int off,int len): 一部分轉化
/**
* 多字節讀取
* byte 有時恰好漢字沒有亂碼
*/
byte [] b = new byte[4];
fin = new FileInputStream("src/liu/b.txt");
while ((x = fin.read(b))!= -1){
System.out.println(new String(b,0,x));
}
練習:文件複製
- 創建一個輸入流對象,綁定讀取數據源
- 創建一個輸出流對象,綁定目的地
- 使用文件輸入流 ,讀取文件
- 使用文件輸出流 ,寫入文件
- 釋放資源(先關閉寫的,後關閉讀的)
package liu;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author LiYang
* @Project Name: bilibili
* @Package Name: liu
* Created by MacBook Air on 2020/02/25.
* Copyright © 2020 LiYang. All rights reserved.
* 字節流練習
*/
public class CopyFile {
public static void main(String[] args) {
try{
FileInputStream fin = new FileInputStream("src/liu/2.jpeg");
FileOutputStream fout = new FileOutputStream("src/1.jpeg",true);
byte [] b = new byte[1024];
while (fin.read(b) != -1){
fout.write(b);
}
fout.close();
fin.close();
} catch (IOException e){
System.out.println(e.getMessage());
}
}
}
字符流
java.io.Reader
解決中文讀取可能亂碼的問題
1箇中文:
GBK: 佔2個字節
UTF-8 :佔3個字節
字符輸入流(Reader)
抽象類
共性成員的方法: close(),read()
層次:
-
Reader
- InputStreamReader
- FileReader
FileReader FileInputStream 讀取字節 -> FileReader轉換成字符
- InputStreamReader
InputStream:文件字符輸入流
- 構造方法:
- FileReader(String filename)
- FileReader(File file)
- 參數:
- filename:文件路徑
- file:一個文件
- 作用:
- 創建一個FileReader對象
- 會把FileReader對象指向文件
- 使用步驟:
- 創建一個對象,並綁定數據源
- 使用對象讀取文件
- 釋放資源
字符輸出流 (Writer)
java.io.Writer;
flush ():刷新
所有字符輸出流的頂層類,是一個抽象類
層次
- java.io.Writer
- java.io.OutputStreamWriter
- java.io.FileWriter
- java.io.OutputStreamWriter
java.io.FileWriter extends OutputStreamWriter extends Writer
FileWriter: 文件字符輸出流
作用: 將內存中的內容輸入到磁盤中
- 構造方法:
- FileWriter(String fielname)
- FileWriter(File file)
- 參數
- …
- 作用
- 創建一個對象
- 根據傳遞的參數,創建一個文件
- 對象指向創建的文件
- 使用步驟
- 創建一個對象,構造方法中綁定要寫入的數據目的地
- 使用FileWriter中的Write,把數據寫入到緩衝區中(字符轉換成字節的過程)
- 使用FileWriter的方法,flush() ,把內存中數據刷新,刷新到文件中
- 釋放資源(先將內存緩衝區中的數據刷新到文件中)
flush 與 close 的區別:
- flush:刷新緩衝區,流對象可以繼續使用
- close:先刷新緩衝區,關閉流對象
續寫和換行寫
FileWriter(String filename , boolean append);
FileWriter(File file , boolean append);
屬性集
-
java.lang.Object
- java.util.Dictionary<K,V>
- java.util.Hashtable<Object,Object>
- java.util.Properties
- java.util.Hashtable<Object,Object>
是一個持久的屬性集。Properties可以保存在流或從流中加載
是唯一一個與IO流相結合的集合 - java.util.Dictionary<K,V>
-
store() : 把集合中的臨時數據,持久化寫入到硬盤中存儲
- public void store(OutputStream out,String comments)
- 不能寫入中文
- public void store(Writer writer,String comments)
- 可以寫入中文
- comments : 註釋文件的作用,不能使用中文,Unicode編碼,通常爲空字符串
- public void store(OutputStream out,String comments)
-
使用步驟
- 創建Properties集合對象,添加數據
- 創建字節/字符輸出流,構造方法綁定文件
- 使用Properties集合的方法 store,持久化寫入硬盤
- 釋放資源
-
load(): 把硬盤中保存的鍵值對,讀取到內存中
- public void load(InputStream inStream)
- 字節輸入流 不能讀取含有中文的鍵值對
- public void load(Reader reader)
- 字符輸入流 能讀中文
- public void load(InputStream inStream)
-
步驟
- 創建Properties 集合對象
- 使用Properties 集合對象中的方法Load讀取文件的鍵值對
- 遍歷Properties 集合
-
注意:
- 鍵值對文件中,鍵與值之間默認連接方式爲 = 或者 空格 或者其他符號
- 鍵值對文件中,可以使用 # 進行註釋
- 鍵值對文件中,鍵與值默認都是字符串
是雙列結合,Key和Value默認都是字符串
緩衝
字節緩衝流
BufferedOutputStream :字節緩衝輸出流
-
構造方法:
- public BufferedOutputStream(OutputStream out)
- public BufferedOutputStream(OutputStream out,int size)
-
參數:
- out:字節輸出流
- size: 自定緩衝流內部緩衝區的大小
-
作用:
- 給OutputStream 創建一個緩衝區,提高寫入效率
-
步驟
- 創建一個字節緩衝流,在構造方法中綁定目的地
- 創建緩衝流對象,構造方法中傳遞字節緩衝流,提高效率
- 使用write方法,把數據寫入到內部緩衝區
- 使用flush方法,把數據刷新到文件中
- 釋放資源
BufferedInputStream : 字節緩衝輸入流
-
構造方法:
- public BufferedInputStream(InputStream in)
- public BufferedInputStream(InputStream in,int size)
-
參數
- in : 字節輸出流
- size : 緩衝區大小
-
使用步驟
- 創建FileInputStream對象
- 創建BufferedInputStream對象,構造方法傳遞 FileInputStream對象
- 使用Buffered的read,讀取文件
- 釋放資源
字符緩衝流
-
字符緩衝輸出流
- BufferedWriter extends Writer
-
構造方法:
- public BufferedWriter(Writer out)
- public BufferedWriter(Writer out,int sz)
-
參數
- out : 字符輸出流
- sz : 指定緩衝區的大小
-
方法
- public void newLine() : 寫一個行分隔符
-
步驟
- 創建字符緩衝輸出流對象,構造方法中傳遞字符輸出流
- 調用緩衝流中的write方法啊,寫入緩衝區
- 調用flush方法,刷新到磁盤文件
- 釋放資源
-
字符緩衝輸入流
- BufferedReader
-
構造函數
- BufferedReader(Reader in)
- BufferedReader(Reader in, int sz)
-
參數
- in : 字符輸入流
- sz : 大小
-
方法
- readLine() : 讀一行
練習:文本排序
分析:
- 創建一個HashMap 集合對象,可以存儲每行的文本序號(1,2,3…);value 存儲每行的內容
- 創建字符緩衝輸入流對象,構造方法綁定字符輸入流
- 創建字符緩衝輸出流對象,構造方法綁定字符輸出流
- 使用字符緩衝輸入流中的readline()方法,逐行讀取文本內容
- 對讀取到的文本進行切割,獲取行號和文本內容,並存儲到HashMap集合中(kay 自動排序)
- 遍歷HashMap 獲取鍵值
- 使用字符緩衝輸出流中的writeLine() 方法,吸入文本
- 釋放資源
轉換流
- 編碼: 字符 -> 字節
- 解碼: 字節 -> 字符
- 字符集(編碼表) Charset
- ASCII
- GBK (國標) 雙字節編碼
- Unicode 萬國碼 (UTF8,16,32)
OutputStreamWriter
- 繼承了 Writer
- 是字符流通向字節流的橋樑,可是指定 charset 將要寫入流中的字符編碼成字節 (編碼的過程)
- 構造方法
- public OutputStreamWriter(OutputStream out)
- public OutputStreamWriter(OutputStream out,String charsetName)
- 參數
- out : 字節輸出流,可以用來寫轉換之後的字節到文件中
- charsetName : 編碼表的名稱 ,不區分大小寫 utf-8 默認使用 utf-8
- 步驟
- 創建OutputStreamWriter 對象,構造方法中傳遞字節輸出流,指定編碼表名稱
- 使用OutputStreamWriter 對象中的 Writer 方法,把字符組轉換成自己儲存到緩衝區
- 使用OutputStreamWriter 對象中的 flush 方法,把內存緩衝區中的字節刷新到文件中去
- 釋放資源
InputStreamReader
- 繼承了 reader
- 構造方法
- public InputStreamReader(InputStream in)
- public InputStreamReader(InputStream in,String charsetName)
- 參數
- in : 字節輸入流,可以用來讀文件中字節
- charsetName : 編碼表的名稱 ,不區分大小寫 utf-8 默認使用 utf-8
- 步驟
- 創建對象,傳參
- 使用read() 方法讀取文件
- 釋放資源
- 注意
- 指定編碼與文件的編碼相同,否則會發生亂碼
練習:文件轉碼
- 創建InputStreamReader對象,傳遞字節輸入流
- 創建OutputStreamWriter對象,傳遞字節輸出流
- 使用InputStreamReader 對象方法啊read 讀文件
- 使用OutputStreamWriter 方法Write 寫文件
- 釋放資源
對象流
對象序列化
- 對象中不僅僅包含字符,使用字節流
- ObjectOutputStream
- 方法:
- public final void writeObject(Object obj)
- throws IOException
- public final void writeObject(Object obj)
- 步驟
- 創建ObjectOutputStream對象,傳遞輸出流
- 使用ObjectOutputStream的 writeObject方法,把對象寫入
- 釋放資源
對象反序列化
- ObjectInputStream
- public final Object readObject()
- throws IOException,ClassNotFoundException
- public final Object readObject()
- NotSerializableException 沒有序列化異常
- :是一個接口,須有實該接口才能使用序列化功能。
- 標記性接口,要進行序列化和反序列化的類必須實現該接口
- 當序列化時需要檢查該標記,沒有拋出異常
- 步驟
- 創建ObjectInputStream對象,傳遞輸入流
- 使用ObjectInputStream的 readObject方法,把對象讀出
- 釋放資源
transient 關鍵字
- 瞬態關鍵字
- static: 靜態關鍵字
- 靜態優先於非靜態加載到內存中 (靜態優先於對象進入到內存)
- 被static 修飾的成員變量不能被序列化
/**
* 序列化標記型接口
*/
class Point implements Serializable{
private int x;
private static int y;
transient int z;
public Point(int x, int y,int z) {
this.x = x;
Point.y = y;
this.z = z;
}
public void print(){
System.out.println("(" +x+","+y+","+z+")");
}
}
- 對於y 是靜態成員,需要用類名直接調用,不能夠序列化寫入到文件中
- 對於z 是瞬態成員,需要用this直接調用,屬於單個對象,也不能被序列化寫入到文件中
InvalidClassException
- 當類文件被修改時,.class 文件的序列號(serialVersionUID)會被修改,當直接進行反序列化時,文件中的序列號會找 .class 文件
找不到:會拋出 InvalidClassException 異常 - 解決方法:
- 手動給類添加 序列號
- The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender’s class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named “serialVersionUID”
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
練習:序列化集合
ArrayListTest.java
ArrayListTest1.java
- 當我們想在文件中保存多個對象的時候
可以把多個對象儲存到一個集合中
對集合進行序列化和反序列化 - 分析:
- 定義一個存儲Point對象的ArrayList集合
- 往List中進行存儲Point對象
- 創建一個序列化流 ObjectOutputStream 對象
- 寫入(WriteObject)
- 創建反序列化對象
- 讀出來 (ReadObject)
- 把Object類型的集合轉化成 ArrayList集合
- 遍歷
- 釋放資源
打印流
- PrintStream
- 只負責輸出,不負責輸入
- 永遠不拋出 IOException
- 特有方法print(),println()
- 構造方法
- public PrintStream(File file)
- public PrintStream(OutputStream out)
- public PrintStream(String fileName)
- extends OutpuStream
- 注意
- 使用父類的 write方法,會查詢自己編碼表 97 -> a
- 使用自己特有的 print/println ,會輸出原樣 97 -> 97
- 可以改變打印的方向
- System.setOut(PrintStream out)
- 示例:
System.out.println("我是控制檯輸出!");
//PrintStream ps = new PrintStream("src/liu/c.txt");
PrintStream ps = new PrintStream(new FileOutputStream("src/liu/d.txt"));
//修改打印的位置
System.setOut(ps);
System.out.println("文件打印!");
FileOutputStream 會創建文件,但是依然需要拋出 FileNotFoundException
流(mooc)
數據流
數據流是指應用程序與對象進行數據交換時的數據傳輸過程。
字節流是按照字節讀/寫二進制數據。
InputStream和OutputStream類是處理以8字節爲基本單位的字節流,讀寫以字節爲單位進行。
Object:InputStream、OutputStream(字節流);File(文件);Reader、Writer(字符流);RandomAccessFile(隨機處理文件)
節點流
節點流是直接與特定數據源相連的流,提供了訪問該數據源的基本操作。
過濾器流
1. 爲了提高數據的處理速度
2. 能對如:int、double之類的數據直接進行操作
會聯合使用被稱爲過濾器(Filter)流,提高留的處理效率。
過濾器流不能夠單獨使用,必須與相應的節點流進行一起使用。
帶緩衝的過濾器流
- 計算機在讀取或寫入內存中數據時速度很快,而在讀取或寫入外部設備(鍵盤、顯示器、文件等)時速度卻慢得多。
- 在IO操作中,爲了提高數據的傳輸效率,通常使用帶緩衝( Buffered )過濾器流。當向一個緩衝過濾器流寫入數據時,系統將數據發送到緩衝區,而不是直接發送到外部設備。
- BufferedInuputStream類和BufferedOutputStream就是兩個常用的帶緩衝的過濾器流。
數據輸入輸出流
當處理的數據爲int等類型時,需要專門的數據輸入輸出流來處理。
DateInputStream 類和DateOutputStream類 能夠直接讀寫Java基本類型的數據和Unicode編碼格式的字符串。
文件流
- FileInputStream
- 打開一個輸入文件,如果沒有該文件會出現異常。
- FileNotFoundException 是一個非運行時錯誤。
- FileOutputSystem
- 打開一個輸出文件,如果沒有該文件會創建一個文件;否則源文件的內容將全部重寫。
- 在I/O操作是會產生 IOException非運行時錯誤, 必須提前捕獲。
//目錄
File file1 = new File("/user/test/");
//file2、3 指向同一個文件
File file2 - new File("/user/test/","a.txt");
File file3 = new File(file1,"a.txt");
try{
File pPath = new File(System.getProperty("user.dir")+"/流/src/");
File in = new File(pPath ,"FileInput.txt");
File out = new File(pPath,"FileOut.txt");
System.out.println(in);
FileInputStream fin = new FileInputStream(in);
FileOutputStream fout = new FileOutputStream(out);
int textNu;
while ((textNu = fin.read())!= -1) {
fout.write(textNu);
System.out.println(textNu);
}
fin.close();
fout.close();
} catch (FileNotFoundException e){
System.out.println("Error:"+e.getMessage());
}
字符流
InputStream 和OutStream 是字節流
Reader和Writer是字符流
BufferReader 和 BufferWriter類 能提高讀寫處理速度。
字節流放到字符流的處理器內,需要用到轉換器。
InputStreamReader 是將輸入的字節流轉換成字符輸入流。
OutStreamReader 是將輸出的字節流轉換成字符流輸出。
隨機讀寫
RandomAccessFile
允許文件同時完成讀和寫操作,它直接繼承Object類
並且實現了接口DataInput和DataOutput類
類似存儲在文件系統的一個大型的byte數組
存在指向該隱含數組的光標或索引,成爲文件指針。
提供了支持隨機文件操作的方法:
- readXXX()或者 writeXXX():如 ReadInt(),ReadLine(),WriteChar(),WriteDouble()等
- int skipBytes(int n):將指針向下移動若干字節
- length():返回文件長度
- long getFilePointer():返回指針當前位置
- void seek(long pos):將指針調用所需位置
構造函數:
RandomAccessFile(File file,String mode)
RandomAccessFile(String name,String mode)
mode 的取值:
- r:只讀,任何寫操作都講拋出 IOException
- rw:讀寫,文件不存在時會創建該文件,文件存在是,原文件內容不變,通過寫操作改變文件內容。
- rws:打開以便讀取和寫入,對於 “rw”,還要求對文件的內容或元數據的每個更新都同步寫入到底層存儲設備。
- rwd:打開以便讀取和寫入,對於 “rw”,還要求對文件內容的每個更新都同步寫入到底層存儲設備。
對象I/O
Java 提供了Serializable的接口,只要實現了這一接口就可以使用ObjectInputStream和ObjectOutputStream 進行對象的輸入輸出。
拷貝
Path path1 = Paths.get("/home/project/1.txt");
Path path2 = Paths.get("/home/project/2.txt");
Files.copy(path1,path2, StandardCopyOption.REPLACE_EXISTING);
StandardCopyOption 屬性:
options 中指定了REPLACE_EXISTING屬性。當該命令複製目錄時,如果目錄中已經有了文件,目錄中的文件將不會被複制。CopyOption 參數支持以下 StandardCopyOption 和 LinkOption 枚舉:
- REPLACE_EXISTING - 即使目標文件已存在,也執行復制。如果目標是符號鏈接,則複製鏈接本身(而不是鏈接的目標)。如果目標是非空目錄,則複製將失敗並顯示 FileAlreadyExistsException 異常。
- COPY_ATTRIBUTES - 將與文件關聯的文件屬性複製到目標文件。支持的確切- 文件屬性是文件系統和平臺相關的,但 last-modified-time 跨平臺支持並複製到目標文件。
- NOFOLLOW_LINKS - 表示不應遵循符號鏈接。如果要複製的文件是符號鏈接,則複製鏈接(而不是鏈接的目標)。
移動和重命名
Files.move(Path,Path,StandardCopyOption);
- REPLACE_EXISTING - 即使目標文件已存在,也執行移動。如果目標是符號鏈接,則替換符號鏈接,但它指向的內容不受影響。
- ATOMIC_MOVE - 將移動作爲原子文件操作執行。如果文件系統不支持原子移動,則拋出異常。使用,ATOMIC_MOVE 您可以將文件移動到目錄中,並保證觀察目錄的任何進程都可以訪問完整的文件。