IO流 (上)
1.理解:IO流即Input流和Output流;
輸入流:外圍設備——>核心處理器;
輸出流:核心處理器——>外圍設備;
總之:以內存爲中心。
2.作用:用來處理設備之間的數據傳輸;設備又分爲輸入設備和輸出設備;
3.字節流和字符流:
字節流:處理的數據都是字節;
字符流:由來:早期的字節流和編碼表;
作用:爲了更便於對文字數據的操作;在內部可以融合編碼表。
體系技巧:該體系中的子類對象中,後綴名都是父類名稱,前綴名都是該流對象的功能名稱。
4.字節流的抽象基類:InputStream和OutputStream ;
5.字符流的抽象基類:Reader和Writer;
讀——>輸入; 寫——>輸出
操作文件中的數據:
1. 寫:將字符串寫入到指定文件中;
1.創建輸出流對象;對象一建立就會將指定的文件創建出來,若存在同名文件,則被覆蓋;
2.通過調用write方法來寫入數據;該數據被寫入到了緩衝區中;
3.通過調用flush()方法刷新流對象。將緩衝區的數據寫入到目的地中;
4.通過調用close()方法將流對象關閉,但關閉之前會刷新流。
代碼體現:
FileWriter fw=new FileWriter("c:\\jdk.txt");
fw.write("abscds");
fw.flush();
fw.close();
flush和close方法的區別:
flush():刷新流對象,流沒有關閉; close():刷新流對象且關閉流。
IO異常的處理方式:
1.在創建輸出流對象時,可能路徑不存在,所以會拋異常;
2.寫數據時可能磁盤空間不足,仍會拋異常;
3.關閉資源時,可能關閉失敗,拋異常;
4.所以將這三句使用try...catch,將異常捕獲;
5.但如果對象創建失敗,就不能再調用close方法即不能關閉流對象;而使用完資源後必須關閉資源,所以將close放在finally語句塊中;
6.在finally語句塊中仍然需要拋異常;
7.對象變量名在try語句塊中,則只能在try中使用,finally中就不能使用,則不能調用close方法;所以將變量定義在try外,將其初始化爲空;
8.如果流對象創建失敗,則一直爲空,就不能調用close方法,則發生空指針異常;所以要判斷到底是否爲空,不爲空再調用close方法。
代碼體現如下:
FileWriter fw = null;
try {
fw = new FileWriter("demo.txt",true);
fw.write("hello"+LINE_SPARATOR+"world");
} catch (IOException e) {
System.out.println(e.toString());
} finally {
if (fw != null)
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException("關閉失敗");
}
}
小細節;1.續寫文件中的內容:通過構造函數FileWriter(String,boolean):boolean 爲真則續寫;
2.將文件中的內容換行:
private static final String LINE_SEPARATOR=System.getProperty(line.separator);//獲取當前系統的換行符
fw.write("Hello"+LINE_SEPARATOR+"World");
2.讀:將指定文件的內容讀取並打印出來;
第一種方式:通過read()讀取單個字符,返回int,碼值,若到結束標記則返回-1;
FileReader fr=new FileReader("d:\\demo.java");//對象一建立,就必須要明確被讀取的文件
int i=0;//定義一個變量表示讀取後的數值
while((i=fr.read())!=-1) {
System.out.println((char)i); //打印i所對應的字符
}
fr.close();
第二種方式:通過read(char[] chs)將讀取到的字符存入到字符數組中,返回讀取的字符個數,若到結束標記則返回-1;
FileReader fr=new FileReader("d:\\demo.java");//對象一建立,就必須要明確被讀取的文件
int len=0;//定義一個變量表示讀取的字符個數
char[] chs = new char[1024];//定義字符數組存儲讀取的字符,長度爲2kb,因爲一個字符2字節;
while((len=fr.read(chs))!=-1) {
System.out.println(new String(chs,0,len)); //打印字符數組中的"有效字符"
}
fr.close();
注意:如果在 char[] chs = new char[3];//若字符串爲abcde,int len1=fr.read(chs);則此時,讀取到的len1爲3,讀取到的內容是abc;若再 int len2=fr.read(chs);此時因數組長度至始至終都爲3,從d開始 讀,d覆蓋a,讀完e就遇到結束標記,而此時e就將b覆蓋了,所以最終數組中的三個元素時dec;若再 int len3=fr.read(chs);此時已經讀到結束標記了,所以返回-1.而chs中的元素仍然是dec.
3.複製文件的原理:從指定文件中讀取文件的數據,將讀取到的數據寫入到另一個目的地。
6.字符流的緩衝區
1.好處:提高了對字符流讀寫數據的效率;若流不存在,則緩衝區存在沒有意義;
2.對應抽象基類:BufferedWriter;BufferedReader
3.原理:將數據進行存儲到數組中,字符流——>字符數組
4.方法:緩衝區中的寫方法都是將數據寫入到緩衝區中,讀方法是從內存中讀數據;
close():內部調用的是流對象中的close()方法,所以關閉緩衝區即關閉流;
newLine():內部調用System.getProperty("line.separator");獲取系統的換行符;是緩衝區對象才能使用,特有方法。
5.BufferedReader 類:該類對象一建立,就創建了一個字符數組,默認長度爲8192,同時多了一個提高行的效率;
read():讀內存中的數據 ;而流對象中的read方法是從硬盤讀數據;
readLine():讀取一行文本,返回行內容,不返回行終止符,若到了末尾,則返回null;
readLine方法原理:內部調用了BufferedReader中的read方法,讀到行終止符之前,將緩衝區中讀取出來的字符臨時存儲,直到讀到行終止符,將臨時存儲的數據轉成字符返回。
6.緩衝區的基本思想,提高效率的原理?
緩衝區的基本思想就是對要處理的數據進行臨時存儲。譬如購物車以及籃子。
原理:減少頻繁的操作,給讀取流對象和寫入流對象提供中轉站,相對於來回跑的麻煩,利用緩衝區的容量,可以一邊先存儲,滿了後再寫入的方式, 這樣就提高了效率。
7.緩衝區的原理分析:緩衝區一建立就已創建一個數組;
緩衝區中的read()方法:內部調用流的read([])方法,將硬盤上讀出來的一部分數據到緩衝區的數組中;若緩衝區的數組中有數據,則調用read()方法將數組中的元素一個一個的取出。
readLine():基於read()方法,read方法將緩衝區數組中的元素一個一個取出,當識別到數組中的元素爲行終止符時,則不再取元素,將之前的元素取出打印,再去識別數組中的元素。
8.裝飾設計模式:
對一組對象進行功能的增強,職責的增強,還不改變原有對象(並不建議繼承,否則體系逐漸龐大,不利於擴展和維護)。
建議;建立另一個類,持有原對象的引用,即與源對象相關聯,對原對象進行裝飾,比繼承更靈活;
記住:被裝飾類和裝飾類必須屬於同一個體系。
9.LineNumberReader 類:
BufferedReader 的子類,返回文本行,已定義了方法setLineNumber(int)和getLineNumber(),可分別用於設置和獲取當前行號;默認 行標號從0開始 ,行號隨數據讀取在每個行結束符遞增,還可以更改行號;若讀完了返回null。
getLineNumber():獲取行號;
10.字節流:什麼數據都能處理;
InputStream OutputStream
FileOutputStream演示:
//1.自定義緩衝區數組 :複製mp3文件
FileInputStream fis=new FileInputStream("c:\\1.mp3");
FileOutputStream fos=new FileOutputStream("c:\\2.mp3");
byte[] by=new byte[1024];
int ch=0;
while((ch=fis.read())!=-1){
fos.write(by,0,ch);
}
fis.close();
fos.close();
//2.使用available()創建長度剛剛好的數組;
FileInputStream fis=new FileInputStream("c:\\1.mp3");
FileOutputStream fos=new FileOutputStream("c:\\2.mp3");
byte[] by=new byte[fis.available()];
int ch=0;
while((ch=fis.read())!=-1){
fos.write(by,0,ch);
}
fis.close();
fos.close();
//3.使用緩衝區複製
FileInputStream fis=new FileInputStream("c:\\1.mp3");
BufferedInputStream bis=new BufferedInputStream(fis);
FileOutputStream fos=new FileOutputStream("c:\\2.mp3");
BufferedOutputStream bos=new BufferedOutputStream(fos);
int by=0;
while((by=bis.read())!=-1){
bos.write(by);
}
bis.close();
bos.close();
//4.不使用緩衝區
FileInputStream fis=new FileInputStream("c:\\1.mp3");
FileOutputStream fos=new FileOutputStream("c:\\2.mp3");
int ch=0;
while((ch=fis.read())!=-1){
fos.write(ch);
}
fis.close();
fos.close();
開發時,建議使用第一種;
11.字節流和字符流讀取區別:
字節流:一次只能讀一個字節,半個中文;
字符流:一次能讀兩個字節,一個字符,一箇中文;
注意:使用字符流不可以複製圖片,因爲字符流就是字節流+編碼表,而用字符流去複製圖片時,字符流會默認將圖片的字節碼格式進行編碼,這樣可能會導致 複製後的圖片與原圖不一致。
12.字節流和字符流的橋樑:轉換流,將字節流和編碼表進行了封裝,提供了對字符操作的更便捷方式。
由來:字節流讀取中文,先將中文讀取到字節數組中,通過String類的getBytes()方法將字節數組中的元素轉爲字符串,但這樣不能讀取單箇中文,要讀取 單箇中文還得通過toCharArray()方法將其轉爲數組;太過麻煩;所以引出字節流和字符流的橋樑——>InputStreamReader,OutputStreamWriter.
轉換流的兩個橋樑都是從哪裏到哪裏?
首先將文件通過InputStreamReader的方式將字節流數據轉成字符流,爲了高效,將其先存儲到緩衝區中;
然後通過OutputStreamWriter將緩衝區中的字符數據轉成字節,最後輸出。
FileReader fr=new FileReader("XXX");
FileInputStream fis=new FileInputStream("XXX");
InputStreamReader isr=new InputStreamReader(fis);
上面三句話,第一句是第二句和第三句話的封裝;FileReader是InputStreamReader類的子類,因爲只有有了轉換流,對字節進行查表編碼,纔可以讀取字符,所以FileReader方法都是來自轉換流轉化後的讀寫方法。
子父類的區別:FileReader是用於操作文本文件使用默認編碼表的便捷類,因爲它將字節流和編碼表進行封裝;如果操作的不是文件且不是默認編碼表, 就不能用RileReader類操作文件,而是用轉換流,通過傳入的具體字節流對象來確定指定操作的數據,而且還可以傳入指定要使用的編碼 表。
便捷類弊端 :因其是將文件、字節流和默認編碼表封裝,所以只能操作文件,而且只能使用默認編碼表。