Java中異常的處理
Java中的異常
分為Error和Exception,其中Error是指Java虛擬機器無法解決的嚴重問題,Exception是指外在因素導致的一般性問題,這裡我們討論的是Exception。
常見的Exception
有空指標異常、陣列下標越界異常、型別轉換異常、算術異常。
Exception的處理
抓拋模型:抓是指抓住上一步丟擲的異常;拋是指程式碼執行出現異常,將會在異常處生成一個對應的異常類物件,並將此物件跑出給方法的呼叫者。三種方式來處理,第一種時try-catch-finally(finally為可選),第二種是在方法定義的後面追加throws xxException,其中try-catch-finally是在此處真正處理掉異常的,以後如果再有方法呼叫這個異常程式碼塊直接呼叫即可,不用做處理;而throws xxException形式的處理方式,只是單純的丟擲異常,並未做實際處理,所以沒有隻要有方法呼叫這個異常程式碼塊還是要做異常處理,因為這個異常程式碼處理異常的方式是將異常拋給方法的呼叫者了。第三種是自定義異常類,用關鍵字throw手動丟擲,如下兩個程式碼段:
Scanner is = new Scanner(System.in);
try {
int s = is.nextInt();
System.out.println(s);
} catch (InputMismatchException e) {
System.out.println("型別轉換出錯了!");
} finally {
System.out.print("我是finally");
}
再看
public class TestExceptionWithThrows {
//這裡mian()方法接收到method2丟擲的異常,需要對他進行處理,如果這裡在採用throws的繼續往上丟擲的話
//那麼異常將會拋給JVM來處理,但是可以看出throws這樣的宣告式的丟擲異常的方式並不是處理異常最根本的方法,最根本的方法應該
//是try-catch這樣的方式,而且如何在異常程式碼塊已經執行了try-catch方式處理過後,那麼呼叫它的方法將不用再最它的異常進行處理
//不想throws方式不斷往上丟擲,上一級還要不斷的對下一級的異常進行處理
public static void main(String[] args){
try {
method2();
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//這裡method()方法將異常拋給了method2(),然後這裡method2()也是採用throws的方式將異常往上一級丟擲
public static void method2 () throws FileNotFoundException,IOException {
method();
}
//這裡將異常拋給呼叫者
public static void method() throws FileNotFoundException,IOException {
FileInputStream fis = new FileInputStream(new File("hellow.text"));
int b;
while((b = fis.read())!=-1){
System.out.println((char)b);
}
fis.close();
}
}
其中finally中的程式碼無論是否發生異常,都肯定會執行的,而且注意他們的執行順序是先執行try{}中的程式碼塊,如何發生異常,那麼直接跳到finlly{}的程式碼塊,先執行finally中的程式碼塊,然後再返回來處理異常,執行catch{}中的程式碼塊,如果沒有finally{}程式碼塊,則先處理異常再執行後續程式碼,並且如果有多個異常,那麼catch的時候,就會從上到下的尋找對應的異常處理方式,處理完退出catch塊,並且這裡注意多個catch()中的異常順序從上到下必須是範圍小的異常到範圍大的異常。如
catch(FileNotFoundException e){
}catch(Exception e){
}
這裡看一下綜合程式碼
public class ReturnExceptionDemo {
static void methodA() {
try{
System.out.println("進入方法A");
//手動丟擲一個異常
throw new RuntimeException("製造異常");
} finally {
System.out.println("呼叫A方法的finally");
}
}
static void methodB() {
try{
System.out.println("進入方法B");
return;
} finally {
System.out.println("呼叫B方法的finally");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
methodB();
}
}
它的執行結果為:
進入方法A
呼叫A方法的finally
製造異常
進入方法B
呼叫B方法的finally
這裡需要注意的還有就是當try{}程式碼塊中有return時,finally{}塊中的程式碼還是會執行,執行後才會執行return,執行完return之後該方法將會被銷燬。
這裡除了手動丟擲已存在的異常,我們還可以自定義異常,自定的異常一定要繼承已有的異常類,如Exception或者RuntimeException,然後重寫父類的構造方法,一般只要重寫一下(String msg, Throwable)和(String msg)就可以了,其他都不需要動,如下:
//自定義的異常類,必須繼承現有的異常類,提供幾個過載的構造器即可
public class MyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MyException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public MyException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
呼叫異常
/**
* 手動丟擲異常,自定義異常的資訊
* @author Weiguo Liu
*
*/
public class TestExceptionWithHand {
public static void main(String[] args) {
Circle c1 = new Circle(2.1);
Circle c2 = new Circle(2.1);
System.out.println(c1.compareTo(c2));
System.out.println(c1.compareTo(new String ("asda")));
}
}
class Circle {
private double radius;//圓的半徑
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public Circle(double radius) {
super();
this.radius = radius;
}
public int compareTo(Object obj) {
if(this == obj) {
return 0;
}else if(obj instanceof Circle){
Circle c = (Circle)obj;
if(this.radius>c.radius){
return 1;
}else if(this.radius == c.radius){
return 0;
}else{
return -1;
}
}else{
//手動的丟擲異常
throw new MyException("傳入的型別有誤!");
}
}
}