異常的第一種處理方式throws。
看以下例子:
import java.io.*;
public class ExceptionTest04{
public static void main(String[] args){
m1();
}
public static void m1(){
m2();
}
public static void m2(){
m3();
}
public static void m3(){
new FileInputStream("c:/ab.txt"); //FileInputStream構造方法聲明位置上使用throws(向上拋)
}
}
以上代碼編譯時出錯:
ExceptionTest04.java:16: 錯誤: 未報告的異常錯誤FileNotFoundException; 必須對其進行捕獲或聲明以便拋出
new FileInputStream("c:/ab.txt");
按照提示修改之后:
import java.io.*;
public class ExceptionTest04{
public static void main(String[] args){
m1();
System.out.println("hello");
}
public static void m1(){
m2();
}
public static void m2(){
m3();
}
public static void m3() throws FileNotFoundException{
new FileInputStream("c:/ab.txt");
}
}
編譯時出錯;
ExceptionTest04.java:30: 錯誤: 未報告的異常錯誤FileNotFoundException; 必須對其進行捕獲或聲明以便拋出
m3();
由此看出,修改之后m3()運行時出現錯誤,上拋給m2(),以此類推,m2()上拋異常給m1(),m1()上拋異常給main()方法。
因此作出如下修改:
import java.io.*;
public class ExceptionTest04{
public static void main(String[] args) throws FileNotFoundException{
m1();
System.out.println("hello");
}
public static void m1() throws FileNotFoundException{
m2();
}
public static void m2() throws FileNotFoundException{
m3();
}
public static void m3() throws FileNotFoundException{
new FileInputStream("c:/ab.txt");
}
}
修改之后編譯通過,但是System.out.println("hello")並不會執行。
從上面的例子中可以看出,使用throws處理異常不是真正處理異常而是推卸責任,誰調用就會拋給誰。上面的m1方法如果出現了異常,因為采用的是上拋,JVM遇到這個異常就會退出JVM,之后的代碼不會執行。因此引入try...catch...方法。修改如下,編譯運行通過,並且輸出:hello。
import java.io.*;
public class ExceptionTest04{
public static void main(String[] args) throws FileNotFoundException{
try{
m1();
} catch(FileNotFoundException e){}
System.out.println("hello");
}
public static void m1() throws FileNotFoundException{
m2();
}
public static void m2() throws FileNotFoundException{
m3();
}
public static void m3() throws FileNotFoundException{
new FileInputStream("c:/ab.txt");
}
}
捕捉 try...catch...
先來看看語法,
try{
可能出現異常的代碼;
}catch(異常類型1 變量){
處理異常的代碼;
}catch(異常類型2 變量){
處理異常的代碼;
}......
看以下例子:
import java.io.*;
public class ExceptionTest05{
public static void main(String[] args){
try{
//FileNotFoundException
FileInputStream fis=new FileInputStream("c:/ab.txt");
}catch(ArithmeticException e){ //捕獲的異常是算術異常
}
}
}
以上的代碼編譯無法通過,因為FileNotFoundException沒有處理,報錯:
ExceptionTest05.java:19: 錯誤: 未報告的異常錯誤FileNotFoundException; 必須對其進行捕獲或聲明以便拋出
FileInputStream fis=new FileInputStream("c:/ab.txt");
^
1 個錯誤
也就是說可能出現異常的代碼和捕獲異常的代碼必須是相對應的。
將捕獲的異常修改之后,編譯通過,
import java.io.*;
public class ExceptionTest05{
public static void main(String[] args){
try{
//FileNotFoundException
FileInputStream fis=new FileInputStream("c:/ab.txt");
}catch(FileNotFoundException e){
}
}
}
再看以下例子,以下程序編譯無法通過,
import java.io.*;
public class ExceptionTest05{
public static void main(String[] args){
try{
//FileNotFoundException
FileInputStream fis=new FileInputStream("c:/ab.txt");
fis.read();
}catch(FileNotFoundException e){
}
}
}
報錯:
ExceptionTest05.java:48: 錯誤: 未報告的異常錯誤IOException; 必須對其進行捕獲或聲明以便拋出
fis.read();
^
1 個錯誤
因為read()方法又拋出了IOException的異常,而catch()只處理了FileNotFoundException的異常。
read()方法的拋出的異常如下圖所示:

要想編譯通過,必選進行IOException處理,
import java.io.*;
public class ExceptionTest05{
public static void main(String[] args){
try{
//FileNotFoundException
FileInputStream fis=new FileInputStream("c:/ab.txt");
fis.read();
}catch(FileNotFoundException e){
}catch(IOException e){
}
}
}
或者如下直接進行IOException處理,這是因為FileNotFoundException繼承IOException。
import java.io.*;
public class ExceptionTest05{
public static void main(String[] args){
try{
//FileNotFoundException
FileInputStream fis=new FileInputStream("c:/ab.txt");
fis.read();
}catch(IOException e){
}
}
}
再看以下例子:
import java.io.*;
public class ExceptionTest05{
public static void main(String[] args){
try{
//FileNotFoundException
FileInputStream fis=new FileInputStream("c:/ab.txt");
fis.read();
}catch(IOException e){
}catch(FileNotFoundException e){
}
}
}
編譯出錯:
ExceptionTest05.java:97: 錯誤: 已捕獲到異常錯誤FileNotFoundException
}catch(FileNotFoundException e){
^
1 個錯誤
這是因為FileNotFoundException繼承IOException,catch語句塊雖然可以寫多個,但是從上到下catch,必須從小類型異常到大類型異常進行捕捉。並且try...catch...中最多執行一個catch語句塊,執行結束后,try...catch...就執行結束了。
最后看一個詳細的例子總結一下之前的內容。
import java.io.*;
public class ExceptionTest06{
public static void main(String[] args){
FileInputStream fis=new FileInputStream("abc");
fis.read();
}
}
以上程序編譯無法通過,可以進行如下處理,
import java.io.*;
public class ExceptionTest06{
public static void main(String[] args)throws IOException,FileNotFoundException{
FileInputStream fis=new FileInputStream("c:/ab.txt");
fis.read();
}
}
或者進行如下處理:
import java.io.*;
public class ExceptionTest06{
public static void main(String[] args)throws IOException{
FileInputStream fis=new FileInputStream("c:/ab.txt");
fis.read();
}
}
或者使用try...catch...進行異常處理。
import java.io.*;
public class ExceptionTest06{
public static void main(String[] args){
try{
FileInputStream fis=new FileInputStream("c:/ab.txt");
fis.read();
}catch(FileNotFoundException e){ //e內存地址指向的堆中的那個對象是FileNotFoundException類型的事件。
System.out.println("讀取的文件不存在!");
}catch(IOException e){
System.out.println("其它IO異常!");
}
System.out.println("ABC");
}
}
運行之后輸出:
讀取的文件不存在!
ABC
如何取得異常對象的具體信息,常用的方法主要有兩種:
取得異常信息描述:getMessage()
取得異常的堆棧信息(比較適合於程序調試階段):printStackTrace();
先看取得異常的堆棧信息printStackTrace()方法。看以下例子。
import java.io.*;
public class ExceptionTest07{
public static void main(String[] args){
try{
FileInputStream fis = new FileInputStream("c:/ab.txt");
}catch(FileNotFoundException e){
//打印異常堆棧信息
//一般情況下都會使用該方式去調試程序
e.printStackTrace();
}
//這段代碼會執行
System.out.println("ABC");
}
}
編譯運行后輸出:
java.io.FileNotFoundException: c:\ab.txt (系統找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at ExceptionTest07.main(ExceptionTest07.java:8)
ABC
再來看取得異常信息描述:getMessage()方法。
import java.io.*;
public class ExceptionTest07{
public static void main(String[] args){
try{
FileInputStream fis = new FileInputStream("c:/ab.txt");
}catch(FileNotFoundException e){
msg=e.getMessage();
System.out.println(msg);
}
System.out.println("ABC");
}
}
編譯運行后輸出:
c:\ab.txt (系統找不到指定的文件。)
ABC
從上面的例子可以看出,e.printStackTrace()比e.getMessage()的方法詳細,前者輸出的異常信息比后者完整,因此一般使用e.printStackTrace()來打印異常信息。捕捉了異常之后進行打印才能知道你的代碼哪里出現了異常,才能不斷地去修改完善自己的代碼,也就是所說的一般使用e.printStackTrace()去調試程序。
接下來再補充一下方法的重寫與異常的知識。
這塊兒內容只要記着一個原則:重寫的方法不能比被重寫的方法拋出更寬泛的異常。結合以下例子來理解一下。
class A{
public void m1(){}
}
public class AB extends A{
//子類永遠無法比父類拋出更寬泛的異常
public void m1() throws Exception{}
}
上述代碼編譯運行后出錯:
AB.java:5: 錯誤: AB中的m1()無法覆蓋A中的m1()
public void m1() throws Exception{}
^
被覆蓋的方法未拋出Exception
1 個錯誤
這是因為子類永遠無法比父類拋出更寬泛的異常。再看一個例子:
import java.io.*;
class A{
public void m1()throws FileNotFoundException{}
}
public class AB extends A{
public void m1()throws IOException{}
}
編譯運行后出錯:
AB.java:22: 錯誤: AB中的m1()無法覆蓋A中的m1()
public void m1()throws IOException{}
^
被覆蓋的方法未拋出IOException
1 個錯誤
父類中的m1()方法拋出FileNotFoundException異常,AB繼承A,並且重寫m1()方法,拋出IOException的異常,而我們知道,FileNotFoundException繼承IOException,所以出錯。
搜索微信公眾號“程序員考拉”,歡迎關注!

