Java之Properties類,緩衝類,轉換流,序列化流,裝飾者模式,commons-io工具包
第一章 IO資源的處理
知識點-- JDK7前處理
目標
- 掌握jdk7之前處理IO異常的方式
路徑
- jdk7之前處理IO異常的方式
講解
之前的入門練習,我們一直把異常丟擲,而實際開發中並不能這樣處理,建議使用try...catch...finally
程式碼塊,處理異常部分,程式碼使用演示:
public class Test { public static void main(String[] args) { // 練習: 拷貝檔案 // jdk7前IO異常處理 FileInputStream fis = null; FileOutputStream fos = null; try { // 1.建立位元組輸入流物件,關聯資料來源檔案路徑 fis = new FileInputStream("day13\\aaa\\hb.jpg"); // 2.建立位元組輸出流物件,關聯目的地檔案路徑 fos = new FileOutputStream("day13\\aaa\\hbCopy1.jpg"); // 3.定義一個位元組陣列,用來儲存讀取到的位元組資料 byte[] bys = new byte[8192]; // 3.定義一個int型別變數,用來儲存讀取到的位元組個數 int len; // 4.迴圈讀取 while ((len = fis.read(bys)) != -1) { // 5.在迴圈中,寫出資料 fos.write(bys, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { // 6.關閉流,釋放資源 try { if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); }finally { try { if (fis != null) { fis.close(); } } catch (IOException e) { e.printStackTrace(); } } } } }
知識點-- JDK7的處理
目標
- 掌握jdk7處理IO異常的方式
路徑
- jdk7處理IO異常的方式
講解
還可以使用JDK7優化後的try-with-resource
語句,該語句確保了每個資源在語句結束時關閉。所謂的資源(resource)是指在程式完成後,必須關閉的物件。
格式:
try (建立流物件語句,如果多個,使用';'隔開) {
// 讀寫資料
} catch (IOException e) {
e.printStackTrace();
}
程式碼使用演示:
public class HandleException2 { public static void main(String[] args) { // 建立流物件 try ( FileWriter fw = new FileWriter("fw.txt"); ) { // 寫出資料 fw.write("黑馬程式設計師"); //黑馬程式設計師 } catch (IOException e) { e.printStackTrace(); } } }
第二章 屬性集
知識點-- Properties類
目標
- 掌握Properties類的使用
路徑
- Properties類的概述
- Properties類的構造方法
- Properties類儲存方法
- Properties類與流相關的方法
講解
Properties類的概述
java.util.Properties
繼承於 Hashtable
,來表示一個持久的屬性集。它使用鍵值結構儲存資料,每個鍵及其對應值都是一個字串。該類也被許多Java類使用,比如獲取系統屬性時,System.getProperties
方法就是返回一個Properties
物件。
Properties類的構造方法
public Properties()
Properties類儲存方法
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
Properties類與流相關的方法
public void load(InputStream inStream)
: 從位元組輸入流中讀取鍵值對。public void load(Reader reader)
: 從位元組輸入流中讀取鍵值對。
引數中使用了位元組輸入流,通過流物件,可以關聯到某檔案上,這樣就能夠載入文字中的資料了。
載入程式碼演示:
public class Test3操作配置檔案 {
public static void main(String[] args) throws Exception{
/*
Properties類操作配置檔案:
1.載入(讀取)配置檔案中的資料 常見
public void load(InputStream inStream): 從位元組輸入流中讀取鍵值對。
public void load(Reader reader): 從位元組輸入流中讀取鍵值對。
2.往配置檔案中寫入資料 不常見
void store(OutputStream out, String comments) 把Properties物件中的所有鍵值對寫入配置檔案中
void store(Writer writer,, String comments) 把Properties物件中的所有鍵值對寫入配置檔案中
注意:
1.文字中的資料,必須是鍵值對形式,可以使用空格、等號、冒號等符號分隔。
2.如果配置檔案中有中文,那麼載入檔案檔案時,使用字元流,但是開發中一般配置檔案中不要寫中文
*/
// 建立Properties物件
Properties pro = new Properties();
// 往pro物件中新增鍵值對
pro.setProperty("username", "root");
pro.setProperty("password", "123456");
pro.setProperty("url", "http://www.itcast.com");
// 把pro物件中的鍵值對寫入到檔案中
FileOutputStream fos = new FileOutputStream("day13\\aaa\\db2.properties");
pro.store(fos,"itheima");
// 關閉流
fos.close();
}
// 1.載入(讀取)配置檔案中的資料 常見
private static void method01() throws IOException {
// 1.載入(讀取)配置檔案中的資料
// 建立Properties物件
Properties pro = new Properties();
// 呼叫load方法載入配置檔案中的資料
//FileInputStream fis = new FileInputStream("day13\\aaa\\a.txt");
FileInputStream fis = new FileInputStream("day13\\aaa\\db.properties");
pro.load(fis);// 把關聯檔案中的資料以鍵值對的形式儲存到pro物件中
// 關閉流,釋放資源
fis.close();
// 列印pro物件
System.out.println(pro);
System.out.println("--------------------------------------------------");
// 注意事項: 如果配置檔案中有中文
// 建立Properties物件
Properties pro2 = new Properties();
// 呼叫load方法載入配置檔案中的資料
FileReader fr = new FileReader("day13\\aaa\\a.txt");
pro2.load(fr);// 把關聯檔案中的資料以鍵值對的形式儲存到pro物件中
// 關閉流,釋放資源
fr.close();
// 列印pro物件
System.out.println(pro2);
}
}
檔案:
db.properties:
username=root
password=123456
url=http://www.itcast.com
a.txt:
username=中國
password=123456
url=http://www.itcast.com
小貼士:文字中的資料,必須是鍵值對形式,可以使用空格、等號、冒號等符號分隔。
第三章 緩衝流
知識點--緩衝流
目標
- 理解緩衝流的概述
路徑
- 緩衝流的概述
講解
昨天學習了基本的一些流,作為IO流的入門,今天我們要見識一些更強大的流。比如能夠高效讀寫的緩衝流,能夠轉換編碼的轉換流,能夠持久化儲存物件的序列化流等等。這些功能更為強大的流,都是在基本的流物件基礎之上建立而來的,就像穿上鎧甲的武士一樣,相當於是對基本流物件的一種增強。
緩衝流,也叫高效流,是對4個基本的FileXxx
流的增強,所以也是4個流,按照資料型別分類:
- 位元組緩衝流:
BufferedInputStream
,BufferedOutputStream
- 字元緩衝流:
BufferedReader
,BufferedWriter
緩衝流的基本原理,是在建立流物件時,會建立一個內建的預設大小的緩衝區陣列,通過緩衝區讀寫,減少系統IO次數,從而提高讀寫的效率。
知識點--位元組緩衝流
目標
- 掌握位元組緩衝流的使用
路徑
- 位元組緩衝流的構造方法
- 拷貝檔案效率測試
講解
位元組緩衝流的構造方法
public BufferedInputStream(InputStream in)
:建立一個 新的緩衝輸入流。public BufferedOutputStream(OutputStream out)
: 建立一個新的緩衝輸出流。
構造舉例,程式碼如下:
// 建立位元組緩衝輸入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 建立位元組緩衝輸出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
拷貝檔案效率測試
查詢API,緩衝流讀寫方法與基本的流是一致的,我們通過複製大檔案(375MB),測試它的效率。
- 基本流,程式碼如下:
// 使用普通位元組流讀寫一個位元組拷貝檔案
public class BufferedDemo {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 建立位元組輸入流物件,關聯資料來源檔案路徑
FileInputStream fis = new FileInputStream("day13\\bbb\\jdk9.exe");
// 建立位元組輸出流物件,關聯目的地檔案路徑
FileOutputStream fos = new FileOutputStream("day13\\bbb\\jdk9Copy.exe");
// 定義一個int型別的變數,用來儲存讀取到的位元組資料
int len;
// 迴圈讀取
while ((len = fis.read()) != -1) {
// 在迴圈中,寫出資料
fos.write(len);
}
// 關閉流,釋放資源
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println("總共需要:" + (end - start) + "毫秒");// 至少10幾分鐘
}
}
- 緩衝流,程式碼如下:
// 使用高效位元組流讀寫一個位元組拷貝檔案
public class BufferedDemo {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
// 建立位元組輸入流物件,關聯資料來源檔案路徑
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day13\\bbb\\jdk9.exe"));
// 建立位元組輸出流物件,關聯目的地檔案路徑
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day13\\bbb\\jdk9Copy1.exe"));
// 定義一個int型別的變數,用來儲存讀取到的位元組資料
int len;
// 迴圈讀取
while ((len = bis.read()) != -1) {
// 在迴圈中,寫出資料
bos.write(len);
}
// 關閉流,釋放資源
bos.close();
bis.close();
long end = System.currentTimeMillis();
System.out.println("總共需要:" + (end - start) + "毫秒");// 大約32秒
}
}
如何更快呢?
使用陣列的方式,程式碼如下:
// 使用高效位元組流讀寫一個數組位元組拷貝檔案
public class BufferedDemo {
public static void main(String[] args) throws FileNotFoundException {
long start = System.currentTimeMillis();
// 建立位元組輸入流物件,關聯資料來源檔案路徑
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day13\\bbb\\jdk9.exe"));
// 建立位元組輸出流物件,關聯目的地檔案路徑
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day13\\bbb\\jdk9Copy2.exe"));
// 定義一個位元組陣列,用來儲存讀取到的位元組資料
byte[] bys = new byte[8192];
// 定義一個int型別的變數,用來儲存讀取到的位元組資料
int len;
// 迴圈讀取
while ((len = bis.read(bys)) != -1) {
// 在迴圈中,寫出資料
bos.write(bys,0,len);
}
// 關閉流,釋放資源
bos.close();
bis.close();
long end = System.currentTimeMillis();
System.out.println("總共需要:" + (end - start) + "毫秒");// 大約4秒
}
}
知識點--字元緩衝流
目標
- 掌握字元緩衝流的使用
路徑
- 字元緩衝流的構造方法
- 字元緩衝流的特有方法
講解
字元緩衝流的構造方法
public BufferedReader(Reader in)
:建立一個 新的緩衝輸入流。public BufferedWriter(Writer out)
: 建立一個新的緩衝輸出流。
構造舉例,程式碼如下:
// 建立字元緩衝輸入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 建立字元緩衝輸出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
字元緩衝流的特有方法
字元緩衝流的基本方法與普通字元流呼叫方式一致,不再闡述,我們來看它們具備的特有方法。
- BufferedReader:
public String readLine()
: 讀一行文字。 - BufferedWriter:
public void newLine()
: 寫一行行分隔符,由系統屬性定義符號。
readLine
方法演示,程式碼如下:
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
// 建立字元緩衝輸入流物件,關聯資料來源檔案路徑
BufferedReader br = new BufferedReader(new FileReader("day13\\bbb\\b.txt"));
// 讀取一行文字
//String line1 = br.readLine();
//System.out.println(line1);
// 迴圈讀取
// 定義一個String型別的變數,用來儲存讀取到的一行文字
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
// 關閉流,釋放資源
br.close();
}
}
newLine
方法演示,程式碼如下:
public class BufferedWriterDemo throws IOException {
public static void main(String[] args) throws IOException {
// 建立字元緩衝輸出流物件,管理目的地檔案路徑
BufferedWriter bw = new BufferedWriter(new FileWriter("day13\\bbb\\c.txt"));
// 寫出資料
bw.write("戒檳榔");
bw.newLine();
bw.write("檳榔加煙, 法力無邊");
bw.newLine();
bw.write("檳榔生吞, 道法乾坤");
bw.newLine();
bw.write("檳榔配酒,永垂不朽");
bw.newLine();
bw.write("檳榔加煙又加酒,閻王在向你招手");
bw.newLine();
bw.write("好詩");
// 關閉流,釋放資源
bw.close();
}
}
實操--文字排序
需求
請將文字資訊恢復順序。
3.侍中、侍郎郭攸之、費禕、董允等,此皆良實,志慮忠純,是以先帝簡拔以遺陛下。愚以為宮中之事,事無大小,悉以諮之,然後施行,必得裨補闕漏,有所廣益。
8.願陛下託臣以討賊興復之效,不效,則治臣之罪,以告先帝之靈。若無興德之言,則責攸之、禕、允等之慢,以彰其咎;陛下亦宜自謀,以諮諏善道,察納雅言,深追先帝遺詔,臣不勝受恩感激。
4.將軍向寵,性行淑均,曉暢軍事,試用之於昔日,先帝稱之曰能,是以眾議舉寵為督。愚以為營中之事,悉以諮之,必能使行陣和睦,優劣得所。
2.宮中府中,俱為一體,陟罰臧否,不宜異同。若有作奸犯科及為忠善者,宜付有司論其刑賞,以昭陛下平明之理,不宜偏私,使內外異法也。
1.先帝創業未半而中道崩殂,今天下三分,益州疲弊,此誠危急存亡之秋也。然侍衛之臣不懈於內,忠志之士忘身於外者,蓋追先帝之殊遇,欲報之於陛下也。誠宜開張聖聽,以光先帝遺德,恢弘志士之氣,不宜妄自菲薄,引喻失義,以塞忠諫之路也。
9.今當遠離,臨表涕零,不知所言。
6.臣本布衣,躬耕於南陽,苟全性命於亂世,不求聞達於諸侯。先帝不以臣卑鄙,猥自枉屈,三顧臣於草廬之中,諮臣以當世之事,由是感激,遂許先帝以驅馳。後值傾覆,受任於敗軍之際,奉命於危難之間,爾來二十有一年矣。
7.先帝知臣謹慎,故臨崩寄臣以大事也。受命以來,夙夜憂嘆,恐付託不效,以傷先帝之明,故五月渡瀘,深入不毛。今南方已定,兵甲已足,當獎率三軍,北定中原,庶竭駑鈍,攘除奸凶,興復漢室,還於舊都。此臣所以報先帝而忠陛下之職分也。至於斟酌損益,進盡忠言,則攸之、禕、允之任也。
5.親賢臣,遠小人,此先漢所以興隆也;親小人,遠賢臣,此後漢所以傾頹也。先帝在時,每與臣論此事,未嘗不嘆息痛恨於桓、靈也。侍中、尚書、長史、參軍,此悉貞良死節之臣,願陛下親之信之,則漢室之隆,可計日而待也。
分析
- 逐行讀取文字資訊。
- 解析文字資訊到集合中。
- 遍歷集合,按順序,寫出文字資訊。
實現
public class Test {
public static void main(String[] args) throws Exception {
// 需求:請將day13\\bbb\\d.txt文字資訊恢復順序。
// 實現步驟:
// 1.建立字元緩衝輸入流物件,關聯資料來源檔案路徑
BufferedReader br = new BufferedReader(new FileReader("day13\\bbb\\d.txt"));
// 2.建立ArrayList集合,限制集合中元素的型別為String型別
ArrayList<String> list = new ArrayList<>();
// 3.定義一個String型別的變數,用來儲存讀取到的行資料
String line;
// 4.迴圈讀取行資料
while ((line = br.readLine()) != null) {
// 5.在迴圈中,把讀取到的行資料儲存到ArrayList集合中
list.add(line);
}
// 6.關閉流,釋放資源
br.close();
for (String s : list) {
System.out.println(s);
}
// 7.使用Collections.sort(List<?> list)方法對ArrayList集合中的元素排序
// 按照預設規則: String類中的預設規則
Collections.sort(list);
System.out.println("=======排序後================");
for (String s : list) {
System.out.println(s);
}
// 8.建立字元緩衝輸出流物件,關聯目的地檔案路徑
BufferedWriter bw = new BufferedWriter(new FileWriter("day13\\bbb\\d.txt"));
// 9.迴圈遍歷ArrayList集合,獲取每一個元素(行資料)
for (String s : list) {
// 10.在迴圈中,把遍歷出來的元素(行資料)寫入到檔案中
bw.write(s);
bw.newLine();// 換行
}
// 11.關閉流,釋放資源
bw.close();
}
}
第四章 轉換流
知識點--字元編碼和字符集
目標
- 理解字元編碼和字符集的概念
路徑
- 字元編碼的概述
- 字符集的概述
講解
字元編碼的概述
計算機中儲存的資訊都是用二進位制數表示的,而我們在螢幕上看到的數字、英文、標點符號、漢字等字元是二進位制數轉換之後的結果。按照某種規則,將字元儲存到計算機中,稱為編碼 。反之,將儲存在計算機中的二進位制數按照某種規則解析顯示出來,稱為解碼 。比如說,按照A規則儲存,同樣按照A規則解析,那麼就能顯示正確的文字符號。反之,按照A規則儲存,再按照B規則解析,就會導致亂碼現象。
- 字元編碼
Character Encoding
: 就是一套自然語言的字元與二進位制數之間的對應規則。
字符集的概述
- 字符集
Charset
:也叫編碼表。是一個系統支援的所有字元的集合,包括各國家文字、標點符號、圖形符號、數字等。
計算機要準確的儲存和識別各種字符集符號,需要進行字元編碼,一套字符集必然至少有一套字元編碼。常見字符集有ASCII字符集、GBK字符集、Unicode字符集等。
可見,當指定了編碼,它所對應的字符集自然就指定了,所以編碼才是我們最終要關心的。
- ASCII字符集 :
- ASCII(American Standard Code for Information Interchange,美國資訊交換標準程式碼)是基於拉丁字母的一套電腦編碼系統,用於顯示現代英語,主要包括控制字元(回車鍵、退格、換行鍵等)和可顯示字元(英文大小寫字元、阿拉伯數字和西文符號)。
- 基本的ASCII字符集,使用7位(bits)表示一個字元,共128字元。ASCII的擴充套件字符集使用8位(bits)表示一個字元,共256字元,方便支援歐洲常用字元。
- ISO-8859-1字符集:
- 拉丁碼錶,別名Latin-1,用於顯示歐洲使用的語言,包括荷蘭、丹麥、德語、義大利語、西班牙語等。
- ISO-5559-1使用單位元組編碼,相容ASCII編碼。
- GBxxx字符集:
- GB就是國標的意思,是為了顯示中文而設計的一套字符集。
- GB2312:簡體中文碼錶。一個小於127的字元的意義與原來相同。但兩個大於127的字元連在一起時,就表示一個漢字,這樣大約可以組合了包含7000多個簡體漢字,此外數學符號、羅馬希臘的字母、日文的假名們都編進去了,連在ASCII裡本來就有的數字、標點、字母都統統重新編了兩個位元組長的編碼,這就是常說的"全形"字元,而原來在127號以下的那些就叫"半形"字元了。
- GBK:最常用的中文碼錶。是在GB2312標準基礎上的擴充套件規範,使用了雙位元組編碼方案,共收錄了21003個漢字,完全相容GB2312標準,同時支援繁體漢字以及日韓漢字等。
- GB18030:最新的中文碼錶。收錄漢字70244個,採用多位元組編碼,每個字可以由1個、2個或4個位元組組成。支援中國國內少數民族的文字,同時支援繁體漢字以及日韓漢字等。
- Unicode字符集 :
- Unicode編碼系統為表達任意語言的任意字元而設計,是業界的一種標準,也稱為統一碼、標準萬國碼。
- 它最多使用4個位元組的數字來表達每個字母、符號,或者文字。有三種編碼方案,UTF-8、UTF-16和UTF-32。最為常用的UTF-8編碼。
- UTF-8編碼,可以用來表示Unicode標準中任何字元,它是電子郵件、網頁及其他儲存或傳送文字的應用中,優先採用的編碼。網際網路工程工作小組(IETF)要求所有網際網路協議都必須支援UTF-8編碼。所以,我們開發Web應用,也要使用UTF-8編碼。它使用一至四個位元組為每個字元編碼,編碼規則:
- 128個US-ASCII字元,只需一個位元組編碼。
- 拉丁文等字元,需要二個位元組編碼。
- 大部分常用字(含中文),使用三個位元組編碼。
- 其他極少使用的Unicode輔助字元,使用四位元組編碼。
知識點--編碼引出的問題
目標
- 瞭解編碼引出的問題
路徑
- 演示編碼引出的問題
講解
在IDEA中,使用FileReader
讀取專案中的文字檔案。由於IDEA的設定,都是預設的UTF-8
編碼,所以沒有任何問題。但是,當讀取Windows系統中建立的文字檔案時,由於Windows系統的預設是GBK編碼,就會出現亂碼。
public class Test {
public static void main(String[] args) throws Exception{
/*
idea預設編碼utf8,window有些系統預設的是gbk編碼
問題: 如果使用字元輸入流讀取含有中文的gbk編碼檔案,就會出現亂碼?
原因: 因為存的時候使用的是gbk編碼,取的時候使用的是utf8編碼
*/
// 建立一個字元輸入流物件,關聯資料來源檔案路徑
FileReader fr = new FileReader("day13\\ccc\\gbk.txt");
// 定義一個int型別的變數,用來儲存讀取到的字元資料
int c;
// 迴圈讀取
while ((c = fr.read()) != -1){
System.out.println((char) c);// 亂碼
}
// 關閉流,釋放資源
fr.close();
}
}
輸出結果:
���
那麼如何讀取GBK編碼的檔案呢?
知識點--InputStreamReader類
目標
- 掌握InputStreamReader類 的使用
路徑
- InputStreamReader類的概述
- InputStreamReader類的構造方法
- InputStreamReader類指定編碼讀取
講解
InputStreamReader類的概述
轉換流java.io.InputStreamReader
,是Reader的子類,是從位元組流到字元流的橋樑。它讀取位元組,並使用指定的字符集將其解碼為字元。它的字符集可以由名稱指定,也可以接受平臺的預設字符集。
InputStreamReader類的構造方法
InputStreamReader(InputStream in)
: 建立一個使用預設字符集的字元流。InputStreamReader(InputStream in, String charsetName)
: 建立一個指定字符集的字元流。
構造舉例,程式碼如下:
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
InputStreamReader類指定編碼讀取
public class Test {
public static void main(String[] args)throws Exception {
// 建立字元轉換輸入流物件,關聯資料來源檔案路徑 平臺預設編碼,utf8
InputStreamReader isr = new InputStreamReader(new FileInputStream("day13\\ccc\\gbk.txt"));
// 定義一個int型別的變數,用來儲存讀取到的字元資料
int c;
// 迴圈讀取
while ((c = isr.read()) != -1){
System.out.println((char) c);// 亂碼
}
// 關閉流,釋放資源
isr.close();
System.out.println("========================================");
// 建立字元轉換輸入流物件,關聯資料來源檔案路徑 指點編碼,gbk
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("day13\\ccc\\gbk.txt"),"gbk");
// 定義一個int型別的變數,用來儲存讀取到的字元資料
int c2;
// 迴圈讀取
while ((c2 = isr2.read()) != -1){
System.out.println((char) c2);// 中國
}
// 關閉流,釋放資源
isr2.close();
}
}
知識點--OutputStreamWriter類
目標
- 掌握OutputStreamWriter類的使用
路徑
- OutputStreamWriter類的概述
- OutputStreamWriter類的構造方法
- OutputStreamWriter類指定編碼寫
講解
OutputStreamWriter類的概述
轉換流java.io.OutputStreamWriter
,是Writer的子類,是從字元流到位元組流的橋樑。使用指定的字符集將字元編碼為位元組。它的字符集可以由名稱指定,也可以接受平臺的預設字符集。
OutputStreamWriter類的構造方法
OutputStreamWriter(OutputStream in)
: 建立一個使用預設字符集的字元流。 idea預設的是utf8OutputStreamWriter(OutputStream in, String charsetName)
: 建立一個指定字符集的字元流。
構造舉例,程式碼如下:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
OutputStreamWriter類指定編碼讀取
public class Test {
public static void main(String[] args) throws Exception{
// 指點gbk編碼寫出字元資料
// 建立字元轉換輸出流,關聯目的地檔案路徑
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day13\\ccc\\a.txt"),"gbk");
// 寫出資料
osw.write("中國");// 4個位元組
// 關閉流,釋放資源
osw.close();
System.out.println("+==========================================");
// 使用平臺預設編碼寫出字元資料
// 建立字元轉換輸出流,關聯目的地檔案路徑
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("day13\\ccc\\b.txt"));
// 寫出資料
osw2.write("中國");// 6個位元組
// 關閉流,釋放資源
osw2.close();
}
}
轉換流理解圖解
轉換流是位元組與字元間的橋樑!
實操--轉換檔案編碼
需求
- 將GBK編碼的文字檔案,轉換為UTF-8編碼的文字檔案。
分析
- 指定GBK編碼的轉換流,讀取文字檔案。
- 使用UTF-8編碼的轉換流,寫出文字檔案。
實現
public class Test {
public static void main(String[] args) throws Exception{
/*
需求:將GBK編碼的文字檔案,轉換為UTF-8編碼的文字檔案。
*/
// 1.建立轉換輸入流物件,關聯資料來源檔案路徑,指定編碼為gbk
InputStreamReader isr = new InputStreamReader(new FileInputStream("day13\\ccc\\gbk.txt"),"gbk");
// 2.建立轉換輸出流物件,關聯目的地檔案路徑,指點編碼為utf8
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day13\\ccc\\utf8.txt"),"utf8");
// 3.定義一個int型別的變數,用來存放讀取到的字元資料
int len;
// 4.迴圈讀取
while ((len = isr.read()) != -1) {
// 5.在迴圈中,寫出資料
osw.write(len);
}
// 6.關閉流,釋放資源
osw.close();
isr.close();
}
}
第五章 序列化
知識點--序列化和反序列化的概念
目標
- 理解序列化和反序列化的概念
路徑
- 序列化
- 反序列化
講解
Java 提供了一種物件序列化的機制。用一個位元組序列可以表示一個物件,該位元組序列包含該物件的資料
、物件的型別
和物件中儲存的屬性
等資訊。位元組序列寫出到檔案之後,相當於檔案中持久儲存了一個物件的資訊。
反之,該位元組序列還可以從檔案中讀取回來,重構物件,對它進行反序列化。物件的資料
、物件的型別
和物件中儲存的資料
資訊,都可以用來在記憶體中建立物件。看圖理解序列化:
知識點--ObjectOutputStream類
目標
- 掌握ObjectOutputStream類的使用
路徑
- ObjectOutputStream類的概述
- ObjectOutputStream類構造方法
- ObjectOutputStream類序列化操作
講解
ObjectOutputStream類的概述
java.io.ObjectOutputStream
類,將Java物件的原始資料型別寫出到檔案,實現物件的持久儲存。
ObjectOutputStream類構造方法
public ObjectOutputStream(OutputStream out)
: 建立一個指定OutputStream的ObjectOutputStream。
構造舉例,程式碼如下:
FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
ObjectOutputStream類序列化操作
- 一個物件要想序列化,必須滿足兩個條件:
- 該類必須實現
java.io.Serializable
介面,Serializable
是一個標記介面
public class Person implements Serializable {
private String name;
private int age;
public Animal anl;// 寵物
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//...set get ...toString...
}
2.寫出物件方法
public final void writeObject (Object obj)
: 將指定的物件寫出。
public class Test {
public static void main(String[] args)throws Exception {
// 建立Person物件
Person p = new Person("張三", 18);
// 需求:將p物件寫入到 day13\\ddd\\a.txt 檔案中
// 建立序列化流物件,關聯目的地檔案路徑
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day13\\ddd\\a.txt"));
// 寫出物件
oos.writeObject(p);
// 關閉流,釋放資源
oos.close();
}
}
知識點--ObjectInputStream類
目標
- 掌握ObjectInputStream類的使用
路徑
- ObjectInputStream類的概述
- ObjectInputStream類構造方法
- ObjectInputStream類反序列化操作
講解
ObjectInputStream類的概述
ObjectInputStream反序列化流,將之前使用ObjectOutputStream序列化的原始資料恢復為物件。
ObjectInputStream類構造方法
public ObjectInputStream(InputStream in)
: 建立一個指定InputStream的ObjectInputStream。
ObjectInputStream類反序列化操作1
如果能找到一個物件的class檔案,我們可以進行反序列化操作,呼叫ObjectInputStream
讀取物件的方法:
public final Object readObject ()
: 讀取一個物件。
public class Test {
public static void main(String[] args) throws Exception{
// 反序列化:讀取之前序列化的物件
// 1.建立反序列化流物件,關聯資料來源檔案路徑
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day13\\ddd\\a.txt"));
// 2.讀取一個物件
Object obj = ois.readObject();
System.out.println(obj);
// 3.關閉流,釋放資源
ois.close();
}
}
知識點--序列化和反序列化注意事項
目標
- 理解序列化和反序列化注意事項
路徑
- 序列化的注意事項
- 反序列化的注意事項
講解
序列化的注意事項
- 該類必須實現
java.io.Serializable
介面,Serializable
是一個標記介面,不實現此介面的類將不會使任何狀態序列化或反序列化,會丟擲NotSerializableException
。 - 該類的所有屬性必須是可序列化的。
- 如果有一個屬性不需要可序列化的,則該屬性必須註明是瞬態的,使用
transient
關鍵字修飾。
反序列化的注意事項
-
對於JVM可以反序列化物件,它必須是能夠找到class檔案的類。如果找不到該類的class檔案,則丟擲一個
ClassNotFoundException
異常。 -
另外,當JVM反序列化物件時,能找到class檔案,但是class檔案在序列化物件之後發生了修改,那麼反序列化操作也會失敗,丟擲一個
InvalidClassException
異常。發生這個異常的原因如下:- 該類的序列版本號與從流中讀取的類描述符的版本號不匹配
- 該類包含未知資料型別
- 該類沒有可訪問的無引數構造方法
Serializable
介面給需要序列化的類,提供了一個序列版本號。serialVersionUID
該版本號的目的在於驗證序列化的物件和對應類是否版本匹配。
public class Person implements Serializable {
static final long serialVersionUID = 4L;
String name;
transient int age;
Animal anl;// 寵物
String sex;
public Person() {
}
public Person(String name, int age, Animal anl) {
this.name = name;
this.age = age;
this.anl = anl;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", anl=" + anl +
'}';
}
}
public class Animal implements Serializable {
String name;
public Animal(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
public class Test1 {
public static void main(String[] args)throws Exception {
/*
序列化的注意事項:
1.要求序列化的物件所屬的類實現Serializable介面,標記該類的物件可以被序列化
2.要求序列化物件的所有屬性值也是可以被序列化的
3.如果物件的某個屬性不想被序列化,那麼就得使用transient關鍵字把該屬性表明為瞬態的
*/
// 序列化
// 建立Animal物件
Animal anl = new Animal("旺財");
// 建立Person物件
Person p = new Person("張三", 18,anl);
// 需求:將p物件寫入到 day13\\ddd\\a.txt 檔案中
// 建立序列化流物件,關聯目的地檔案路徑
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day13\\ddd\\b.txt"));
// 寫出物件
oos.writeObject(p);
// 關閉流,釋放資源
oos.close();
}
}
public class Test2 {
public static void main(String[] args) throws Exception{
/*
反序列化注意事項:
1.對於JVM可以反序列化物件,它必須是能夠找到class檔案的類。
如果找不到該類的class檔案,則丟擲一個 ClassNotFoundException 異常。
2.當JVM反序列化物件時,能找到class檔案,但是class檔案在序列化物件之後發生了修改,
那麼反序列化操作也會失敗,丟擲一個InvalidClassException異常
*/
// 反序列化:
// 1.建立反序列化流物件,關聯資料來源檔案路徑
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day13\\ddd\\b.txt"));
// 2.讀取一個物件
Object obj = ois.readObject();
System.out.println(obj);
// 3.關閉流,釋放資源
ois.close();
}
}
實操--序列化集合
需求
- 將存有多個自定義物件的集合序列化操作,儲存到
list.txt
檔案中。 - 反序列化
list.txt
,並遍歷集合,列印物件資訊。
分析
- 把若干學生物件 ,儲存到集合中。
- 把集合序列化。
- 反序列化讀取時,只需要讀取一次,轉換為集合型別。
- 遍歷集合,可以列印所有的學生資訊
實現
public class Student implements Serializable {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) throws Exception {
/*
需求
1. 將存有多個自定義物件的集合序列化操作,儲存到list.txt檔案中。
2. 反序列化list.txt ,並遍歷集合,列印物件資訊。
*/
// 建立反序列化流物件,關聯資料來源檔案路徑
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day13\\ddd\\list.txt"));
// 重構集合物件
Object obj = ois.readObject();
// 關閉流,釋放資源
ois.close();
// 遍歷集合,列印物件資訊。
ArrayList<Student> list = (ArrayList<Student>)obj;
for (Student stu : list) {
System.out.println(stu);
}
}
// 序列化:
private static void method01() throws IOException {
// 1.建立ArrayList集合,限制集合中元素的型別為Student型別
ArrayList<Student> list = new ArrayList<>();
// 2.建立多個Student物件
Student stu1 = new Student("張三", 18);
Student stu2 = new Student("李四", 12);
Student stu3 = new Student("王五", 13);
Student stu4 = new Student("趙六", 16);
// 3.把Student物件新增到集合中
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
// 4.建立序列化流物件,關聯目的地檔案路徑
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day13\\ddd\\list.txt"));
// 5.序列化集合物件
oos.writeObject(list);
// 6.關閉流,釋放資源
oos.close();
}
}
第六章 列印流
目標
- 理解列印流的使用
路徑
- 列印流的概述
- 列印流的使用
講解
列印流的概述
平時我們在控制檯列印輸出,是呼叫print
方法和println
方法完成的,這兩個方法都來自於java.io.PrintStream
類,該類能夠方便地列印各種資料型別的值,是一種便捷的輸出方式。
列印流的使用
public PrintStream(String fileName)
: 使用指定的檔名建立一個新的列印流。
構造舉例,程式碼如下:
PrintStream ps = new PrintStream("ps.txt");
System.out
就是PrintStream
型別的,只不過它的流向是系統規定的,列印在控制檯上。不過,既然是流物件,我們就可以玩一個"小把戲",將資料輸出到指定文字檔案中。
public class Test {
public static void main(String[] args) throws Exception{
/*
列印流PrintStream:
- 列印流的概述
概述: java.io.PrintStream類最終是繼承了OutputStream,所以它其實本質也是一個位元組輸出流
特點: 能夠方便地列印各種資料型別的值,是一種便捷的輸出方式。
- 列印流的使用
構造方法:
public PrintStream(String fileName): 使用指定的檔名建立一個新的列印流。
特有方法:
void print(任意型別的資料) 不換行列印
void println(任意型別的資料) 換行列印
*/
// 建立列印流物件,關聯目的地檔案路徑
PrintStream ps = new PrintStream("day13\\eee\\a.txt");
// 列印資料
ps.print(100);
ps.print(2.3);
ps.print('b');
ps.print(false);
ps.println();// 換行
ps.println(97);
ps.println(3.14);
ps.println('a');
ps.println(true);
ps.println("jack");
// 關閉流,釋放資源
ps.close();
System.out.println("========================練習--玩一玩======================");
// 獲取系統的列印流物件:
System.out.println(100);// 列印到控制檯 100
// 需求: 把System.out.println()列印的目的地從控制檯改成day13\\eee\\b.txt
PrintStream ps2 = System.out;
ps2.println(100);// 列印到控制檯 100
// 修改系統的列印流物件:
// 建立列印流物件,關聯目的地檔案路徑
PrintStream ps3 = new PrintStream("day13\\eee\\b.txt");
// 修改System.out的值
System.setOut(ps3);
System.out.println(100);// 列印b.txt檔案中
}
}
第七章 裝飾設計模式
目標
- 會使用裝飾設計模式
路徑
- 裝飾模式概述
- 案例演示
講解
裝飾模式概述
在我們今天所學的緩衝流中涉及到java的一種設計模式,叫做裝飾模式,我們來認識並學習一下這個設計模式。
裝飾模式指的是在不改變原類, 不使用繼承的基礎上,動態地擴充套件一個物件的功能。
裝飾模式遵循原則:
- 裝飾類和被裝飾類必須實現相同的介面
- 在裝飾類中必須傳入被裝飾類的引用
- 在裝飾類中對需要擴充套件的方法進行擴充套件
- 在裝飾類中對不需要擴充套件的方法呼叫被裝飾類中的同名方法
案例演示
準備環境:
- 編寫一個Star介面, 提供sing 和 dance抽象方法
- 編寫一個LiuDeHua類,實現Star介面,重寫抽象方法
public interface Star {
public void sing();
public void dance();
}
public class LiuDeHua implements Star {
@Override
public void sing() {
System.out.println("劉德華在唱忘情水...");
}
@Override
public void dance() {
System.out.println("劉德華在跳街舞...");
}
}
需求:
在不改變原類的基礎上對LiuDeHua類的sing方法進行擴充套件
實現步驟:
- 編寫一個LiuDeHuaWarpper類, 實現Star介面,重寫抽象方法
- 提供LiuDeHuaWarpper類的有參構造, 傳入LiuDeHua類物件
- 在LiuDeHuaWarpper類中對需要增強的sing方法進行增強
- 在LiuDeHuaWarpper類對不需要增強的方法呼叫LiuDeHua類中的同名方法
實現程式碼如下
LiuDeHua類: 被裝飾類
LiuDeHuaWarpper類: 我們稱之為裝飾類
/*
裝飾模式遵循原則:
裝飾類和被裝飾類必須實現相同的介面
在裝飾類中必須傳入被裝飾類的引用
在裝飾類中對需要擴充套件的方法進行擴充套件
在裝飾類中對不需要擴充套件的方法呼叫被裝飾類中的同名方法
*/
public class LiuDeHuaWarpper implements Star {
// 存放被裝飾類的引用
private LiuDeHua liuDeHua;
// 通過構造器傳入被裝飾類物件
public LiuDeHuaWarpper(LiuDeHua liuDeHua){
this.liuDeHua = liuDeHua;
}
@Override
public void sing() {
// 對需要擴充套件的方法進行擴充套件增強
System.out.println("劉德華在鳥巢的舞臺上演唱忘情水.");
}
@Override
public void dance() {
// 不需要增強的方法呼叫被裝飾類中的同名方法
liuDeHua.dance();
}
}
測試結果
public static void main(String[] args) {
// 建立被裝飾類物件
LiuDeHua liuDeHua = new LiuDeHua();
// 建立裝飾類物件,被傳入被裝飾類
LiuDeHuaWarpper liuDeHuaWarpper = new LiuDeHuaWarpper(liuDeHua);
// 呼叫裝飾類的相關方法,完成方法擴充套件
liuDeHuaWarpper.sing();
liuDeHuaWarpper.dance();
}
小結
裝飾模式可以在不改變原類的基礎上對類中的方法進行擴充套件增強,實現原則為:
- 裝飾類和被裝飾類必須實現相同的介面
- 在裝飾類中必須傳入被裝飾類的引用
- 在裝飾類中對需要擴充套件的方法進行擴充套件
- 在裝飾類中對不需要擴充套件的方法呼叫被裝飾類中的同名方法
第八章 commons-io工具包
目標
- 掌握commons-io工具包的使用
路徑
- commons-io工具包的概述
- commons-io工具包的使用
- commons-io工具包常用api介紹
講解
commons-io工具包的概述
commons-io是apache開源基金組織提供的一組有關IO操作的類庫,可以挺提高IO功能開發的效率。commons-io工具包提供了很多有關io操作的類,見下表:
包 | 功能描述 |
---|---|
org.apache.commons.io | 有關Streams、Readers、Writers、Files的工具類 |
org.apache.commons.io.input | 輸入流相關的實現類,包含Reader和InputStream |
org.apache.commons.io.output | 輸出流相關的實現類,包含Writer和OutputStream |
org.apache.commons.io.serialization | 序列化相關的類 |
commons-io工具包的使用
步驟:
- 下載commons-io相關jar包;http://commons.apache.org/proper/commons-io/
- 把commons-io-2.6.jar包複製到指定的Module的lib目錄中
- 將commons-io-2.6.jar加入到classpath中
commons-io工具包的使用
- commons-io提供了一個工具類 org.apache.commons.io.IOUtils,封裝了大量IO讀寫操作的程式碼。其中有兩個常用方法:
- public static int copy(InputStream in, OutputStream out); 把input輸入流中的內容拷貝到output輸出流中,返回拷貝的位元組個數(適合檔案大小為2GB以下)
- public static long copyLarge(InputStream in, OutputStream out);把input輸入流中的內容拷貝到output輸出流中,返回拷貝的位元組個數(適合檔案大小為2GB以上)
檔案複製案例演示:
// IOUtils工具類拷貝檔案
private static void method01() throws IOException {
// 拷貝一個檔案
// 建立輸入流和輸出流物件
FileInputStream fis = new FileInputStream("day13\\aaa\\hb.jpg");
FileOutputStream fos = new FileOutputStream("day13\\fff\\hb1.jpg");
// 拷貝
IOUtils.copy(fis,fos);
// 關閉流,釋放資源
fos.close();
fis.close();
}
- commons-io還提供了一個工具類org.apache.commons.io.FileUtils,封裝了一些對檔案操作的方法:
- public static void copyFileToDirectory(final File srcFile, final File destFile) //複製檔案到另外一個目錄下。
- public static void copyDirectoryToDirectory( file1 , file2 );//複製file1目錄到file2位置。
案例演示:
public static void main(String[] args) throws IOException {
// 拷貝資料夾
/*File file1 = new File("day13\\aaa");
File file2 = new File("day13\\fff");
FileUtils.copyDirectoryToDirectory(file1,file2);*/
// 拷貝檔案
File file3 = new File("day13\\aaa\\hbCopy1.jpg");
File file4 = new File("day13\\fff");
FileUtils.copyFileToDirectory(file3,file4);
}
總結
練習:
- IO異常的處理(jdk7前,jdk7)
- 使用Properties讀取配置檔案中的資料到程式中
- 字元緩衝流的讀一行資料和寫換行
- 轉換檔案編碼
- 序列化集合
- 裝飾者設計模式案例
- commons-io2個例題
- 能夠使用Properties的load方法載入檔案中配置資訊
load(InputStream is)
load(Reader r)
- 能夠使用位元組緩衝流讀取資料到程式
高效讀寫資料
BufferedInputStream: read() 讀一個位元組 read(byte[] bys)讀位元組陣列
- 能夠使用位元組緩衝流寫出資料到檔案
BufferedOutputStream: write()寫一個位元組 write(byte[] b,int off,int len)寫指定範圍的位元組陣列
- 能夠明確字元緩衝流的作用和基本用法
字元緩衝流的作用: 高效讀寫
基本用法:
BufferedReader: read() 讀一個字元,read(char[] chs)讀字元陣列
BufferedWriter: write() 寫一個字元 write(char[] chs,int off,int len)寫指定範圍的字元陣列
write(String str) 寫字串
- 能夠使用緩衝流的特殊功能
字元緩衝輸入流: readLine()讀一行
字元緩衝輸出流: newLine()根據系統寫一個行分隔符(換行)
- 能夠闡述編碼表的意義
定義字元和二進位制數的對應的規則
編碼: 字元--->二進位制數
解碼: 二進位制數--->字元
- 能夠使用轉換流讀取指定編碼的文字檔案
InputStreamReader(InputStream is,String charsetName)
- 能夠使用轉換流寫入指定編碼的文字檔案
OutputStreamWriter(OutputStream os,String charsetName)
- 能夠使用序列化流寫出物件到檔案
ObjectOutputStream: writeObject(Object obj)
- 能夠使用反序列化流讀取檔案到程式中
ObjectInputStream: readObject()
- 能夠理解裝飾模式的實現步驟
1.裝飾類和被裝飾類需要實現共同的介面
2.在裝飾類中需要獲取被裝飾類的引用(成員變數)
3.在裝飾類中對需要增強的方法進行增強
4.在裝飾類中對不需要增強的方法,就使用被裝飾類的引用呼叫被裝飾類的原有方法
- 能夠使用commons-io工具包
1.拷貝commons-io工具包到模組下的lib資料夾中
2.把commons-io工具包新增到classpath路徑中
3.使用commons-io工具包中的常用工具類: IOUtils,FileUtils
IOUtils:
copy(InputStream in, OutputStream out);// 適合2gb以下
copyLarge(InputStream in, OutputStream out);// 適合2gb以上
FileUtils:
copyDirectoryToDirectory(File file1 ,File file2 );// 拷貝資料夾到另一個資料夾
copyFileToDirectory(File file1 ,File file2 );// 拷貝檔案到另一個資料夾