概述:
1、IO流:即Input Output的縮寫。
2、特點:
1)IO流用來處理設備間的數據傳輸。
2)Java對數據的操作是通過流的方式。
3)Java用於操作流的對象都在IO包中。
4)流按操作數據分爲兩種:字節流和字符流。
5)流按流向分爲:輸入流和輸出流。
注意:流只能操作數據,而不能操作文件。
3、IO流的常用基類:
1)字節流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
注:此四個類派生出來的子類名稱都是以父類名作爲子類名的後綴,以前綴爲其功能;如InputStream子類FileInputStream和Reader子類FileReader
第一節 字符流
一、簡述:
1、字符流中的對象融合了編碼表。使用的是默認的編碼,即當前系統的編碼。
2、字符流只用於處理文字數據,而字節流可以處理媒體數據。
3、既然IO流是用於操作數據的,那麼數據的最常見體現形式是文件。專門用於操作文件的Writer子類對象:FileWriter ---> 後綴名爲父類名,前綴名爲流對象的功能。
二、字符流的讀寫
1、寫入字符流:
1)創建一個FileWriter對象,該對象一被初始化,就必須要明確被操作的文件。且該目錄下如果已有同名文件,則同名文件將被覆蓋。其實該步就是在明確數據要存放的目的地。
2)調用write(String s)方法,將字符串寫入到流中。
3)調用flush()方法,刷新該流的緩衝,將數據刷新到目的地中。
4)調用close()方法,是關閉流資源。但是關閉前會刷新一次內部的緩衝數據,並將數據刷新到目的地中。
注:
--->close()和flush()區別:flush()刷新後,流可以繼續使用;而close()刷新後,將會關閉流,不可再寫入字符流。
--->其實java自身不能寫入數據,而是調用系統內部方式完成數據的書寫,使用系統資源後,一定要關閉資源。
--->數據的續寫是通過構造函數 FileWriter(String s,boolean append),根據給定文件名及指示是否附加寫入數據的boolean值來構造FileWriter對象。
2、讀取字符流:
1)創建一個文件讀取流對象,和指定名稱的文件相關聯。要保證該文件已經存在,若不存在,將會發生異常FileNotFoundException。
2)調用讀取流對象的read()方法。read():一次讀一個字符,且會繼續往下讀。
第一種方式:讀取單個字符。第二種方式:通過字符數組進行讀取。
3)讀取後要將流資源關閉。
示例:
二、文件的拷貝:
原理:其實就是將一個磁盤下的文件數據存儲到另一個磁盤的一個文件中
步驟:
1、在D盤上創建一個文件,用於存儲E盤文件中的數據
2、定義讀取流和E盤文件關聯
3、通過不斷讀寫完成數據存儲
方式一:讀取一個字符,存入一個字符
方式二:先將讀取的數據存入到內存中,再將存入的字符取出寫入D盤
4、關閉流資源:輸入流資源和輸出流資源。
示意圖:
示例:
三、BufferedWriter和BufferedReader:字符流緩衝區
1、緩衝區的出現:提高了流的讀寫效率,所以在緩衝區創建前,要先創建流對象,即先將流對象初始化到構造函數中。
2、緩衝技術原理:此對象中封裝了數組,將數據存入,在一次性取出。
3、寫入流緩衝區BufferedWriter的步驟:
1)創建一個字符寫入流對象
2)爲提高字符寫入流效率,加入緩衝技術,只要將需要被提高效率的流對象作爲參數傳遞給緩衝區的構造函數即可。
注意,只要用到緩衝去就需要刷新。
3)其實關閉緩衝區就是在關閉緩衝區中的流對象。
--->該緩衝區中提供了一個跨平臺的換行符:newLine()。
4、讀取流緩衝區BufferedReader的步驟:
1)創建一個讀取流對象和文件關聯
2)爲提高效率,加入緩衝技術,將字符讀取流對象作爲參數傳遞給緩衝對象的構造函數。
3)該緩衝區提供了一個一次讀一行的方法readLine(),方便與對文本數據的獲取,當返回null時,表示讀到文件末尾。
readLine()方法返回的時只返回回車符之前的數據內容,並不返回回車符,即讀取的內容中不包含任何行終止符(回車符和換行符)。
--->readLine()方法原理:無論是讀一行,或讀取多個字符,其實最終都是在硬盤上一個一個讀取。所以最終使用的還是read方法一次讀一個。
示意圖:
5、使用緩衝技術拷貝文件,提高效率:
示例:
6、自定義BufferedReader:
原理:可根據BufferedReader類中特有發那個發readLine()的原理,自定義一個類中包含相同功能的方法
步驟:a.初始化自定義的類,加入流對象。
b.定義一個臨時容器,原BufferedReader封裝的是字符數組,此類中可定義一個StringBuilder的容器,最終可實現字符串的提取。
7、LineNumberReader
在BufferedReader中有個直接的子類LineNumberReader,其中有特有的方法獲取和設置行號:setLineNumber()和getLineNumber()
示例:
通過這個例子,就可以引出裝飾設計模式
四、裝飾設計模式:
1、簡述:當想對已有對象進行功能增強是,可定義類:將已有對象傳入,基於已有對象的功能,並提供加強功能,那麼自定義的該類稱之爲裝飾類。即對原有類進行了優化。
2、特點:裝飾類通常都會通過構造方法接收被裝飾的對象,並基於被裝飾的對i型那個的功能提供更強的功能。
3、裝飾和繼承的區別:
1)裝飾模式比繼承要靈活,通過避免了繼承體系的臃腫,且降低了類與類間的關係。
2)裝飾類因爲增強已有對象,具備的功能和已有的是相同的,只不過提供了更強的功能,所以裝飾類和被裝飾的類通常都是屬於一個體系。
3)從繼承結構轉爲組合結構。
注:在定義類的時候,不要以繼承爲主;可通過裝飾設計模式進行增強類功能。靈活性較強,當裝飾類中的功能不適合,可再使用被裝飾類的功能。
要繼承相應的父類,就需要將所有的抽象方法實現,或交給子類實現。
示例:其中MyBufferedReader的例子就是最好的裝飾設計模式的例子。
第二節 字節流
一、概述:
1、字節流和字符流的原理是相似的,只不過字節流可以對媒體進行操作。
2、由於媒體數據中都是以字節存儲的,所以,字節流對象可直接對媒體進行操作,而不用再進行刷流動作。
3、讀寫字節流:InputStream ---> 輸入流(讀)
OutputStream ---> 輸出流(寫)
4、爲何不用進行刷流動作:
因爲字節流操作的是字節,即數據的最小單位,不需要像字符流一樣要進行轉換爲字節。可直接將字節寫入到指定文件中,但是需要在寫代碼的時候,如果有字符串,要將字符串轉爲字節數組再進行操作。
5、特有方法:
int available() ---> 放回數據字節的長度,包含終止符
在定義字節數組長度的時候,可以用到這個方法:byte[] = new byte[fos.available()] (fos爲字節流對象)
但是,對於這個方法要慎用,如果字節過大(幾個G),那麼如此大的數組就會損壞內存,超過jvm所承受的大小(指定內存爲64M)。
二、拷貝媒體文件:
1、思路:
1)用字節流讀取流對象和媒體文件相關聯
2)用字節寫入流對象,創建一個媒體文件,用於存儲獲取到的媒體文件數據
3)通過循環讀寫,完成數據的存儲
4)關閉資源
示例:
三、字節流緩衝區:
1、讀寫特點:
read():會將字節byte型值提升爲int型值
write():會將int型強轉爲byte型,即保留二進制數的最後八位。
2、原理:將數據拷貝一部分,讀取一部分,循環,直到數據全部讀取完畢。
1)先從數據中抓取固定數組長度的字節,存入定義的數組中,再通過然後再通過read()方法讀取數組中的元素,存入緩衝區
2)循環這個動作,知道最後取出一組數據存入數組,可能數組並未填滿,同樣也取出包含的元素
3)每次取出的時候,都有一個指針在移動,取到數組結尾就自動回到數組頭部,這樣指針在自增
4)取出的時候,數組中的元素再減少,取出一個,就減少一個,直到減到0即數組取完
5)到了文件的結尾處,存入最後一組數據,當取完數組中的元素,就會減少到0,這是全部數據就取完了
3、示意圖:
4、自定義字節流緩衝區:
思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0
3、每次將字節數據存入元素要先將數組中的元素取完
注:取出的是byte型,返回的是int型,這裏存在提升的動作,
當byte中的八位全爲1的時候是byte的-1,提升爲int類型,就變爲int型的-1,,read循環條件就結束了
變爲-1的原因是由於在提升時,將byte的八位前都補的是1,即32位的數都是1,即爲int型的-1了。
如何保證提升後的最後八位仍爲1呢?就需要將前24位補0,就可以保留原字節數據不變,又可以避免轉爲int型出現-1的情況;
那麼要如何做呢?
這就需要將提升爲int的數據和前24位爲0,後八位仍爲原字節數據的這個值做與運算。即和255做與運算即可
一、讀取鍵盤錄入:
1、System.out:對應的是標準的輸出設備 ---> 控制檯
System.in: 對應的是標準的輸出設備 ---> 鍵盤
in字段返回的是IputStream類型,其read()方法在未讀取到數據時,一直阻塞
2、如何使用readLine方法完成鍵盤錄入一行數據的讀取?
說明:readLine()方法是字符流中BufferedReader類的方法;而鍵盤錄入的read()方法是字節流IputStream的方法
那麼,如何將字節流轉成字符流在使用字符流緩衝區的readLine()的方法呢?這就需要使用InputStreamReader對象。
3、轉換流:
3.1、InputStreamReader:讀取轉換流
a.獲取鍵盤錄入對象: ---> InputStream in = System.in;
b.將字節流對象轉換成字符流對象,使用轉換流InputStreamReader: ---> InputStreamReader isr = new InputStreamReader(in);
c.爲提高效率,將字符串進行緩衝區技術操作,使用BufferedReader: ---> BufferedReader bufw = new BufferedReader(isr);
d.之後就可以使用readLine()方法讀取錄入的一行數據了。
3.2、OutputStreamWriter:寫入轉換流 ---> 和讀取轉換流同理,即使用對應的Writer的子類
一、讀取鍵盤錄入:
1、System.out:對應的是標準的輸出設備 ---> 控制檯
System.in: 對應的是標準的輸出設備 ---> 鍵盤
in字段返回的是IputStream類型,其read()方法在未讀取到數據時,一直阻塞
2、如何使用readLine方法完成鍵盤錄入一行數據的讀取?
說明:readLine()方法是字符流中BufferedReader類的方法;而鍵盤錄入的read()方法是字節流IputStream的方法
那麼,如何將字節流轉成字符流在使用字符流緩衝區的readLine()的方法呢?這就需要使用InputStreamReader對象。
3、轉換流:
3.1、InputStreamReader:讀取轉換流
a.獲取鍵盤錄入對象: ---> InputStream in = System.in;
b.將字節流對象轉換成字符流對象,使用轉換流InputStreamReader: ---> InputStreamReader isr = new InputStreamReader(in);
c.爲提高效率,將字符串進行緩衝區技術操作,使用BufferedReader: ---> BufferedReader bufw = new BufferedReader(isr);
d.之後就可以使用readLine()方法讀取錄入的一行數據了。
3.2、OutputStreamWriter:寫入轉換流 ---> 和讀取轉換流同理,即使用對應的Writer的子類
二、流操作規律:
1、將鍵盤錄入的數據打印到屏幕上
源:鍵盤錄入 ---> 目的:控制檯
2、將鍵盤錄入的數據存入文件中
源:鍵盤錄入 ---> 目的:文件
---> 創建一個寫入(輸出)流對象文件作爲參數,傳給轉換流(橋樑):OutputStreamWriter
3、要將一個文件的數據打印到控制檯:
源:文件 ---> 目的:控制檯
---> 創建一個讀取(輸入)流對象文件作爲參數,傳給轉換流(橋樑):InputStreamReader
4、流操作基本規律:
在寫代碼過程中最痛苦的是:流對象較多,不知道該用哪一個,這就需要有如下的三點明確:
第一、明確源和目的:
源 :輸入流 ---> InputStream和Reader
目的:輸出流 ---> OutputStream和Writer
第二、明確操作的數據是否爲純文本:
是:字符流
否:字節流
第三、當體系明確後,再明確具體要用哪個具體對象,通過設備來區分
源 設 備:內存(ArrayStream)、硬盤(FileStream)、鍵盤(System.in)
目的設備:內存(ArrayStream)、硬盤(FileStream)、控制檯(System.out)
可以在下面的需求體現中,發現元和目的是對應的,都爲對應體系的一對對象。
5、需求體現:
5.1、將一個文本文件中的數據存入另一個文件,即複製文本
1)源:使用讀取流:InputStream和Reader
---> 明確體系:是否爲文本文件:是,選Reader
---> 明確設備:明確要使用該體系中哪個對象:硬盤上一個文件。即Reader體系中可操作文件的對象:FileReader
---> 是否需要高效:是,加入Reader體系中的緩衝區:BufferedReader
2)目的:使用寫入流:OutputStream和Writer
---> 明確體系:是否爲文本文件:是,選Writer
---> 明確設備:明確要使用該體系中哪個對象:硬盤上一個文件。即Writer體系中可操作對象的是FileWriter
---> 是否需要高效:是,加入Writer體系中的緩衝區:BufferedWriter
1)源:InputStream和Reader
圖片:字節流 ---> InputStream
設備:硬盤上的文件 ---> FileInputStream
2)目的:OutoutStream和Writer
圖片:字節流 ---> OutputStream
設備:硬盤上的文件 ---> FileOutputStream
5.3、將鍵盤錄入數據保存到一個文件中。
這個需求中有源和目的,都存在:
1)源:InputStream和Reader
純文本?是,字符流 ---> Reader
設備:鍵盤 ---> 對應的是System.in,是字節流
---> 爲方便操作鍵盤的文本數據,轉成字符流;按照字符串操作是最方便的,所以既然明確了Reader,那麼就將產生字節流System.in轉爲字符流Reader,就需用Reader體系中的轉換流InputStreamReader
即 InutStreamReader isr = new InputStreamReader(System.in);
---> 是否需要高效:是,加入Reader體系中的緩衝區:BufferedReader;
即BufferedReader bufr = new BufferedReader(isr);
2)目的:OutoutStream和Writer
純文本?是,字符流 ---> Writer
設備:硬盤上的文件 ---> FileWriter
---> 是否需要高效:是,加入Writer體系中的緩衝區:BufferedWriter;
即 BufferedWriter bufw = new BufferedWriter(fw);
5.4、擴展:
想要將錄入的數據按指定編碼表(UTF-8),將數據存入文件
1)源:InputStream和Reader
純文本?是,字符流 ---> Reader
設備:鍵盤 ---> 對應的是System.in,是字節流;需要用轉換流:InputStreamReader,即InputStreamReader isr = new InputStreamReader(System.in);
2)目的:OutoutStream和Writer
純文本?是,字符流 ---> Writer
設備:硬盤上的文件 ---> 應該用FileInputStream,但是存儲時,需要加入指定的編碼表,而指定編碼表只有轉換流可以指定,所以要使用的對象是:OutputStreamWriter;而轉換流對象要接收一個字節輸出流,且還可以操作文件的字節輸出流,即FileOutputStream
即:OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt","UTF-8"));
---> 是否需要高效:是,BufferedWriter bufw = new BufferedWriter(osw);
記住:轉換流何時使用:字符流和字節流間的橋樑,通常涉及字符編碼轉換時,需要用到轉換流。
示例:
第四節 信息
一、異常的日誌信息:
當程序在執行的時候,出現的問題是不希望直接打印給用戶看的,是需要作爲文件存儲起來,方便程序員查看,並及時調整的。
示例:
二、系統信息:
獲取系統信息:Properties getProperties()
將信息輸出到指定輸出流中
void list(PrintStream out)
將輸出流中數據存入指定文件中
new PrintStream("systeminfo.txt")
示例: