Java(8)I/O
阿新 • • 發佈:2020-11-26
[TOC]
## 一、File類
### 1、File類概述
- **java.io.File類**:一個java.io.File類的物件,表示檔案和檔案目錄路徑(就是資料夾)
- File 能新建、刪除、重新命名檔案和目錄,但 File 不能訪問檔案內容本身。如果需要訪問檔案內容本身,則需要使用輸入/輸出流。
- 想要在Java程式中表示一個真實存在的檔案或目錄,那麼必須有一個File對 象,但是Java程式中的一個File物件,可能沒有一個真實存在的檔案或目錄。
![](https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201126005707349-645424871.png)
- **經典應用場景:**File物件可以作為引數傳遞給流的構造器。
### 2、File類例項化
- **構造器**
| 構造器 | 描述 |
| :-------------------------------: | :-----------------------------: |
| File(String pathname) | 由**路徑名稱**來建立 |
| File(String parent, String child) | 由**上層目錄路徑+檔名**來建立 |
| File(File parent, String child) | 由**上層檔名+檔名**來建立 |
| File(URI uri) | 由**uri**來建立 |
- **路徑**
- 絕對路徑:是一個固定的路徑,從碟符開始
- 相對路徑:是相對於某個位置開始。在IDEA中是相對於當前的module
- **路徑分隔符**
- windows和DOS系統預設使用“\”來表示
- UNIX和URL使用“/”來表示
- Java程式支援跨平臺執行,因此路徑分隔符要慎用。為了解決這個隱患,File類提供了一個常量:`public static final String separator`,根據作業系統,動態的提供分隔符。
- 舉例
```java
File file1 = new File("d:\\atguigu\\info.txt");
File file2 = new File("d:" + File.separator + "atguigu" + File.separator + "info.txt");
File file3 = new File("d:/atguigu");
```
### 3、File類常用方法
- **獲取
| 方法 | 描述 |
| :------------------------ | :----------------------------------------------- |
| getAbsolutePath() | 獲取絕對路徑 |
| getPath() | 獲取路徑 |
| getName() | 獲取名 |
| getParent() | 獲取上層檔案目錄路徑。若無,返回null |
| length() | 獲取檔案長度(即:位元組數)。不能獲取目錄的長度。 |
| lastModified() | 獲取最後一次的修改時間,毫秒值 |
| 下面兩個是針對目錄的 | |
| public String[] list() | 獲取指定目錄下的所有檔案或者檔案目錄的名稱陣列 |
| public File[] listFiles() | 獲取指定目錄下的所有檔案或者檔案目錄的File陣列 |
- **重新命名**
`public boolean renameTo(File dest)`:把檔案重新命名為指定的檔案路徑。(實際上就是把file1的內容複製到file2,並把file1刪除)
對於 `file1.renameTo(file2)`要求:file1存在,file2不存在
- **判斷**
| 方法 | 描述 |
| ------------- | ------------------ |
| isDirectory() | 判斷是否是檔案目錄 |
| isFile() | 判斷是否是檔案 |
| exists() | 判斷是否存在 |
| canRead() | 判斷是否可讀 |
| canWrite() | 判斷是否可寫 |
| isHidden() | 判斷是否隱藏 |
- **建立和刪除**
| 方法 | 描述 | 注意事項 |
| ------------------------------ | ------------------ | ------------------------------------------------------------ |
| public boolean createNewFile() | 建立檔案 | 若檔案存在,則不建立,返回false |
| public boolean mkdir() | 建立檔案目錄 | 如果此檔案目錄存在,就不建立;
如果此檔案目錄的上層目錄不存在,也不建立 | | public boolean mkdirs() | 建立檔案目錄 | 如果上層檔案目錄不存在,一併建立 | | public boolean delete() | 刪除檔案或者資料夾 | Java中的刪除不走回收站
要刪除的檔案目錄內不能包含檔案或檔案目錄 | **如果你建立檔案或者 檔案 目錄沒有寫碟符路徑 , 那麼 ,預設在專案路徑下。** ## 二、IO流的原理 ### 1、IO流的原理 - **I/O**是input/output的縮寫,IO技術用於**裝置之間的資料傳輸**。(如。**讀/寫檔案、網路通訊**) - Java程式中,資料的輸入/輸出操作以**“流(stream)”** 的方式進行。 - **java.io包下提供了各種“流”類和介面**,用以獲取不同種類的資料,並通過標準的方法輸入或輸出資料。 ### 2、input和output的理解 - 首先對於入和出,我們是站在程式的角度來說的,**想象自己身處程式內部。** - input:磁碟、光碟等儲存裝置的資料-----> 程式、記憶體
- output:程式、記憶體中的資料----->磁碟、光碟等儲存裝置
## 三、IO流的分類
### 1、分類
- √√√按**操作的資料單位**不同分為:**位元組流(8bit)、字元流(16bit)**。位元組流適合操作圖片、視訊等檔案,字元流適合操作文字檔案。
- 按資料**流的流向**不同分為:**輸入流、輸出流。**
- 按**流的角色**不同分為:**節點流、處理流。
- **節點流:**直接從資料來源或目的地讀寫資料。也叫**檔案流**
![](https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201126005745176-1812936854.png)
- **處理流:**不直接連線到資料來源或目的地,而是“連線”在已存 在的流(節點流或處理流)之上,通過對資料的處理為程式提 供更為強大的讀寫功能
![](https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201126005756209-569026929.png)
### 2、圖示
![](https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201126005814987-631981059.png)
### 3、四個抽象基類
| 抽象基類 | 位元組流 | 字元流 |
| :------: | :----------: | :----: |
| 輸入流 | InputStream | Reader |
| 輸出流 | OutputStream | Writer |
### 4、IO流體系
![](https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201126005835858-212857294.png)
## 四、FileReader和FileWriter
### 1、IDEA中單元測試方法和main()下相對路徑對比
- 單元測試方法下的相對路徑是:相較於當前module而言
- main()下的相對路徑:相較於當前工程而言
### 2、使用FileReader讀入資料
- 最初的程式碼實現
```java
public void test1() throws IOException {
//1.例項化File類,指明要操作的物件.這一步的目的是建立硬碟中的檔案和Java中類的對應關係.
File file = new File("hello1.txt");
//2.提供具體的流.引數的作用就是幫助我們並連線上檔案這個"大水庫"
FileReader fileReader = new FileReader(file);
//3.用流讀取到記憶體
//read():返回讀入的字元,是int需要轉換為char.到了檔案結尾返回-1
int read = fileReader.read();
while (read != -1) {
System.out.print((char) read);
read = fileReader.read();
}
//4.關閉流
fileReader.close();
}
//整個過程結合圖示去理解很合理
```
- 改進後的程式碼實現
```java
/*
優化:
1.第三部可以在語法上的優化,但是效率其實是一樣的
2.為了保證關閉操作一定執行,使用try-catch-finally
3.讀入的檔案一定要存在,否則會出現:FileNotFoundException
*/
public void test2() {
FileReader fileReader = null;
try {
File file = new File("hello1.txt");
fileReader = new FileReader(file);
//改進1
int read;
while ((read = fileReader.read()) != -1){
System.out.print((char) read);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null)
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
```
- **使用緩衝流改進**(**這個是用的最多的**★★★★★)
````java
//使用陣列
char[] charBuffer = new char[5];
int len;//記錄每次讀入到charBuffer陣列中的字元個數
while ((len = fileReader.read(charBuffer)) != -1){
for (int i = 0; i < len; i++) {//這裡要用len(讀取的字元數)二不是陣列的長度
System.out.print(charBuffer[i]);
}
}
//當然for迴圈也可以換位String的構造器來把字串陣列轉換為String
String string = new String(charBuffer, 0, len);
System.out.print(string);
````
### 3、使用FileWriter寫出資料
- **程式碼實現**
````java
File file = new File("hello2.txt");
FileWriter fw = new FileWriter(file);
fw.write("i have a dream!");
fw.close();
//最後用try-catch處理一下異常,上面的步驟更清晰一些
````
- 說明
- 輸出時,File可以不存在,不會報異常。
- File對應的硬碟的檔案如果不存在,自動建立
- File對應的硬碟的檔案如果存在
- 如果流使用的構造器是FileWriter(file, false)/FileWriter(file),對原有的檔案進行覆蓋
- 如果流使用的構造器是FileWriter(file, true),對原有的檔案進行追加
### 4、使用FileReader和FileWriter複製文字檔案
- 程式碼實現
````java
public void test5() throws IOException {
File srcFile = new File("hello2.txt");
File destFile = new File("hello3.txt");
FileReader fr = new FileReader(srcFile);
FileWriter fw = new FileWriter(destFile);
char[] charBuffer = new char[5];
int len;
while ((len = fr.read(charBuffer)) != -1) {
fw.write(charBuffer, 0, len);//和用String來取是類似的★★★★★
}
fw.close();
fr.close();
}
//最後用try-catch處理一下異常,上面的步驟更清晰一些
````
### 5、使用FileReader和FileWriter不能處理圖片的複製的測試
- 當把hello.txt文字檔案改為圖片檔案時,發現程式碼是可以正常執行,但是複製結果並不對,新圖片打不開。
- 這是因為,圖片是用位元組來儲存的。用字元流來處理顯然不行。
## 五、FileInputStream和FileOutputStream
### 1、用FileInputStream和FileOutputStream處理文字檔案會怎樣?
- **結論**
- **輸出到控制檯時**:英文不亂碼,中文可能會亂碼
- 單純複製,而不在記憶體層面檢視:不會亂碼,是可以的。
- **解釋**
- **對於英文**,utf-8和gbk都是用一個位元組(4bit位)來存一個字母,因此每個字母都是完完整整的存入byte陣列,從而能完整的複製過去。
- **對於中文**,utf-8中用的是三個位元組來存一個漢字,那麼位元組陣列中的資料在輸出時,不確定在哪裡截斷,就會出現一部分字的亂碼。
### 2、確定使用字元流還是位元組流
- 對於文字檔案(.txt, .java, .cpp),使用字元流
- 對於非文字檔案(.jpg, .mp3, .mp4, .avi, .doc, .ppt...),使用位元組流
### 3、用FileInputStream和FileOutputStream複製圖片
- 同“使用FileReader和FileWriter複製文字檔案”,只要
- 使用FileInputStream和FileOutputStream
- 把陣列改為byte陣列
## 六、緩衝流
### 1、緩衝流有哪些
BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
### 2、作用
- 提高讀寫速度
### 3、原因
- 在使用這些流類時,會**建立一個內部緩衝區陣列,**預設使用8192個位元組(8Kb)的緩衝區。
- 當讀取資料時,資料按塊**讀入緩衝區**,其後的讀操作則**直接訪問緩衝區**。
- 當使用BufferedInputStream讀取位元組檔案時,BufferedInputStream會一次性從檔案中讀取8192個(8Kb),存在緩衝區中,直到緩衝區裝滿了,才重新從檔案中讀取下一個8192個位元組陣列。
- 向流中寫入位元組時,不會直接寫到檔案,先寫到緩衝區中直到緩衝區寫滿, BufferedOutputStream才會把緩衝區中的資料一次性寫到檔案裡。
- 使用**方法 flush()**可以強制將緩衝區的內容全部寫入輸出流。
- 如果是帶緩衝區的流物件的close()方法,不但會關閉流,還會在關閉流之前刷 新緩衝區,關閉後不能再寫出。
- 填坑:自己寫程式碼的時候忘記關閉流操作,導致複製的圖片打不開的原因就是,沒有關閉流,緩衝區內還有一部分資料沒能複製過去。
### 4、使用緩衝流複製圖片
````java
public void test() throws IOException {
//1.建立File
File srcFile = new File("img1.png");
File destFile = new File("img2.png");
//2.建立流
//2.1建立檔案流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//2.2建立位元組流
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
//3.複製
byte[] bytes = new byte[10];
int len;
while ((len = bis.read(bytes)) != -1){
bos.write(bytes,0,len);
}
//4.關閉流
bis.close();
bos.close();
}
````
- 關閉外層的流的同時,會自動關閉內層的流。所以只寫外層的關閉操作就可以。
### 5、使用緩衝流複製文字檔案
````java
public void test1() throws IOException {
//1.建立檔案和流
BufferedReader br = new BufferedReader(new FileReader(new File("hello1.txt")));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("hello4.txt")));
// //2.複製
// char[] chars = new char[10];
// int len;
// while ((len = br.read(chars)) != -1) {
// bw.write(chars, 0, len);
// }
// 複製:用String來實現★★★★★★★★★★★
String data;//但是是不帶換行的,可以用一以下兩種方法實現
while ((data = br.readLine()) != null) {
// //方法一★★★★★★★★
// bw.write(data + "\n");
//方法二★★★★★★★★
bw.write(data);
bw.newLine();
}
//3.關閉
br.close();
bw.close();
}
````
### 6、練習:統計文字每個字元出現次數
## 七、轉換流
### 1、什麼是轉換流
- 轉換流提供了在**位元組流和字元流之間的轉換**
- 轉換流屬於**字元流**
- Java API提供了兩個轉換流
- InputStreamReader:將InputStream轉換為Reader
- 構造器一:`public InputStreamReader(InputStream in)`預設使用utf-8字符集
- 構造器二:`public InputSreamReader(InputStream in,String charsetName)`可以自己選擇字符集。
- OutputStreamWriter:將Writer轉換為OutputStream
- 構造器和上面類似
- 位元組流中的資料都是字元時,轉成字元流操作更高效。
- 很多時候我們使用轉換流來處理檔案亂碼問題。實現編碼和解碼的功能。
### 2、編碼與解碼
- 編碼:位元組、位元組陣列---> 字元陣列、字串
- 解碼:字元陣列、字串--->位元組、位元組陣列
### 3、字符集
- **什麼是編碼表**:計算機只能識別二進位制資料,早期由來是電訊號。為了方便應用計算機,讓它可以識 別各個國家的文字。就將各個國家的文字用數字來表示,並一一對應,形成一張表。 這就是編碼表。
- **常見編碼表**
| 按照地區輔助記憶 | 編碼表 | 描述 |
| :--------------- | :----------------- | :----------------------------------------------------------- |
| 美國 | ASCII | 用**一個位元組的7位**來表示所有英文和符號 |
| 歐洲 | ISO8859-1 | 用**一個位元組的8位**表示所有歐洲語言的字母 |
| 中國 | GB2312
GBK | **最多兩個位元組**編碼所有漢字
升級版,加入了更多的漢字 | | 國際通用 | Unicode
UTF-8 | Unicode編碼是對UTF-8/16的統稱
用**1-4個位元組**表示人類所有文字 | ### 4、轉換流的作用示意圖 ![](https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201126005920627-2038164186.png) ### 5、練習題 - 綜合使用:將文字檔案從utf-8轉換為gbk編碼 - 程式碼實現 ````java @Test public void test2() throws Exception { //1.造檔案、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.讀寫過程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.關閉資源 isr.close(); osw.close(); } ```` ## 八、標準輸入輸出流(瞭解) ### 1、簡介 - 標準輸入流:System.in。預設輸入裝置是鍵盤。**型別是InputStream。** - 標準輸出流:System.out。預設輸出裝置是控制檯。**型別是PrintStream,是OutputStream的子類。** ### 2.練習 - 題目:從鍵盤輸入字串,要求將讀取到的整行字串轉成大寫輸出。然後繼續 進行輸入操作,直至當輸入“e”或者“exit”時,退出程式。 - 程式碼實現 ````java public class Exercise { public static void main(String[] args) {//idea不支援在單元測試中輸入內容,所以改用main()來測試 BufferedReader br = null; try { InputStreamReader isr = new InputStreamReader(System.in); br = new BufferedReader(isr); while (true) { System.out.println("請輸入字串: "); String data = br.readLine(); if ("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){ System.out.println("程式結束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } } } } ```` ## 九、列印流(瞭解) ### 1、列印流簡介 - 實現將**基本資料型別**的資料格式轉化為**字串**來**輸出** - 包含兩個:**PrintStream和PrintWriter** - 提供了一系列過載的print()和println()方法,用於多種資料型別的輸出 - System.out返回的是PrintStream的例項 ### 2、程式碼演示 - 把標準輸出流(控制檯輸出)改成檔案 ````java PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt")); // 建立列印輸出流,設定為自動重新整理模式(寫入換行符或位元組 '\n' 時都會重新整理輸出緩衝區) ps = new PrintStream(fos, true); if (ps != null) {// 把標準輸出流(控制檯輸出)改成檔案 System.setOut(ps); } for (int i = 0; i <= 255; i++) { // 輸出ASCII字元 System.out.print((char) i); if (i % 50 == 0) { // 每50個數據一行 System.out.println(); // 換行 } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (ps != null) { ps.close(); } } ```` ## 十、資料流(瞭解) ### 1、簡介 - **引入**:為了方便地操作Java語言的基本資料型別和String的資料,可以使用資料流 - 資料流有兩個類:(用於**讀取和寫出**基本資料型別、String類的資料,方便持久化) - DataInputStream 和 DataOutputStream - **DataInputStream中的方法** boolean readBoolean() char readChar() double readDouble() long readLong() String readUTF() byte readByte() float readFloat() short readShort() int readInt() void readFully(byte[] b) - **DataOutputStream中的方法** 將上述的方法的read改為相應的write即可 ### 2、練習 ````java 練習:將記憶體中的字串、基本資料型別的變數寫出到檔案中。 注意:處理異常的話,仍然應該使用try-catch-finally. */ @Test public void test3() throws IOException { //1. DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); //2. dos.writeUTF("劉建辰"); dos.flush();//重新整理操作,將記憶體中的資料寫入檔案 dos.writeInt(23); dos.flush(); dos.writeBoolean(true); dos.flush(); //3. dos.close(); } /* 將檔案中儲存的基本資料型別變數和字串讀取到記憶體中,儲存在變數中。 注意點:讀取不同型別的資料的順序要與當初寫入檔案時,儲存的資料的順序一致! */ @Test public void test4() throws IOException { //1. DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); //2. String name = dis.readUTF(); int age = dis.readInt(); boolean isMale = dis.readBoolean(); System.out.println("name = " + name); System.out.println("age = " + age); System.out.println("isMale = " + isMale); //3. dis.close(); } ```` ## 十一、物件流 ### 1、簡介 - 包含:兩個類ObjectInputStream和ObjectOutputStream - 是:用於儲存和讀取基本資料型別資料或物件的處理流 - 強大之處:可以把Java中的物件寫入到資料來源中,也能把物件從資料來源中還原回來。 ### 2、物件的序列化★★★★★ - **什麼是物件的序列化機制(面試題)** - 一方面,物件的序列化機制允許記憶體中的**Java物件轉換為平臺無關的二進位制流**。從而允許吧二進位制流**持久化到磁碟**,或,通過網路**傳給另一個網路節點**; - 另一方面,其他程式獲取了二進位制流,就可以恢復成原來的Java物件。 - **序列化的好處**:可將任何實現了Serializable接 使其在儲存和傳輸時可被還原。 ### 3、程式碼實現String類的物件的序列化和反序列化 ````java public class ObjectInputOutputStream { /* 程式碼實現String類的物件的序列化和反序列化 */ @Test//序列化 public void testObjectOutputStream(){ ObjectOutputStream oos = null; try { //1.造流和檔案 oos = new ObjectOutputStream(new FileOutputStream(new File("objectString.dat"))); //2.寫出 oos.writeObject(new String("我愛你中國")); } catch (IOException e) { e.printStackTrace(); } finally { //3.關閉流 try { if (oos != null) { oos.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void testObjectInputStream(){ ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(new File("objectString.dat"))); Object readObject = ois.readObject(); System.out.println(readObject); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (ois != null) { ois.close(); } } catch (IOException e) { e.printStackTrace(); } } } } ```` ### 4、自定義類的序列化 - **要求** - 實現Serializable介面(這是一個標識介面,內部沒有方法) - 提供一個全域性常量`public static final long serialVersionUID = xxxxxxxxL;` - 如果類沒有顯示定義這個靜態常量,它的值是Java執行時環境根據類的內部細節自 動生成的。若類的例項變數做了修改,serialVersionUID 可能發生變化。故建議, 顯式宣告。 - 還要保證類的所以屬性也是可序列化的。 - 基本資料型別是可序列化的 - **注意**:ObjectOutputStream和ObjectInputStream不能序列化**static和transient修飾**的成員變數 - 因為static修飾的變數是類所有的 - 不想序列化的就可以用transient ### 5、面試題 - 談談你對java.io.Serializable介面的理解,我們知道它用於序列化, 是空方法介面,還有其它認識嗎? - 實現了Serializable介面的物件,可將它們轉換成一系列位元組,並可在以後 完全恢復回原來的樣子。這一過程亦可通過網路進行。這意味著序列化機 制能自動補償作業系統間的差異。換句話說,可以先在Windows機器上創 建一個物件,對其序列化,然後通過網路發給一臺Unix機器,然後在那裡 準確無誤地重新“裝配”。不必關心資料在不同機器上如何表示,也不必 關心位元組的順序或者其他任何細節。 - 由於大部分作為引數的類如String、Integer等都實現了 java.io.Serializable的介面,也可以利用多型的性質,作為引數使介面更 靈活。 ## 十二、RandomAccessFile ### 1、簡介 - RandomAccessFile,隨機存取檔案流,**直接繼承於java.lang.Object類**。 - **隨機:**RandomAccessFile 物件包含一個記錄指標,用以標示當前讀寫處的位置。 RandomAccessFile 類物件可以自由移動記錄指標 - long getFilePointer():獲取檔案記錄指標的當前位置 - **void seek(long pos):將檔案記錄指標定位到 pos 位置(這是他的靈魂所在)** - 應用場景:可以多執行緒斷點下載同一檔案再拼接起來;下載不完,下次接著下載。 - **存取**:實現了DataInput、DataOutput兩個介面**,所以這個類**既可以讀也可以寫**。 - 可以用**構造器裡的引數**決定是輸出流還是輸入流。 - public RandomAccessFile(File file, String **mode)** - public RandomAccessFile(String name, String **mode)** - mode引數指定了訪問模式 - r: 以只讀方式開啟 (常用) - rw:開啟以便讀取和寫入 (常用) - rwd:開啟以便讀取和寫入;同步檔案內容的更新 - rws:開啟以便讀取和寫入;同步檔案內容和元資料的更新 ### 2、用RandomAccessFile類實現文字檔案的複製 ````java @Test public void test1() throws IOException { //用引數mode標識,讓類代表輸入 RandomAccessFile r = new RandomAccessFile(new File("hello1.txt"), "r"); //用引數mode標識,讓類代表輸出 RandomAccessFile rw = new RandomAccessFile(new File("hello5.txt"), "rw"); byte[] bytes = new byte[1024]; int len; while ((len=r.read(bytes)) != -1){ rw.write(bytes,0,len); } r.close(); rw.close(); } ```` ### 3、用 RandomAccessFile作為輸出流時的特點 - 如果寫出到的檔案不存在,就建立 - 如果寫出到的檔案存在,會對原文**從頭開始進行覆蓋**,能覆蓋多少算多少。 ### 4、如何使用 RandomAccessFile對文字檔案實現插入效果 ````java /* 使用RandomAccessFile實現資料的插入效果 */ @Test public void test3() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//將指標調到角標為3的位置 //儲存指標3後面的所有資料到StringBuilder中 StringBuilder builder = new StringBuilder((int) new File("hello.txt").length()); byte[] buffer = new byte[20]; int len; while((len = raf1.read(buffer)) != -1){ builder.append(new String(buffer,0,len)) ; } //調回指標,寫入“xyz” raf1.seek(3); raf1.write("xyz".getBytes()); //將StringBuilder中的資料寫入到檔案中 raf1.write(builder.toString().getBytes()); raf1.close(); } ```` ## 十三、第三方jar包的使用 ### 1、為什麼使用 - 第三方jar包提供了很多方便高效的API,方便我們開發中使用,實際開發中也是用這些jar包來提高工作效率,而不只是根據JDK提供的API,因為有些不夠簡練。 ### 2、IDEA中匯入第三方jar包 - 當前module下建立名為lib或libs的directory - 複製第三方jar包到lib目錄 - 右鍵jar包,選擇 add as library - 搞定 ### 3、用第三方jar包實現檔案複製 ````java public class JarTest { public static void main(String[] args) throws IOException { File srcFile = new File("s6_IO/hello1.txt"); File destFile = new File("s6_IO/hello6.txt"); FileUtils.copyFile(srcFile,destFile); }
如果此檔案目錄的上層目錄不存在,也不建立 | | public boolean mkdirs() | 建立檔案目錄 | 如果上層檔案目錄不存在,一併建立 | | public boolean delete() | 刪除檔案或者資料夾 | Java中的刪除不走回收站
要刪除的檔案目錄內不能包含檔案或檔案目錄 | **如果你建立檔案或者 檔案 目錄沒有寫碟符路徑 , 那麼 ,預設在專案路徑下。** ## 二、IO流的原理 ### 1、IO流的原理 - **I/O**是input/output的縮寫,IO技術用於**裝置之間的資料傳輸**。(如。**讀/寫檔案、網路通訊**) - Java程式中,資料的輸入/輸出操作以**“流(stream)”** 的方式進行。 - **java.io包下提供了各種“流”類和介面**,用以獲取不同種類的資料,並通過標準的方法輸入或輸出資料。 ### 2、input和output的理解 - 首先對於入和出,我們是站在程式的角度來說的,**想象自己身處程式內部。** - input:磁碟、光碟等儲存裝置的資料----->
升級版,加入了更多的漢字 | | 國際通用 | Unicode
UTF-8 | Unicode編碼是對UTF-8/16的統稱
用**1-4個位元組**表示人類所有文字 | ### 4、轉換流的作用示意圖 ![](https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201126005920627-2038164186.png) ### 5、練習題 - 綜合使用:將文字檔案從utf-8轉換為gbk編碼 - 程式碼實現 ````java @Test public void test2() throws Exception { //1.造檔案、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.讀寫過程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.關閉資源 isr.close(); osw.close(); } ```` ## 八、標準輸入輸出流(瞭解) ### 1、簡介 - 標準輸入流:System.in。預設輸入裝置是鍵盤。**型別是InputStream。** - 標準輸出流:System.out。預設輸出裝置是控制檯。**型別是PrintStream,是OutputStream的子類。** ### 2.練習 - 題目:從鍵盤輸入字串,要求將讀取到的整行字串轉成大寫輸出。然後繼續 進行輸入操作,直至當輸入“e”或者“exit”時,退出程式。 - 程式碼實現 ````java public class Exercise { public static void main(String[] args) {//idea不支援在單元測試中輸入內容,所以改用main()來測試 BufferedReader br = null; try { InputStreamReader isr = new InputStreamReader(System.in); br = new BufferedReader(isr); while (true) { System.out.println("請輸入字串: "); String data = br.readLine(); if ("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){ System.out.println("程式結束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException e) { e.printStackTrace(); } } } } ```` ## 九、列印流(瞭解) ### 1、列印流簡介 - 實現將**基本資料型別**的資料格式轉化為**字串**來**輸出** - 包含兩個:**PrintStream和PrintWriter** - 提供了一系列過載的print()和println()方法,用於多種資料型別的輸出 - System.out返回的是PrintStream的例項 ### 2、程式碼演示 - 把標準輸出流(控制檯輸出)改成檔案 ````java PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt")); // 建立列印輸出流,設定為自動重新整理模式(寫入換行符或位元組 '\n' 時都會重新整理輸出緩衝區) ps = new PrintStream(fos, true); if (ps != null) {// 把標準輸出流(控制檯輸出)改成檔案 System.setOut(ps); } for (int i = 0; i <= 255; i++) { // 輸出ASCII字元 System.out.print((char) i); if (i % 50 == 0) { // 每50個數據一行 System.out.println(); // 換行 } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (ps != null) { ps.close(); } } ```` ## 十、資料流(瞭解) ### 1、簡介 - **引入**:為了方便地操作Java語言的基本資料型別和String的資料,可以使用資料流 - 資料流有兩個類:(用於**讀取和寫出**基本資料型別、String類的資料,方便持久化) - DataInputStream 和 DataOutputStream - **DataInputStream中的方法** boolean readBoolean() char readChar() double readDouble() long readLong() String readUTF() byte readByte() float readFloat() short readShort() int readInt() void readFully(byte[] b) - **DataOutputStream中的方法** 將上述的方法的read改為相應的write即可 ### 2、練習 ````java 練習:將記憶體中的字串、基本資料型別的變數寫出到檔案中。 注意:處理異常的話,仍然應該使用try-catch-finally. */ @Test public void test3() throws IOException { //1. DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); //2. dos.writeUTF("劉建辰"); dos.flush();//重新整理操作,將記憶體中的資料寫入檔案 dos.writeInt(23); dos.flush(); dos.writeBoolean(true); dos.flush(); //3. dos.close(); } /* 將檔案中儲存的基本資料型別變數和字串讀取到記憶體中,儲存在變數中。 注意點:讀取不同型別的資料的順序要與當初寫入檔案時,儲存的資料的順序一致! */ @Test public void test4() throws IOException { //1. DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); //2. String name = dis.readUTF(); int age = dis.readInt(); boolean isMale = dis.readBoolean(); System.out.println("name = " + name); System.out.println("age = " + age); System.out.println("isMale = " + isMale); //3. dis.close(); } ```` ## 十一、物件流 ### 1、簡介 - 包含:兩個類ObjectInputStream和ObjectOutputStream - 是:用於儲存和讀取基本資料型別資料或物件的處理流 - 強大之處:可以把Java中的物件寫入到資料來源中,也能把物件從資料來源中還原回來。 ### 2、物件的序列化★★★★★ - **什麼是物件的序列化機制(面試題)** - 一方面,物件的序列化機制允許記憶體中的**Java物件轉換為平臺無關的二進位制流**。從而允許吧二進位制流**持久化到磁碟**,或,通過網路**傳給另一個網路節點**; - 另一方面,其他程式獲取了二進位制流,就可以恢復成原來的Java物件。 - **序列化的好處**:可將任何實現了Serializable接 使其在儲存和傳輸時可被還原。 ### 3、程式碼實現String類的物件的序列化和反序列化 ````java public class ObjectInputOutputStream { /* 程式碼實現String類的物件的序列化和反序列化 */ @Test//序列化 public void testObjectOutputStream(){ ObjectOutputStream oos = null; try { //1.造流和檔案 oos = new ObjectOutputStream(new FileOutputStream(new File("objectString.dat"))); //2.寫出 oos.writeObject(new String("我愛你中國")); } catch (IOException e) { e.printStackTrace(); } finally { //3.關閉流 try { if (oos != null) { oos.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Test public void testObjectInputStream(){ ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream(new File("objectString.dat"))); Object readObject = ois.readObject(); System.out.println(readObject); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (ois != null) { ois.close(); } } catch (IOException e) { e.printStackTrace(); } } } } ```` ### 4、自定義類的序列化 - **要求** - 實現Serializable介面(這是一個標識介面,內部沒有方法) - 提供一個全域性常量`public static final long serialVersionUID = xxxxxxxxL;` - 如果類沒有顯示定義這個靜態常量,它的值是Java執行時環境根據類的內部細節自 動生成的。若類的例項變數做了修改,serialVersionUID 可能發生變化。故建議, 顯式宣告。 - 還要保證類的所以屬性也是可序列化的。 - 基本資料型別是可序列化的 - **注意**:ObjectOutputStream和ObjectInputStream不能序列化**static和transient修飾**的成員變數 - 因為static修飾的變數是類所有的 - 不想序列化的就可以用transient ### 5、面試題 - 談談你對java.io.Serializable介面的理解,我們知道它用於序列化, 是空方法介面,還有其它認識嗎? - 實現了Serializable介面的物件,可將它們轉換成一系列位元組,並可在以後 完全恢復回原來的樣子。這一過程亦可通過網路進行。這意味著序列化機 制能自動補償作業系統間的差異。換句話說,可以先在Windows機器上創 建一個物件,對其序列化,然後通過網路發給一臺Unix機器,然後在那裡 準確無誤地重新“裝配”。不必關心資料在不同機器上如何表示,也不必 關心位元組的順序或者其他任何細節。 - 由於大部分作為引數的類如String、Integer等都實現了 java.io.Serializable的介面,也可以利用多型的性質,作為引數使介面更 靈活。 ## 十二、RandomAccessFile ### 1、簡介 - RandomAccessFile,隨機存取檔案流,**直接繼承於java.lang.Object類**。 - **隨機:**RandomAccessFile 物件包含一個記錄指標,用以標示當前讀寫處的位置。 RandomAccessFile 類物件可以自由移動記錄指標 - long getFilePointer():獲取檔案記錄指標的當前位置 - **void seek(long pos):將檔案記錄指標定位到 pos 位置(這是他的靈魂所在)** - 應用場景:可以多執行緒斷點下載同一檔案再拼接起來;下載不完,下次接著下載。 - **存取**:實現了DataInput、DataOutput兩個介面**,所以這個類**既可以讀也可以寫**。 - 可以用**構造器裡的引數**決定是輸出流還是輸入流。 - public RandomAccessFile(File file, String **mode)** - public RandomAccessFile(String name, String **mode)** - mode引數指定了訪問模式 - r: 以只讀方式開啟 (常用) - rw:開啟以便讀取和寫入 (常用) - rwd:開啟以便讀取和寫入;同步檔案內容的更新 - rws:開啟以便讀取和寫入;同步檔案內容和元資料的更新 ### 2、用RandomAccessFile類實現文字檔案的複製 ````java @Test public void test1() throws IOException { //用引數mode標識,讓類代表輸入 RandomAccessFile r = new RandomAccessFile(new File("hello1.txt"), "r"); //用引數mode標識,讓類代表輸出 RandomAccessFile rw = new RandomAccessFile(new File("hello5.txt"), "rw"); byte[] bytes = new byte[1024]; int len; while ((len=r.read(bytes)) != -1){ rw.write(bytes,0,len); } r.close(); rw.close(); } ```` ### 3、用 RandomAccessFile作為輸出流時的特點 - 如果寫出到的檔案不存在,就建立 - 如果寫出到的檔案存在,會對原文**從頭開始進行覆蓋**,能覆蓋多少算多少。 ### 4、如何使用 RandomAccessFile對文字檔案實現插入效果 ````java /* 使用RandomAccessFile實現資料的插入效果 */ @Test public void test3() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//將指標調到角標為3的位置 //儲存指標3後面的所有資料到StringBuilder中 StringBuilder builder = new StringBuilder((int) new File("hello.txt").length()); byte[] buffer = new byte[20]; int len; while((len = raf1.read(buffer)) != -1){ builder.append(new String(buffer,0,len)) ; } //調回指標,寫入“xyz” raf1.seek(3); raf1.write("xyz".getBytes()); //將StringBuilder中的資料寫入到檔案中 raf1.write(builder.toString().getBytes()); raf1.close(); } ```` ## 十三、第三方jar包的使用 ### 1、為什麼使用 - 第三方jar包提供了很多方便高效的API,方便我們開發中使用,實際開發中也是用這些jar包來提高工作效率,而不只是根據JDK提供的API,因為有些不夠簡練。 ### 2、IDEA中匯入第三方jar包 - 當前module下建立名為lib或libs的directory - 複製第三方jar包到lib目錄 - 右鍵jar包,選擇 add as library - 搞定 ### 3、用第三方jar包實現檔案複製 ````java public class JarTest { public static void main(String[] args) throws IOException { File srcFile = new File("s6_IO/hello1.txt"); File destFile = new File("s6_IO/hello6.txt"); FileUtils.copyFile(srcFile,destFile); }