java進階 09

2020-08-08 13:07:48

IO概述

1.Java中I/O操作主要是指使用 java.io 包下的內容,進行輸入、輸出操作。輸入也叫做讀取數據,輸出也叫做作寫出數據

IO的分類

1.根據數據的流向分爲:
(1)輸入流 :把數據從其他裝置 上讀取到記憶體 中的流。
(2)輸出流 :把數據從記憶體 中寫出到 其他裝置上的流。
2.格局數據的型別分爲:
(1)位元組流 :以位元組爲單位,讀寫數據的流。
(2)字元流 :以字元爲單位,讀寫數據的流。
在这里插入图片描述
頂級父類別們:
在这里插入图片描述

位元組流

1.一切檔案數據(文字、圖片、視訊等)在儲存時,都是以二進制數位的形式儲存,都一個一個的位元組,那麼傳輸時一 樣如此。所以,位元組流可以傳輸任意檔案數據
2.無論使用什麼樣的流物件,底 層傳輸的始終爲二進制數據

位元組輸出流【OutputStream】

1.java.io.OutputStream 抽象類是表示位元組輸出流的所有類的超類,將指定的位元組資訊寫出到目的地。它定義了位元組輸出流的基本共性功能方法。
public void close() :關閉此輸出流並釋放與此流相關聯的任何系統資源。
public void flush() :重新整理此輸出流並強制任何緩衝的輸出位元組被寫出。
public void write(byte[] b) :將 b.length位元組從指定的位元組陣列寫入此輸出流。
public void write(byte[] b, int off, int len) :從指定的位元組陣列寫入 len位元組,從偏移量 off開始輸 出到此輸出流。
public abstract void write(int b) :將指定的位元組輸出流。
注:close方法,當完成流的操作時,必須呼叫此方法,釋放系統資源。

FileOutputStream類

1.java.io.FileOutputStream 類是檔案輸出流,用於將數據寫出到檔案。
2.構造方法:
public FileOutputStream(File file) :建立檔案輸出流以寫入由指定的 File物件表示的檔案。
public FileOutputStream(String name) : 建立檔案輸出流以指定的名稱寫入檔案。
程式碼範例:

public class FileOutputStreamConstructor throws IOException {
public static void main(String[] args) {
// 使用File物件建立流物件
File file = new File("a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用檔名稱建立流物件
FileOutputStream fos = new FileOutputStream("b.txt");
}
}

寫出位元組數據

1.寫出位元組: write(int b) 方法,每次可以寫出一個位元組數據,程式碼範例:

public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileOutputStream fos = new FileOutputStream("fos.txt");
// 寫出數據
fos.write(97); // 寫出第1個位元組
fos.write(98); // 寫出第2個位元組
fos.write(99); // 寫出第3個位元組
// 關閉資源
fos.close();
}
}

輸出結果:
abc
注:

  1. 雖然參數爲int型別四個位元組,但是隻會保留一個位元組的資訊寫出。
  2. 流操作完畢後,必須釋放系統資源,呼叫close方法,千萬記得。

2.寫出位元組陣列: write(byte[] b) ,每次可以寫出陣列中的數據,程式碼範例:

public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字串轉換爲位元組陣列
byte[] b = "黑馬程式設計師".getBytes();
// 寫出位元組陣列數據
fos.write(b);
// 關閉資源
fos.close();
}
}

輸出結果:
黑馬程式設計師

3.寫出指定長度位元組陣列: write(byte[] b, int off, int len) ,每次寫出從off索引開始,len個位元組,程式碼範例:

public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字串轉換爲位元組陣列
byte[] b = "abcde".getBytes();
// 寫出從索引2開始,2個位元組。索引2是c,兩個位元組,也就是cd。
fos.write(b,2,2);
// 關閉資源
fos.close();
}
}

輸出結果:
cd

數據追加續寫

1.每次程式執行,建立輸出流物件,都會清空目標檔案中的數據。如何保留目標檔案中數據,還能 繼續新增新數據呢?
2.構造方法;
public FileOutputStream(File file, boolean append) : 建立檔案輸出流以寫入由指定的 File物件表示的 檔案。
public FileOutputStream(String name, boolean append) : 建立檔案輸出流以指定的名稱寫入檔案。
3.這兩個構造方法,參數中都需要傳入一個boolean型別的值, true 表示追加數據, false 表示清空原有數據。 這樣建立的輸出流物件,就可以指定是否追加續寫了,程式碼範例:

public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileOutputStream fos = new FileOutputStream("fos.txt"true);
// 字串轉換爲位元組陣列
byte[] b = "abcde".getBytes();
// 寫出從索引2開始,2個位元組。索引2是c,兩個位元組,也就是cd。
fos.write(b);
// 關閉資源
fos.close();
}
}

輸出結果:
檔案操作前:cd
檔案操作後:cdabcde

寫出換行

1.Windows系統裡,換行符號是 \r\n 。
2.回車符 \r 和換行符 \n :
回車符:回到一行的開頭(return)。
換行符:下一行(newline)。
3.系統中的換行:
(1)Windows系統裡,每行結尾是回車+換行 ,即 \r\n
(2)Unix系統裡,每行結尾只有換行 ,即 \n ;
(3)Mac系統裡,每行結尾是回車 ,即 \r 。從 Mac OS X開始與Linux統一。
程式碼範例:

public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileOutputStream fos = new FileOutputStream("fos.txt");
// 定義位元組陣列
byte[] words = {97,98,99,100,101};
// 遍歷陣列
for (int i = 0; i < words.length; i++) {
// 寫出一個位元組
fos.write(words[i]);
// 寫出一個換行, 換行符號轉成陣列寫出
fos.write("\r\n".getBytes());
}
// 關閉資源
fos.close();
}
}

輸出結果:
a
b
c
d
e

位元組輸入流【InputStream】

1.java.io.FileInputStream 類是檔案輸入流,從檔案中讀取位元組。
2.構造方法:
FileInputStream(File file) : 通過開啓與實際檔案的連線來建立一個FileInputStream ,該檔案由檔案系統中的 File物件 file命名
FileInputStream(String name) : 通過開啓與實際檔案的連線來建立一個 FileInputStream ,該檔案由文件系統中的路徑名 name命名
3.建立一個流物件時,必須傳入一個檔案路徑。該路徑下,如果沒有該檔案,會拋出 FileNotFoundException
程式碼範例:

public class FileInputStreamConstructor throws IOException{
public static void main(String[] args) {
// 使用File物件建立流物件
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);
// 使用檔名稱建立流物件
FileInputStream fos = new FileInputStream("b.txt");
}
}
  1. 讀取位元組數據
    (1)讀取位元組: read 方法,每次可以讀取一個位元組的數據,提升爲int型別,讀取到檔案末尾,返回 -1 ,程式碼範例:
public class FISRead {
public static void main(String[] args) throws IOException{
// 使用檔名稱建立流物件
FileInputStream fis = new FileInputStream("read.txt");
// 定義變數,儲存數據
int b ;
// 回圈讀取
while ((b = fis.read())!=1) {
System.out.println((char)b);
}
// 關閉資源
fis.close();
}
}

輸出結果:
a
b
c
d
e
注:
(1)雖然讀取了一個位元組,但是會自動提升爲int型別。
(2)流操作完畢後,必須釋放系統資源,呼叫close方法,千萬記得。

(2)使用位元組陣列讀取: read(byte[] b) ,每次讀取b的長度個位元組到陣列中,返回讀取到的有效位元組個數,讀取到末尾時,返回 -1 ,程式碼範例:

public class FISRead {
public static void main(String[] args) throws IOException{
// 使用檔名稱建立流物件.
FileInputStream fis = new FileInputStream("read.txt"); // 檔案中爲abcde
// 定義變數,作爲有效個數
int len ;
// 定義位元組陣列,作爲裝位元組數據的容器
byte[] b = new byte[2];
// 回圈讀取
while (( len= fis.read(b))!=1) {
// 每次讀取後,把陣列變成字串列印
System.out.println(new String(b));
}
// 關閉資源
fis.close();
}
}

輸出結果:
ab
cd
ed
注:錯誤數據 d ,是由於最後一次讀取時,只讀取一個位元組 e ,陣列中,上次讀取的數據沒有被完全替換,所以要通過 len ,獲取有效的位元組,程式碼範例:

public class FISRead {
public static void main(String[] args) throws IOException{
// 使用檔名稱建立流物件.
FileInputStream fis = new FileInputStream("read.txt"); // 檔案中爲abcde
// 定義變數,作爲有效個數
int len ;
// 定義位元組陣列,作爲裝位元組數據的容器
byte[] b = new byte[2];
// 回圈讀取
while (( len= fis.read(b))!=1) {
// 每次讀取後,把陣列的有效位元組部分,變成字串列印
System.out.println(new String(b,0,len));// len 每次讀取的有效位元組個數
}
// 關閉資源
fis.close();
}
}

輸出結果:
ab
cd
e
5.使用陣列讀取,每次讀取多個位元組,減少了系統間的IO操作次數,從而提高了讀寫的效率,建議開發中使用。

位元組流練習:圖片複製

在这里插入图片描述
程式碼範例:

public class Copy {
public static void main(String[] args) throws IOException {
// 1.建立流物件
// 1.1 指定數據源
FileInputStream fis = new FileInputStream("D:\\test.jpg");
// 1.2 指定目的地
FileOutputStream fos = new FileOutputStream("test_copy.jpg");
// 2.讀寫數據
// 2.1 定義陣列
byte[] b = new byte[1024];
// 2.2 定義長度
int len;
// 2.3 回圈讀取
while ((len = fis.read(b))!=1) {
// 2.4 寫出數據
fos.write(b, 0 , len);
}
// 3.關閉資源
fos.close();
fis.close();
}
}

字元流

1.位元組流讀取文字檔案時,可能會有一個小問題。就是遇到中文字元時,可能不會顯示完整的字元,那是因爲 一箇中文字元可能佔用多個位元組儲存。所以Java提供一些字元流類,以字元爲單位讀寫數據,專門用於處理文字檔案。

字元輸入流【Reader】

1.java.io.Reader 抽象類是表示用於讀取字元流的所有類的超類,可以讀取字元資訊到記憶體中。它定義了字元輸入流的基本共性功能方法:
public void close() :關閉此流並釋放與此流相關聯的任何系統資源。
public int read() : 從輸入流讀取一個字元。
public int read(char[] cbuf) : 從輸入流中讀取一些字元,並將它們儲存到字元陣列 cbuf中 。

FileReader類

1.java.io.FileReader 類是讀取字元檔案的便利類。構造時使用系統預設的字元編碼和預設位元組緩衝區
2.字元編碼:位元組與字元的對應規則。Windows系統的中文編碼預設是GBK編碼表。 idea中UTF-8。
3.位元組緩衝區:一個位元組陣列,用來臨時儲存位元組數據。
4.構造方法:
FileReader(File file) : 建立一個新的 FileReader ,給定要讀取的File物件。 FileReader(String fileName) : 建立一個新的 FileReader ,給定要讀取的檔案的名稱。
程式碼範例:

public class FileReaderConstructor throws IOException{
public static void main(String[] args) {
// 使用File物件建立流物件
File file = new File("a.txt");
FileReader fr = new FileReader(file);
// 使用檔名稱建立流物件
FileReader fr = new FileReader("b.txt");
}
}

讀取字元數據

  1. 讀取字元: read 方法,每次可以讀取一個字元的數據,提升爲int型別,讀取到檔案末尾,返回 -1 ,回圈讀 取,程式碼範例:
public class FRRead {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileReader fr = new FileReader("read.txt");
// 定義變數,儲存數據
int b ;
// 回圈讀取
while ((b = fr.read())!=1) {
System.out.println((char)b);
}
// 關閉資源
fr.close();
}
}

輸出結果:





2.使用字元陣列讀取: read(char[] cbuf) ,每次讀取b的長度個字元到陣列中,返回讀取到的有效字元個數, 讀取到末尾時,返回 -1 ,程式碼範例:

public class FRRead {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileReader fr = new FileReader("read.txt");
// 定義變數,儲存有效字元個數
int len ;
// 定義字元陣列,作爲裝字元數據的容器
char[] cbuf = new char[2];
// 回圈讀取
while ((len = fr.read(cbuf))!=1) {
System.out.println(new String(cbuf));
}
// 關閉資源
fr.close();
}
}

輸出結果:
黑馬
程式
員序
獲取有效的字元改進,程式碼範例:

public class FISRead {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileReader fr = new FileReader("read.txt");
// 定義變數,儲存有效字元個數
int len ;
// 定義字元陣列,作爲裝字元數據的容器
char[] cbuf = new char[2];
// 回圈讀取
while ((len = fr.read(cbuf))!=1) {
System.out.println(new String(cbuf,0,len));
}
// 關閉資源
fr.close();
}
}

輸出結果:
黑馬
程式

字元輸出流【Writer】

1.java.io.Writer 抽象類是表示用於寫出字元流的所有類的超類,將指定的字元資訊寫出到目的地。它定義了位元組 輸出流的基本共性功能方法。
2.方法:
void write(int c) 寫入單個字元。
void write(char[] cbuf) 寫入字元陣列。
abstract void write(char[] cbuf, int off, int len) 寫入字元陣列的某一部分,off陣列的開始索引,len 寫的字元個數。
void write(String str) 寫入字串。
void write(String str, int off, int len) 寫入字串的某一部分,off字串的開始索引,len寫的字元個 數。
void flush() 重新整理該流的緩衝。
void close() 關閉此流,但要先重新整理它。

FileWriter類

1.java.io.FileWriter 類是寫出字元到檔案的便利類。構造時使用系統預設的字元編碼和預設位元組緩衝區。
2.構造方法:
FileWriter(File file) : 建立一個新的 FileWriter,給定要讀取的File物件。 FileWriter(String fileName) : 建立一個新的 FileWriter,給定要讀取的檔案的名稱。
程式碼範例:

public class FileWriterConstructor {
public static void main(String[] args) throws IOException {
// 使用File物件建立流物件
File file = new File("a.txt");
FileWriter fw = new FileWriter(file);
// 使用檔名稱建立流物件
FileWriter fw = new FileWriter("b.txt");
}
}

基本寫出數據

1.寫出字元: write(int b) 方法,每次可以寫出一個字元數據,程式碼範例:

public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileWriter fw = new FileWriter("fw.txt");
// 寫出數據
fw.write(97); // 寫出第1個字元
fw.write('b'); // 寫出第2個字元
fw.write('C'); // 寫出第3個字元
fw.write(30000); // 寫出第4個字元,中文編碼表中30000對應一個漢字。
/*
【注意】關閉資源時,與FileOutputStream不同。
如果不關閉,數據只是儲存到緩衝區,並未儲存到檔案。
*/
// fw.close();
}
}

輸出結果:
abC田
注:未呼叫close方法,數據只是儲存到了緩衝區,並未寫出到檔案中。

關閉和重新整理

1.因爲內建緩衝區的原因,如果不關閉輸出流,無法寫出字元到檔案中。但是關閉的流物件,是無法繼續寫出數據 的。如果既想寫出數據,又想繼續使用流,就需要 flush 方法了。
2.:
flush :重新整理緩衝區,流物件可以繼續使用。
close :先重新整理緩衝區,然後通知系統釋放資源。流物件不可以再被使用了。
程式碼範例:

public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileWriter fw = new FileWriter("fw.txt");
// 寫出數據,通過flush
fw.write('刷'); // 寫出第1個字元
fw.flush();
fw.write('新'); // 繼續寫出第2個字元,寫出成功
fw.flush();
// 寫出數據,通過close
fw.write('關'); // 寫出第1個字元
fw.close();
fw.write('閉'); // 繼續寫出第2個字元,【報錯】java.io.IOException: Stream closed
fw.close();
}
}

寫出其他數據

  1. 寫出字元陣列 : write(char[] cbuf) 和 write(char[] cbuf, int off, int len) ,每次可以寫出字元數 組中的數據,用法類似FileOutputStream,程式碼範例:
public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileWriter fw = new FileWriter("fw.txt");
// 字串轉換爲位元組陣列
char[] chars = "好好學習".toCharArray();
// 寫出字元陣列
fw.write(chars); // 好好學習
// 寫出從索引2開始,2個位元組。索引2是'學',兩個位元組,也就是'學習'。
fw.write(b,2,2); // 學習
// 關閉資源
fos.close();
}
}

2.寫出字串: write(String str) 和 write(String str, int off, int len) ,每次可以寫出字串中的 數據,更爲方便,程式碼範例:

public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件
FileWriter fw = new FileWriter("fw.txt");
// 字串
String msg = "好好學習";
// 寫出字元陣列
fw.write(msg); //好好學習
// 寫出從索引2開始,2個位元組。索引2是'學',兩個位元組,也就是'學習'。
fw.write(msg,2,2); // 學習
// 關閉資源
fos.close();
}
}

3.續寫和換行:操作類似於FileOutputStream。程式碼範例:

public class FWWrite {
public static void main(String[] args) throws IOException {
// 使用檔名稱建立流物件,可以續寫數據
FileWriter fw = new FileWriter("fw.txt"true);
// 寫出字串
fw.write("好好");
// 寫出換行
fw.write("\r\n");
// 寫出字串
fw.write("學習");
// 關閉資源
fw.close();
}
}

輸出結果:
好好
學習

注:字元流,只能操作文字檔案,不能操作圖片,視訊等非文字檔案。 當我們單純讀或者寫文字檔案時使用字元流 其他情況使用位元組流。

IO異常的處理

1.實際開發中建議使用 try...catch...finally 程式碼塊處理異常部分,程式碼使範例:

public class HandleException1 {
public static void main(String[] args) {
// 宣告變數
FileWriter fw = null;
try {
//建立流物件
fw = new FileWriter("fw.txt");
// 寫出數據
fw.write("黑馬程式設計師"); //黑馬程式設計師
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

2.JDK7的處理:可以使用JDK7優化後的 try-with-resource 語句,該語句確保了每個資源在語句結束時關閉。所謂的資源 (resource)是指在程式完成後,必須關閉的物件
程式碼範例:

public class HandleException2 {
public static void main(String[] args) {
// 建立流物件
try ( FileWriter fw = new FileWriter("fw.txt"); ) {
// 寫出數據
fw.write("黑馬程式設計師"); //黑馬程式設計師
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.JDK9的改進:JDK9中 try-with-resource 的改進,對於引入物件的方式,支援的更加簡潔。被引入的物件,同樣可以自動關閉, 無需手動close。
程式碼範例:

public class TryDemo {
public static void main(String[] args) throws IOException {
// 建立流物件
final FileReader fr = new FileReader("in.txt");
FileWriter fw = new FileWriter("out.txt");
// 引入到try中
try (fr; fw) {
// 定義變數
int b;
// 讀取數據
while ((b = fr.read())!=1) {
// 寫出數據
fw.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

屬性集

1.概述:java.util.Properties 繼承於 Hashtable ,來表示一個持久的屬性集。它使用鍵值結構儲存數據,每個鍵及其 對應值都是一個字串。

Properties類

1.構造方法:
public Properties() :建立一個空的屬性列表。
2.儲存方法:
public Object setProperty(String key, String value) : 儲存一對屬性。
public String getProperty(String key) :使用此屬性列表中指定的鍵搜尋屬性值。
public Set<String> stringPropertyNames() :所有鍵的名稱的集合。
程式碼範例:

public class ProDemo {
public static void main(String[] args) throws FileNotFoundException {
// 建立屬性集物件
Properties properties = new Properties();
// 新增鍵值對元素
properties.setProperty("filename", "a.txt");
properties.setProperty("length", "209385038");
properties.setProperty("location", "D:\\a.txt");
// 列印屬性集物件
System.out.println(properties);
// 通過鍵,獲取屬性值
System.out.println(properties.getProperty("filename"));
System.out.println(properties.getProperty("length"));
System.out.println(properties.getProperty("location"));
// 遍歷屬性集,獲取所有鍵的集合
Set<String> strings = properties.stringPropertyNames();
// 列印鍵值對
for (String key : strings ) {
System.out.println(key+" ‐‐ "+properties.getProperty(key));
}
}
}

輸出結果:
{filename=a.txt, length=209385038, location=D:\a.txt}
a.txt
209385038
D:\a.txt
filename ‐‐ a.txt
length ‐‐ 209385038
location ‐‐ D:\a.txt

3.與流相關的方法
public void load(InputStream inStream) : 從位元組輸入流中讀取鍵值對。
程式碼範例:

//文字數據:filename=a.txt length=209385038 location=D:\a.txt
public class ProDemo2 {
public static void main(String[] args) throws FileNotFoundException {
// 建立屬性集物件
Properties pro = new Properties();
// 載入文字中資訊到屬性集
pro.load(new FileInputStream("read.txt"));
// 遍歷集合併列印
Set<String> strings = pro.stringPropertyNames();
for (String key : strings ) {
System.out.println(key+" ‐‐ "+pro.getProperty(key));
}
}
}

輸出結果:
filename ‐‐ a.txt
length ‐‐ 209385038
location ‐‐ D:\a.txt
注:文字中的數據,必須是鍵值對形式,可以使用空格、等號、冒號等符號分隔。