【左京淳的JAVA学习笔记】第八章 例外处理

本章重点

  • 例外和例外的处理
  • 例外处理class
  • try-catch-finally
  • throws和throw
  • over write

JAVA的source file即使编译成功,在执行时也可能出错,此时的报错称为例外。
例外由负责执行程序的JVM发出给使用者知道。如果没有指定应对方法的话,程序就会直接中断。

对应例外的class阶层如下
Throwable(全部例外处理的父类)

  • Error //记忆体不足等严重错误,难以由程序进行对应所以直接报error结束。
  • Exception //可对应的例外
    • RuntimeException //直译就是跑程序时发生的例外,不一定要指定对应方式。
    • 非RuntimeException例外 //又称作checked例外,必须指定对应方式。通常是外部的档案或DataBase找不到。

来看看各例外class下面常见的子class

Error class

class 说明
AssertionError assert(条件)返回false时终止程序
OutOfMemoryError 记忆体不足
StackOverflowError 重复回圈数过多
NoClassDefFoundError 找不到class档

RuntimeException class

class 说明
ArrayIndexOutOfBoundsException index值超出阵列/列表范围
ClassCastException 强制转换物件的class时出错
IllegalArgument Exception 引数格式不对
ArithmeticException 除数为0
NullPointerException 方法执行时引用到null资料
NumberFormat Exception 资料转换为整数时报错

非RuntimeException class

class 说明
IOException 输入输出资料时出错
FileNotFoundException 找不到档案
ClassNotFoundException 找不到class

除了JAVA内建的例外class,开发者也可以自定义class如下例:

public class MyException extends Exception{}

继承Exception类可以获得以下方法

  • void printStackTrace() //显示例外发生的地方
  • String getMessage() //取得例外发生的原因等相关资讯

例外的处理方式有以下两种:

  • try-catch-finally
  • throws

先来看看try-catch-finally的文法和范例
文法如下:

void method(){
  try{
    可能发生例外的地方
  }catch(例外处理的class 变数){
    例外发生时如何处理
  }finally{
    不管有没有例外都要进行的处理
  }
}

使用时未必三种关键字都要写,catch和finally择一即可。

范例:

class Sample8_1 {
  public static void main(String[] args) {
    int[] num = {10,20,30};
    for (int i = 0; i < 4; i++){
      try{
        System.out.print("num :" + num[i]);
        System.out.println(" : 第" + (i + 1) + "次的回圈");
      }catch(ArrayIndexOutOfBoundsException e){
        System.out.println("例外发生!");
      }
    }
    System.out.println("--end--");
  }
}

执行结果

num :10 : 第1次的回圈
num :20 : 第2次的回圈
num :30 : 第3次的回圈
例外发生!
--end--

如果把阵列缩短一点,执行结果如下

num :10 : 第1次的回圈
num :20 : 第2次的回圈
例外发生!
例外发生!
--end--

添加了finally的范例如下:

class Sample8_2 {
  public static void main(String[] args) {
    int[] num = {10,20,30};
    for (int i = 0; i < 4; i++){
      try{
        System.out.print("num :" + num[i]);
        System.out.println(" : 第" + (i + 1) + "次的回圈");
      }catch(ArrayIndexOutOfBoundsException e){
        System.out.println("例外发生!");
      }
    }
    System.out.println("--end--");
  }
}

执行结果

num :10 : 第1次的回圈
--finally--
num :20 : 第2次的回圈
--finally--
num :30 : 第3次的回圈
--finally--
例外发生!
--finally--
--end--

catch处理可以指定多个例外处理class,但若class之间有继承关系,则须从子类到父类(范围从小到大)撰写,否则会编译错误(因为不合逻辑)。
会报错的范例:

}catch(Exception e){
  ...
}catch(NullPointerException e){
  ...
}

正确的范例

}catch(NullPointerException e){
  ...
}catch(Exception e){
  ...
}

若是没有继承关系的class,则可以"|"符号组合起来。

}catch(FileNotFoundException | ArithmeticException e){

用throws传递例外

有时候遇到例外时我们不想当场处理,可以使用throws来传递给上一层或更上层,最後再用try-catch来处理它。
请看以下范例:

class DBAccess{
  void select(...){
    try{
      ...
    }catch{
      ...
    }
  }
  void insert(...){
    try{
      ...
    }catch{
      ...
    }
  }
}

上面是一个访问DB的class,里面有复数的方法存在。如果每一个都要写try-catch的话,程序码会变得很冗长。
可以利用throws改写为以下样子

class DBAccess{
  void select(...) throws SQLException{
  }
  void insert(...) throws SQLException{
  }
}

然後在呼叫出此物件的上层,利用try-catch进行处理

class Test{
  DBAccess d = new DBAccess();
  try{
    d.select(...);
    d.insert(...);
  }catch(SQLException e){
    // get log
  }
}

如上例,如果物件里有throws方法的话,使用这些方法时就必须使用try-catch包覆起来。
JAVA里面的工具类也有这样的案例,例如:
java.io包提供的FileReader类的建构式,发生例外时会throws FileNotFoundException。
这个例外不属於RuntimeException的子类,所以"必须"指定例外处理方法。
java.lang包提供的Integer类的parseInt()方法出错时会throws NumberFormatException
这个例外属於RuntimeException的子类,所以不一定要指定例外处理方法。

关於是不是一定要指定例外处理方法,请看以下范例:

import java.io.*;

class Sample8_2 {
  public static void main(String[] args) {
    //不一定要处理的例外
    int i = Integer.parseInt("10");
    
    //一定要处理的例外
    FileReader r = new FileReader("Test.txt");
}

编译结果

Sample8_2.java:9: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
    FileReader r = new FileReader("Test.txt");
                   ^
1 error

必须处理的例外,如果没有用try-catch包覆,编译时就会报错。

用throw手动抛出例外

throw和前面说的throws是不一样的指令,请看清楚了。
有些时候我们想要自己定义什麽样的值是正常的,什麽样的值是异常值需要进行例外处理。
这时候就可以使用throw指令丢出例外并终止後续的处理。

class MyException extends Exception {
  private int age;
  public void setAge(int age){this.age = age;}
  public int getAge(){return this.age;}
}
class Sample8_3 {
  public static void main(String[] args) {
    try{
      int age = -10;
      checkAge(age);
    }catch(MyException e){
      System.out.println("年龄异常。age:" + e.getAge());
    }
  }
  
  public static void checkAge(int age) throws MyException{
    if (age >= 0){ 
      System.out.println("OK");
    }else{
      MyException e = new MyException();
      e.setAge(age);
      throw e;
    }
  }
}

执行结果

年龄异常。age:-10

覆写具有throws的方法时,须注意的规则:

当我们要写一个子类来继承父类,并覆写具有throws的方法时,须注意以下规则:
子类方法可以跟父类丢出一样的例外处理,或是不写throws,或是改写为符合以下规则的处理

  1. 比之前的例外处理范围更小的例外处理(也就是其子类)。可以想像子类应该是要比父类更精确地描述例外。
  2. RuntimeException

来看看范例:

import java.io.*;

class Super{ void method() throws IOException{} }

class SubA extends Super { void method() {} }
class SubB extends Super { void method() throws FileNotFoundException {} }
class SubC extends Super { void method() throws Exception{} }
class SubD extends Super { void method() throws ClassNotFoundException{} }
class SubE extends Super { void method() throws RuntimeException{} }

编译结果

class SubC extends Super { void method() throws Exception{} }
                                ^
  overridden method does not throw Exception
Sample8_4.java:8: error: method() in SubD cannot override method() in Super
class SubD extends Super { void method() throws ClassNotFoundException{} }
                                ^
  overridden method does not throw ClassNotFoundException
2 errors

说明
SubA没有写throws,OK
SubB抛出FileNotFoundException,是IOException的子类,OK。
SubC抛出Exception范围大於Super的IOException因此报错
SubD抛出ClassNotFoundException与 Super的IOException没有继承关系,而且也不是RuntimeException,所以报错。
SubE抛出RuntimeException,OK。

以上 是关於JAVA的学习笔记,到此结束。之後会整理JAVA Web的学习笔记(虽然还很菜..希望整理得出来)

参考教材: JAVAプログラマSilver SE8 - 山本 道子


<<:  NIST SP 800-53A(附录E:渗透测试)

>>:  进击的软件工程师之路-前言

ASP.NET MVC 从入门到放弃(Day19)-MVC模型(Model)介绍

接下来讲讲Model 部分... 简单来说Model负责与资料库沟通的相关逻辑,或者定义模板(.cs...

无限风光在险峰 结案会议

无限风光在险峰 结案会议 to-do-List PLus 可以多人协作 to-do-list , 在...

计算资源及资料的设定03

在上一个章节,我们已经建立了一个Datatstore及Container,也上传资料到Contain...

【D31】回顾这两天的讯号灯与大盘的关系

前言 做出简单的讯号灯後,来藉由昨天的讯号以及今天的讯号,来验证今天的盘势以及推估下周一的可能性,来...

[Day11]C# 鸡础观念- 把复杂的事情与关系简单化~列举与结构

在电脑世界中,很多编码都是复杂且看不懂的, 例如颜色编码,FF0000代表红色,800080代表绿色...