1. 程式人生 > >java基礎(2)--異常類&IO流ing

java基礎(2)--異常類&IO流ing

1、異常類
底層是Throwable,直接子類包括Exception和Error。
一般RuntimeException類都是程式設計師錯誤。

2、異常處理
try:用來檢測異常
catch:用來捕獲異常
finally:釋放資源
一個try,可以對應多個catch,一個finally。
一個catch中可以包含多種異常,類用’|'分開
可以直接使用throw在方法體內直接丟擲,此時需要在方法後使用throws宣告異常。
PS:執行時異常在方法體內丟擲,那麼就不需要throws宣告;編譯時異常必須有。
PS:Throwable的預設輸出(default):printStackTrace();

3、throws和throw的區別
3.1 throws用在方法聲明後面,跟的是異常類名;用在方法體內,跟的是異常物件名。
3.2 throws可以跟多個異常類名,用逗號隔開;throw只能丟擲一個異常物件名。
3.3 throws表示丟擲異常,由該方法的呼叫者來處理;throw表示丟擲異常,由方法體內的語句處理。

4、finally關鍵字控制的語句體一定會執行(即使提前return也會執行),除非在此之前jvm提前退出(e.g. System.exit(0))
PS:對於catch中的return,finally是在return之後執行的。

5、final、finally和finalize的區別
5.1 final可以修飾類,不能被繼承;修飾方法,不能被重寫;修飾變數,只能賦值一次。
5.2 finally是try語句中的一個語句體,不能單獨使用,用來釋放資源。
5.3 finalize是一個方法,當垃圾回收器確定不存在對該物件的更多引用時,由物件的垃圾回收器呼叫此方法。

6、異常類繼承:子類只能丟擲父類的異常子集(相同或少於),如果重寫的類沒有丟擲異常,那麼繼承該類的子類也不能丟擲異常

7、File類
7.1 createNewFile():建立檔案,如果存在,就不建立並返回false
7.2 mkdir():建立資料夾,如果存在,就不建立並返回false
7.3 mkdirs():建立資料夾,如果父資料夾不存在,也會一同建立
7.4 renameTo(File dest):重新命名檔案;如果路徑相同,那麼就是重新命名;如果路徑不同,那麼就是重新命名+剪下
7.5 delete():刪除檔案/資料夾;刪除不走回收站;如果刪除資料夾,那麼資料夾中不能包含內容
7.6 isDirectory():是否是目錄;isFile():是否是檔案
7.7 exists():是否存在;isHidden():是否隱藏
7.8 canRead():是否可讀;canWrite():是否可寫
PS:設定setReadable(false),window下依然可讀,因為window認為一切檔案都可讀;linux下設定不可讀就不可讀。
設定setWriteable(false),那麼window和linux都不可寫
7.9 getAbsolutePath():獲取絕對路徑;getPath():獲取路徑;getName():獲取名稱;lastModified():獲取最後一次的修改時間(毫秒);list():獲取指定目錄下的所有檔案或資料夾的名稱(String[],檔名稱);listFiles():獲取指定目錄下的所有檔案或資料夾的File陣列(File[],完全路徑)

8、FilenameFilter介面,過濾檔名
示例:

new FilenameFilter() {
	@Override
	public boolean accept(File dir, String name){
		File file = new File(dir, name);
		return file.isFile() && file.getName().endsWith(".jpg");
	}
}

9、IO流,就好像建立了一根記憶體到硬碟檔案的管子,來對檔案進行讀寫、操作。
IO流的常用父類:
1.1 位元組流的抽象父類:InputStream、OutputStream (位元組流可以操作任何資料,因為計算機中資料是以位元組形式儲存的)
1.1.1 InputStream read():雖然讀的是byte,但是一般返回的是int:因為位元組輸入流可以操作任意型別的檔案,檔案底層都是以二進位制形式儲存的。如果返回byte,可能會讀到中間的時候遇到全1位元組,那麼就和那會-1。因為檔案讀完的時候,也是返回-1,這樣就會出現錯誤。因此使用int型別接受,那麼當遇到全1位元組時會在其前面補上24個0湊足4個位元組,此時byte的-1就變為了int的255。保證了整個檔案資料的讀取完畢,同時結束標記的-1在byte和int型別下都是相同的。
1.1.2 InputStream available():檔案剩餘位元組數
1.1.3 InputStream read(byte[] b) or read(byte[] b, int off, int len):一次性讀取b.length(or len)個位元組資料到b陣列中;off表示陣列中的偏移量(索引)
1.1.4 OutputStream 如果檔案不存在,會建立檔案;預設清空重寫檔案,第二引數為是否追加模式;write():雖然寫的是int型別,但是會轉換成byte型別寫入,自動去除前三個8位。補充:write(byte[] b)
PS:FileInputStream(path)、FileOutputStream(path);BufferedInputStream(InputStream)、BufferedOutputStream(OutputStream)
BufferedInputStream:
BufferedInputStream內建一個緩衝區(陣列)
從BufferedInputStream中讀取一個位元組時,BufferedInputStream會一次性從檔案中讀取8192個位元組,存在緩衝區中,並返回程式一個位元組
程式再次讀取時,直接從緩衝區獲取,直到緩衝區所有位元組被獲取完畢以前,無需再查詢檔案
BufferOutputStream:
BufferOutputStream也內建一個緩衝區(陣列)
程式向流中寫位元組時,不會直接寫到檔案中,而是先寫到緩衝區中
直到緩衝區寫滿/資料寫完,BufferOutputStream才會把緩衝區的資料一次性寫到檔案中。
因此,BufferInputStream和BufferOutputStream的read()和write()方法同樣是讀寫位元組(單純讀寫位元組),但是比FileInputStream和FileOutputStream快很多。
但是如果在FileInputStream和FileOutputStream使用read(byte[] b)和write(byte[] b)方法的話(8192個位元組大小),會比BufferInputStream和BufferOutputStream的read()和write()方法快一點點。因為FileXXXputStream操作的是同一個陣列,而BufferedXXXputStream操作的是兩個不同的陣列。
1.1.5 close():具備重新整理功能,在關閉流之前,就會想重新整理一次緩衝區,將緩衝區的直接全部重新整理到檔案上,再關閉。
1.1.6 flush():具備單純的重新整理功能。主要用於難以將快取區填滿就需要傳遞的場景,e.g. 聊天
PS:因為呼叫流方法開啟就必須有關閉。同時為了防止檔案不存在,所以需要將FileXXXputStream、BufferedXXXputStream等類的包含檔案的宣告需要加到try語句塊中。
不過可以在JDK1.7以後,可以將該類宣告加到try(…)中,那麼檔案流呼叫完畢以後會自動呼叫close()方法。原因是因為JDK1.7中這些原生檔案流繼承了InputStream(or OutputStream)類 -->實現了Closeable介面 -->繼承了AutoCloseable介面,該類中只有一個方法就是close()方法(PS:那麼如果自定義檔案流類實現了AutoCloseable介面,也可以自動呼叫close()方法)。
e.g.

try(
	FileInputStream fis = new FileInputStream("xxx.txt");
	FileOutputStream fis = new FileOutputStream("yyy.txt");
) {
	int b;
	while((b = fis.read()) != -1){
		fos.write(b);
	}
}catch(...){...}

PS:加密圖片最簡單的方法就是將每個位元組異或上一個數字,當字元再次異或該數字時就解密了。
1.2 字元流的抽象父類:Reader、Writer (字元流只能操作字元資料;Writer自帶1024位元組的緩衝區)
1.2.1 常用子類:FileReader、FileWriter;BufferedReader、BufferedWriter;LineNumberReader、LineNumberWriter;InputStreamReader、OutputStreamWriter
BufferedReader的readLine():讀取一行字元(不包含換行符)
BufferedWriter的newLine():輸出一個跨平臺的換行符號
PS:不推薦在拷貝文字的時候,使用字元流,因為讀取時會把位元組轉為字元,寫出時需要將字元轉為位元組。
但是當只是單向流操作(只讀,只寫)的時候,可以使用字元流,因為讀取的時候是按照字元的大小讀取的,不會出現半個中文;寫出的時候可以直接將字串寫出,不用轉換為位元組資料。
PS:字元流不可以拷貝非純文字檔案,因為位元組會被轉換為字元,那麼就可能存在一些位元組在轉成字元的過程中沒有在碼錶中找到對應的字元,會被程式使用?字元替代,但是在轉換寫出的時候,並不能轉換為正確的位元組資料。
PS:newLine()是跨平臺的方法,"\r\n"只支援window系統(linux下只需要"\n",IOS下只需要"\r")
LineNumberXXX整行讀整行寫。LineNumberReader可以通過setLineNumber()方法設定從哪一行之後開始讀,getLineNumber()獲取讀取的行號。
PS:UTF-8中一個漢字佔據3個位元組,而GBK中一個漢字佔據2兩個位元組。
XXputStreamXXX是位元組流與字元流之間的橋樑(InputStreamReader位元組流通向字元流;OutputStreamWriter字元流到位元組流)
e.g.

BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("utf-8.txt","utf-8"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("gbk.txt","gbk"));

PS:多層類巢狀包裝,可以提升讀寫的效率
圖解:

1.3 特殊流
序列流 SequenceInputStream可以將多個位元組輸入流整合成一個,從序列流中讀取資料時,將從被整合的第一個流開始讀,讀完一個以後繼續讀取第二個,直到讀取完畢。
e.g.

FileInputStream fis1 = new FileInputStream("1.txt");
FileInputStream fis2 = new FileInputStream("2.txt");
FileInputStream fis3 = new FileInputStream("3.txt");
// 整合兩個位元組流
SequenceInputStream sis1 = new SequenceInputStream(fis1,fis2);
// 整合多個位元組流
Vector<InputStream> v = new Vector<>();
v.add(fis1);
v.add(fis2);
v.add(fis3);
Enumeration<InputStream> en = v.elements();
SequenceInputStream sis2 =new SequenceInputStream(en);

記憶體輸出流 ByteArrayOutputStream可以向記憶體中寫資料,將記憶體當做一個緩衝區(陣列),當需要寫出時,可以一次性獲取所有資料。方法:write()寫資料,toByteArray()獲取資料