天天看點

Java基礎-6.try catch、file、IO流一、 異常的概述和分類二、Throwable二、File三、IO流四、FileReader

一、 異常的概述和分類

異常就是Java程式在運作過程中出現的錯誤。

Throwable:異常的最頂層

Error:伺服器當機,資料庫崩潰等

Exception: RuntimeException

1.JVM預設是如何處理異常的

main函數收到這個問題時,有兩種處理方式:自己将該問題處理,然後繼續運作;自己沒有針對的處理方式,隻有交給調用main的jvm來處理;

jvm有一個預設的異常處理機制,将該異常進行處理.并将該異常的名稱,異常的資訊。異常出現的位置列印在了控制台上,同時将程式停止運作

2.try...catch方式1

try…catch…finally

throws

3.try...catch方式2

try...catch的方式處理多個異常,即try後面有多個catch,每個catch不同的錯誤,一般用Exception就行。JDK7之後,用“|”就可以并列多個異常。

4.finally

finally用于釋放資源,在IO流操作和資料庫操作中會見到,被finally控制的語句體一定會執行,特殊情況:在執行到finally之前jvm退出了(比如System.exit(0)),如果catch裡面有return語句, finally的代碼還會執行,但是最好不要在裡面寫return語句,否則會覆寫掉其他的return。

5.final,finally和finalize

final可以修飾類,不能被繼承,修飾方法,不能被重寫,修飾變量,隻能指派一次。

finally是try語句中的一個語句體,不能單獨使用,用來釋放資源。

finalize是一個方法,當垃圾回收器确定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。

6.編譯期異常和運作期異常的差別

Java中的異常被分為兩大類:編譯時異常和運作時異常。

所有的RuntimeException類及其子類的執行個體被稱為運作時異常,其他的異常就是編譯時異常。編譯時異常,程式可能不會出錯,但是Java為了防止錯誤出現,在編譯時候就會提示程式員必須要對這裡進行處理。運作時異常,無需顯示處理,也可以和編譯時異常一樣處理。

二、Throwable

getMessage()擷取異常資訊,傳回字元串。

toString()擷取異常類名和異常資訊,傳回字元串。

printStackTrace()擷取異常類名和異常資訊,以及異常出現在程式中的位置。傳回值void。

1.throws處理異常

定義功能方法時,需要把出現的問題暴露出來讓調用者去處理。那麼就通過throws在方法上辨別。在方法中如果throw一個runtimeexception,不需要在方法上再throws。

2.throw和throws

throw在功能方法内部出現某種情況,程式不能繼續運作,需要進行跳轉時,就用throw把異常對象抛出。throws表示抛出異常,由該方法的調用者來處理,用在方法聲明後面,跟的是異常類名,可以跟多個異常類名,用逗号隔開。

3.自定義異常概述和基本使用

繼承自Exception

繼承自RuntimeException

在實際用的時候調用此異常即可

class AgeOutOfBoundsException extends Exception {

         public AgeOutOfBoundsException() {

                   super();

          }

          public AgeOutOfBoundsException(String message) {

                   super(message);

          }

}

4.異常的注意事項

子類重寫父類方法時,子類的方法必須抛出相同的異常或父類異常的子類。(父親壞了,兒子不能比父親更壞)。

如果父類抛出了多個異常,子類重寫父類時,隻能抛出相同的異常或者是他的子集,子類不能抛出父類沒有的異常。

如果被重寫的方法沒有異常抛出,那麼子類的方法絕對不可以抛出異常,如果子類方法内有異常發生,那麼子類隻能try,不能throws。

5.練習

鍵盤錄入一個int類型的整數,對其求二進制表現形式,如果錄入的整數過大,給予提示,錄入的整數過大請重新錄入一個整數BigInteger,如果錄入的是小數,給予提示,錄入的是小數,請重新錄入一個整數,如果錄入的是其他字元,給予提示,錄入的是非法字元,請重新錄入一個整數。    

public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        System.out.println("請輸入一個整數:");

        while(true) {

            String line = sc.nextLine();                    //将鍵盤錄入的結果存儲在line中

            try {

                int num = Integer.parseInt(line);                //将字元串轉換為整數

                System.out.println(Integer.toBinaryString(num));//将整數轉換為二進制

                break;                                            //跳出循環

            }catch(Exception e) {

                try {

                    new BigInteger(line);

                    System.out.println("錄入錯誤,您錄入的是一個過大整數,請重新輸入一個整數:");

                }catch (Exception e2) {                            //alt + shif + z (try catch快捷鍵)

                    try {

                        new BigDecimal(line);

                        System.out.println("錄入錯誤,您錄入的是一個小數,請重新輸入一個整數:");

                    } catch (Exception e1) {

                        System.out.println("錄入錯誤,您錄入的是非法字元,請重新輸入一個整數:");

                    }

                }

            }

        }        

    }

}

二、File

File更應該叫做一個路徑,檔案路徑或者檔案夾路徑。

File(String pathname):根據一個路徑得到File對象

File(String parent, String child):根據一個目錄和一個子檔案/目錄得到File對象,就是兩個路徑的拼接。

File(File parent, String child):根據一個父File對象和一個子檔案/目錄得到File對象,就是把父級路徑封裝成一個file,可以調用裡面的函數。

1.建立功能

public boolean createNewFile():建立檔案 如果存在這樣的檔案,就不建立了

public boolean mkdir():建立檔案夾 如果存在這樣的檔案夾,就不建立了

public boolean mkdirs():建立檔案夾,如果父檔案夾不存在,會幫你建立出來

如果你建立檔案或者檔案夾忘了寫盤符路徑,那麼,預設在項目路徑下。

2.重命名和删除功能

public boolean renameTo(File dest):把檔案重命名為指定的檔案路徑,相當于剪切

public boolean delete():删除檔案或者檔案夾

3.File類的判斷功能

public boolean isDirectory():判斷是否是目錄

public boolean isFile():判斷是否是檔案

public boolean exists():判斷是否存在

public boolean canRead():判斷是否可讀,windows系統預設都是可讀的

public boolean canWrite():判斷是否可寫

public boolean isHidden():判斷是否隐藏

4.擷取功能

public String getAbsolutePath():擷取絕對路徑

public String getPath():擷取路徑

public String getName():擷取名稱

public long length():擷取長度。位元組數

public long lastModified():擷取最後一次的修改時間,毫秒值

public String[] list():擷取指定目錄下的所有檔案或者檔案夾的名稱數組

public File[] listFiles():擷取指定目錄下的所有檔案或者檔案夾的File數組

5.輸出指定目錄下指定字尾的檔案名

需求:判斷E盤目錄下是否有字尾名為.jpg的檔案,如果有,就輸出該檔案名稱

public static void main(String[] args) {

       File dir = new File("f:\\");

       String[] arr = dir.list(); //擷取e盤下所有的檔案或檔案夾

       for (String string : arr) {

               if(string.endsWith(".jpg")) {

                   System.out.println(string);

               }

        }

或者

File[] subFiles = dir.listFiles(); //擷取e盤下所有的檔案或檔案夾對象

for (File subFile : subFiles) {

        if(subFile.isFile() && subFile.getName().endsWith(".jpg")) {

           System.out.println(subFile);

        }

}

6.檔案名稱過濾器

public String[] list(FilenameFilter filter):需要重寫方法

public File[] listFiles(FileFilter filter):需要重寫方法

File dir = new File("f:\\");

String[] arr = dir.list(new FilenameFilter() {

          @Override

          public boolean accept(File dir,String name) {

                   File file = new File(dir,name);

                   return file.isFile() && file.getName().endsWith(".jpg");

          }

});

隻會傳回自定義的能被接受的檔案

for (String string : arr) {

     System.out.println(string);

}

三、IO流

IO流用來處理裝置之間的資料傳輸,Java對資料的操作是通過流的方式,Java用于操作流的類都在IO包中,流按流向分為兩種:輸入流,輸出流。

流按操作類型分為兩種:

位元組流 : 位元組流可以操作任何資料,因為在計算機中任何資料都是以位元組的形式存儲的

字元流 : 字元流隻能操作純字元資料,比較友善。

IO流常用父類

位元組流的抽象父類:InputStream、OutputStream

字元流的抽象父類:Reader、Writer

1.FileInputStream

read()一次讀取一個位元組

FileInputStream fis = new FileInputStream("aaa.txt");   

//建立一個檔案輸入流對象,并關聯aaa.txt

int b;                  //定義變量,記錄每次讀到的位元組

while((b = fis.read()) != -1) {   //将每次讀到的位元組指派給b并判斷是否是-1

    System.out.println(b);     //列印每一個位元組

}

fis.close();    //關閉流釋放資源

這裡也會将回車符号讀到,檔案中存的是字元,讀進來變成int值,例如a為97。

2.read()方法傳回值為什麼是int

read()方法讀取的是一個位元組,為什麼傳回是int,而不是byte?

因為位元組輸入流可以操作任意類型的檔案,比如圖檔音頻等,這些檔案底層都是以二進制形式的存儲的,如果每次讀取都傳回byte,有可能在讀到中間的時候遇到111111111,那麼這11111111是byte類型的-1,我們的程式是遇到-1就會停止不讀了,後面的資料就讀不到了,是以在讀取的時候用int類型接收,如果11111111會在其前面補上24個0湊足4個位元組,那麼byte類型的-1就變成int類型的255了,這樣可以保證整個資料讀完,而結束标記的-1就是int類型。

3.FileOutputStream

write()一次寫出一個位元組

FileOutputStream fos = new FileOutputStream("bbb.txt"); //如沒有bbb.txt,會建立出一個

//fos.write(97);  //雖然寫出的是一個int數,但是在寫出的時候會将前面的24個0去掉,是以寫出的一個byte,實際是對應a

fos.write(98); 

fos.write(99);

fos.close();

FileOutputStream fos = new FileOutputStream("bbb.txt",true);  true表示可以追加

4.IO流拷貝圖檔

FileInputStream讀取

FileOutputStream寫出

FileInputStream fis = new FileInputStream("QQ.mp3");   //建立輸入流對象,關聯QQ.mp3

FileOutputStream fos = new FileOutputStream("copy.mp3");//建立輸出流對象,關聯copy.mp3

int b;

while((b = fis.read()) != -1) {

    fos.write(b);

}

fis.close();

fos.close();   弊端:效率太低

5.位元組數組拷貝之available()方法

int read(byte[] b):一次讀取一個位元組數組,具體大小可定義

write(byte[] b):一次寫出一個位元組數組

available():擷取讀的檔案所有的位元組個數,弊端:有可能會記憶體溢出

FileInputStream fis = new FileInputStream("緻青春.mp3");

FileOutputStream fos = new FileOutputStream("copy.mp3");

byte[] arr = new byte[fis.available()];      //根據檔案大小做一個位元組數組

fis.read(arr);       //将檔案上的所有位元組讀取到數組中

fos.write(arr);     //将數組中的所有位元組一次寫到了檔案上

fis.close();

fos.close();

6.定義小數組拷貝

write(byte[] b)

write(byte[] b, int off, int len)寫出有效的位元組個數

FileInputStream fis = new FileInputStream("緻青春.mp3"); 

FileOutputStream fos = new FileOutputStream("copy.mp3"); 

int len; 

byte[] arr = new byte[1024 * 8]; //自定義位元組數組

while((len = fis.read(arr)) != -1) {

//fos.write(arr); 

fos.write(arr, 0, len); //寫出位元組數組寫出有效個位元組個數 ,定義length長度是為了避免将數組後面已經寫入的資料又寫一遍

}

fis.close(); 

fos.close();

7.BufferedInputStream和BufferOutputStream拷貝

位元組流一次讀寫一個數組的速度明顯比一次讀寫一個位元組的速度快很多,這是加入了數組這樣的緩沖區效果,java本身在設計的時候,也考慮到了這樣的設計思想(裝飾設計模式後面講解),是以提供了位元組緩沖區流。

BufferedInputStream内置了一個緩沖區(數組),從BufferedInputStream中讀取一個位元組時BufferedInputStream會一次性從檔案中讀取8192個,存在緩沖區中,傳回給程式,程式再次讀取時, 就不用找檔案了,直接從緩沖區中擷取,直到緩沖區中所有的都被使用過,才重新從檔案中讀取8192個。

BufferedOutputStream也内置了一個緩沖區(數組),程式向流中寫出位元組時,不會直接寫到檔案,先寫到緩沖區中,直到緩沖區寫滿,BufferedOutputStream才會把緩沖區中的資料一次性寫到檔案裡。 

FileInputStream fis = new FileInputStream("緻青春.mp3");  //建立檔案輸入流對象

BufferedInputStream bis = new BufferedInputStream(fis);   //建立緩沖區對fis裝飾

FileOutputStream fos = new FileOutputStream("copy.mp3");  //建立輸出流對象,關聯copy.mp3

BufferedOutputStream bos = new BufferedOutputStream(fos);  //建立緩沖區對fos裝飾

int b;

while((b = bis.read()) != -1) {     

    bos.write(b);

}

bis.close(); //隻關裝飾後的對象即可

bos.close();

8.flush和close方法的差別

flush()方法用來重新整理緩沖區的,重新整理後可以再次寫出。

close()方法用來關閉流釋放資源的的,如果是帶緩沖區的流對象的close()方法,不但會關閉流,還會再關閉流之前重新整理緩沖區,關閉後不能再寫出。

9.位元組流讀寫中文

位元組流在讀中文的時候有可能會讀到半個中文,造成亂碼,可用字元流解決。

位元組流寫出中文的問題

位元組流直接操作的位元組,是以寫出中文必須将字元串轉換成位元組數組

寫出回車換行write("\r\n".getBytes());因為沒有字元串的方法

10.流的标準處理異常代碼1.7版本

try(

    FileInputStream fis = new FileInputStream("aaa.txt");

    FileOutputStream fos = new FileOutputStream("bbb.txt");

){

    int b;

    while((b = fis.read()) != -1) {

        fos.write(b);

    }

}

在try()中建立的流對象必須實作了AutoCloseable這個接口,如果實作了,在try後面的{}(讀寫代碼)執行後就會自動調用,流對象的close方法将流關掉。

11.圖檔加密

給圖檔加密

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.jpg"));

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.jpg"));

int b;

while((b = bis.read()) != -1) {

    bos.write(b^123);

}

bis.close();

bos.close();

12.拷貝檔案

在控制台錄入檔案的路徑,将檔案拷貝到目前項目下

Scanner sc = new Scanner(System.in);

System.out.println("請輸入一個檔案路徑");

String line = sc.nextLine();           //将鍵盤錄入的檔案路徑存儲在line中

File file = new File(line);               //封裝成File對象

FileInputStream fis = new FileInputStream(file);

FileOutputStream fos = new FileOutputStream(file.getName());

int len;

byte[] arr = new byte[8192];         //定義緩沖區

while((len = fis.read(arr)) != -1) {

    fos.write(arr,0,len);

}

fis.close();

fos.close();

13.錄入資料拷貝到檔案

将鍵盤錄入的資料拷貝到目前項目下的text.txt檔案中,鍵盤錄入資料當遇到quit時就退出

Scanner sc = new Scanner(System.in);

FileOutputStream fos = new FileOutputStream("text.txt");

System.out.println("請輸入:");

while(true) {

    String line = sc.nextLine();

    if("quit".equals(line))

        break;

    fos.write(line.getBytes());

    fos.write("\r\n".getBytes());

}

fos.close();

四、FileReader

字元流是可以直接讀寫字元的IO流,字元流讀取字元,就要先讀取到位元組資料,然後轉為字元。如果要寫出字元,需要把字元轉為位元組再寫出。

FileReader類的read()方法可以按照字元大小讀取

FileReader fr = new FileReader("aaa.txt");    //建立輸入流對象,關聯aaa.txt

int ch;

while((ch = fr.read()) != -1) {           //将讀到的字元指派給ch

    System.out.println((char)ch);      //将讀到的字元強轉後列印

}

fr.close();//關流

1.FileWriter

FileWriter類的write()方法可以自動把字元轉為位元組寫出

2.字元流的拷貝

FileReader fr = new FileReader("a.txt");

FileWriter fw = new FileWriter("b.txt");

int ch;

while((ch = fr.read()) != -1) {

       fw.write(ch);

}

fr.close();

fw.close();

3.什麼情況下使用字元流

字元流也可以拷貝文本檔案,但不推薦使用。因為讀取時會把位元組轉為字元,寫出時還要把字元轉回位元組。程式需要讀取一段文本, 或者需要寫出一段文本的時候可以使用字元流,讀取的時候是按照字元的大小讀取的,不會出現半個中文,寫出的時候可以直接将字元串寫出,不用轉換為位元組數組。

不可以拷貝非純文字的檔案,因為在讀的時候會将位元組轉換為字元,在轉換過程中,可能找不到對應的字元,就會用?代替,寫出的時候會将字元轉換成位元組寫出去,如果是?,直接寫出,這樣寫出之後的檔案就亂了。

4.自定義字元數組的拷貝

FileReader fr = new FileReader("aaa.txt");  //建立字元輸入流,關聯aaa.txt

FileWriter fw = new FileWriter("bbb.txt"); //建立字元輸出流,關聯bbb.txt

int len;

char[] arr = new char[1024*8];                      //建立字元數組

while((len = fr.read(arr)) != -1) {                 //将資料讀到字元數組中

    fw.write(arr, 0, len);   //從字元數組将資料寫到檔案上

}

fr.close();

fw.close();

5.帶緩沖的字元流

BufferedReader的read()方法讀取字元時會一次讀取若幹字元到緩沖區,然後逐個傳回給程式,降低讀取檔案的次數,提高效率。

BufferedWriter的write()方法寫出字元時會先寫到緩沖區,緩沖區寫滿時才會寫到檔案,降低寫檔案的次數,提高效率。

BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));  //建立字元輸入流對象,關聯aaa.txt

BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));  //建立字元輸出流對象,關聯bbb.txt

int ch; 

while((ch = br.read()) != -1) { //read一次,會先将緩沖區讀滿,從緩沖去中一個一個的返給臨時變量ch

    bw.write(ch);   //write一次,是将資料裝到字元數組,裝滿後再一起寫出去

}

br.close();

bw.close();

6.readLine()和newLine()方法

BufferedReader的readLine()方法可以讀取一行字元(不包含換行符号)

BufferedWriter的newLine()可以輸出一個跨平台的換行符号"\r\n"

BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));

BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));

String line;

while((line = br.readLine()) != null) {

    bw.write(line);

    //bw.write("\r\n");                 //隻支援windows系統

    bw.newLine();                       //跨平台的

}

br.close();

bw.close();

7.LineNumberReader

LineNumberReader是BufferedReader的子類, 具有相同的功能,并且可以統計行号

調用getLineNumber()方法可以擷取目前行号

調用setLineNumber()方法可以設定目前行号

LineNumberReader lnr = new LineNumberReader(new FileReader("aaa.txt"));

String line;

lnr.setLineNumber(100);                                 //設定行号

while((line = lnr.readLine()) != null) {

    System.out.println(lnr.getLineNumber() + ":" + line);//擷取行号

}

lnr.close();

8.使用指定的碼表讀寫字元

FileReader是使用預設碼表讀取檔案,如果需要使用指定碼表讀取,那麼可以使用InputStreamReader(位元組流,編碼表)

FileWriter是使用預設碼表寫出檔案,如果需要使用指定碼表寫出, 那麼可以使用OutputStreamWriter(位元組流,編碼表)

BufferedReader br =                      //高效的用指定的編碼表讀

        new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8"));

BufferedWriter bw =                       //高效的用指定的編碼表寫

        new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK"));

int ch;

while((ch = br.read()) != -1) {

    bw.write(ch);

}

br.close();

bw.close();

9.遞歸讀取檔案名

public static void printJavaFile(File dir) {

        //1,擷取到該檔案夾路徑下的所有的檔案和檔案夾,存儲在File數組中

        File[] subFiles = dir.listFiles();

        //2,周遊數組,對每一個檔案或檔案夾做判斷

        for (File subFile : subFiles) {

            //3,如果是檔案,并且字尾是.java的,就列印

            if(subFile.isFile() && subFile.getName().endsWith(".java")) {

                System.out.println(subFile);

            //4,如果是檔案夾,就遞歸調用

            }else if (subFile.isDirectory()){

                printJavaFile(subFile);

            }

        }

    }