参考《第一行代码java》《java程序设计教程》
java中程序的错误有语法错误、语义错误。如果是语法性错误,在编译时就可以检查出来并解决。语义错误是在程序运行时出现的,在编译时没有错误,但在运行时可能会出现错误导致程序退出,这些错误称为异常。在没有异常处理的情况下,也即默认情况下,程序出现异常后会打印异常信息,同时终止执行异常之后的代码。为了让程序在出现异常后仍然可以正常执行完毕,必须引入异常处理语句完善代码。因此,异常处理机制用于检测和处理异常,提高java程序的健壮性。
在java中,所有的异常都当做对象处理,即当发生异常时产生了异常对象。java异常处理机制的语法为:
try{
//可能出现异常的语句
}catch(异常类型 对象){
//异常处理
}finally{
//不管是否出现异常,仍会统一执行的代码
}
try语句用于捕获可能出现异常的代码。catch语句根据不同的异常类型进行不同的异常处理,因此一个try语句可以对应多个catch语句。如果try语句中产生异常,程序会跳转到匹配异常类型的catch语句中,进行异常处理。不管程序是否产生异常,都会执行finally语句。finally语句可以省略。如果省略了finally语句,程序在执行完catch语句后,会继续向下执行。
catch语句和finally语句可以省略,但是不能同时省略。异常格式的组合通常有如下三种:try...catch, try...catch...finally, try...finally。
异常的继承结构
在java中,所有异常类型最高继承类是Throwable。Throwable下面有两个子类Error和Exception。java的异常子类命名都会使用***Error或***Exception的形式,开发者可以根据这个特征进行区分Error和Exception。
Error是JVM错误,属于不可查错误。出现Error时程序因无法处理,没有执行。
Exception是程序运行中的错误,用户可以使用异常处理格式处理。异常分为可查异常和不可查异常。
不可查异常是在运行中出现的异常。这类异常在编译中可能出现,也可能不出现,所以在程序中可以选择处理这类异常,或者选择不处理。RuntimeException和其子类属于不可查异常。
可查异常是在编译时被强制检查的异常。这种异常可以预见,因此必须在程序中进行处理,或进行捕获和处理,或抛出给上一级调用方法处处理。否则,编译将无法通过。RuntimeException以外的异常都属于可查异常。
常见异常
ArithmeticException:数学运算异常。
NullPointerException:空指针异常。
NegativeArraySizeException:数组大小为负值异常。
ArrayIndexOutOfBoundException:数组下标越界异常。
NumberFormatException:数字格式异常。
InputMismatchException:输入类型不匹配异常。
NoSuchMethodException:方法不存在异常。
DataFormatException:数据格式错误异常。
NoClassDefFoundError:未找到类定义错误。
OutOfMemoryError:内存不足错误。
StackOverflowError:堆栈溢出错误。
ThreadDeath:线程结束。
UnknownError:未知错误。
Throwable类的常用方法
public Throwable getCause() //返回Throwable对象的原因。
public String getMessage() //返回Throwable对象的详细信息
public void printStackTrace() //将此Throwable对象的堆栈跟踪输出至错误输出流,作为System.err的值
异常的处理流程
1、当程序出现异常,JVM自动根据异常的类型实例化一个与之类型匹配的异常类对象。
2、根据异常对象判断是否存在异常处理。如果不存在异常处理,则由JVM对异常默认处理:输出异常信息,结束程序调用。
3、如果存在异常捕获操作,try语句捕获异常类实例化对象,再与catch语句进行异常类型匹配,并处理异常。
4、不管是否匹配到catch语句,如果存在finally语句,就会执行finally语句代码。
5、finally语句后面的代码根据之前是否匹配到catch语句进行处理。如果匹配到catch语句,也即捕获到异常,则继续执行finally后的语句。如果没有匹配到catch语句,则将异常交由JVM默认处理。
异常捕获是一个异常类对象的传递过程,所有异常类对象都可以相父类对象转型。因此可以异常类对象可以使用Exception接收,简单实现异常处理。
对于异常分开处理还是一起处理的问题,没有严格的规范,而是根据项目开发标准决定。异常分开处理便于代码的维护,统一处理则可以节省开发精力。
在处理多个异常时,范围小的异常要放在范围大的异常之前处理。否则,范围小的异常会因异常处理完成而无法处理。
throws关键字
throws关键字用在方法的定义上,表示此方法不进行异常的处理,而是交给被调用处处理。
myMath.java
class myMath{
public static int div(int x, int y) throws Exception{ //该方法不处理异常
return x/y;
}
}
exception_throws.java
public class exception_throws{
public static void main(String args[]){
try{ //div()方法抛出异常,这里必须明确处理异常
System.out.println(myMath.div(10,0));
}catch(Exception e){
e.printStackTrace();
}
}
}
throw关键字
throw关键字用于用户手动抛出一个异常类实例化对象。
exception_throw.java
public class exception_throw{
public static void main(String args[]){
try{
throw new Exception("self-defined exception"); //直接抛出自定义异常类对象
}catch(Exception e){
e.printStackTrace();
}
}
}
异常处理的标准格式
除在上面提到的三种异常组合格式,还有一种是try...catch...finally结合throw和throws的异常处理格式。
class myMath{
public static int div(int x, int y) throws Exception{ //出现异常交给被调用处输出
System.out.println("===start===");
int result = 0;
try{
result = x / y;
}catch(Exception e){
throw e; //向上抛出
}finally{
System.out.println("===end===");
}
return result;
}
}
public class exception_standerd{
public static void main(String args[]){
try{
System.out.println(myMath.div(10,0)); //被调用处处理异常
}catch(Exception e){
e.printStackTrace();
}
}
}
在本例中,也可以将myMath.java中的catch语句省略。myMath.java中的try语句捕获的异常通过div()方法抛出给调用处处理。
RuntimeException类
RuntimeException的子类对象可以根据用户需要进行有选择的处理,程序在编译时不会强制性要求用户处理异常,所以在调用时不处理也不会有任何编译错误。如果没有处理却发生异常,则交给JVM默认处理。
例如Integer类中的parseInt()方法
public static int parseInt(String s) throws NumberFormatException;
异常类NumberFormatException属于RuntimeException子类。使用parseInt()时可根据需要处理Runtime子类异常。
public class exception_RuntimeException{
public static void main(String args[]){
int temp = Integer.parseInt("100");
System.out.println(temp);
}
}
assert关键字
assert在JDK1.4时候引入的,通常用于程序不准备使用捕获异常来处理的错误。其功能是在程序执行到某行后,判断是否是预期的结果。断言的语法格式:
assert 布尔表达式
assert 布尔表达式:字符串表达式
java默认没有开启断言功能。因此程序正式执行时不用去除断言语句。在执行解释java字节码文件时,增加参数“-ea”,开启断言功能。
java -ea exception_assert
public class exception_assert{
public static void main(String args[]){
int num = 10;
assert num == 20 : "num isn't 20";
System.out.println("num = " + num);
}
}
自定义异常
当java提供的异常类型不能满足开发需要时,可以自己开发一个异常类。实现自定义异常类,只需要继承Exception或RuntimeException父类即可。
class AddException extends Exception{
public AddException(String msg){
super(msg);
}
}
public class exception_selfdefine{
public static void main(String args[]){
int num = 20;
try{
if(num > 10){
throw new AddException("value is too large!");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
练习
填空
1、 Throwable下有哪两个子类: 。
2、 ArthmeticException类表示 异常,ArraysIndexOutOfBoundsException表示 异常。
3、 一个try代码后面必须跟着若干个 代码段或者一个 代码段。
4、 如果一个方法使用了 ,则编译器会强制在使用此方法时进行异常的处理。
5、 异常处理中使用 作为异常的统一出口。
选择
1、 使用那个关键字可以在程序中手工抛出异常 。
A、 throws B、 throw C、 assert D、 class
2、 下面 关键字可以用在方法的声明处?
A、 throws B、 assert C、 class D、 interface
3、 为了捕获一个异常,代码必须放在下面 语句块中。
A、 try块 B、 catch块 C、 throws块 D、 finally块
4、 下面关于try块的说法正确的是 。
A、 try块后至少应有一个catch块 B、 try块后必须有finally块
C、 可能抛出异常的方法应放在try块中 D、 对抛出的异常的处理应放在try块中
5、 finally块中的代码将 。
A、 总是被执行
B、 如果try块后面没有catch块时,finally块中的代码才会执行
C、 异常发生时才被执行
D、 异常没有发生时才执行
6、 一个异常将终止 。
A、 整个程序 B、 只终止抛出异常的方法
C、 产生异常的try块 D、 上面的说法都不对
7、 所有异常的共同父类是 。
A、 Error B、 Exception C、 Throwable D、 RuntimeException
判断
1、 一个try语句后有多个catch时,捕获范围大的异常要放在捕获范围小的异常之后。 ( )
2、 finally语句可以根据需要有选择的添加。 ( )
简答
1、 简述RuntimeException和Exception的区别。
2、 try、catch、finally三种语句的功能是什么?
3、 简述Java中的异常处理机制。
4、 简述Error和Exception的区别。
5、 列举三个常见的RuntimeException子类。
编程
1、 编写应用程序,从命令行输入两个小数参数,求它们的商。要求程序中捕获NumberFormatException异常和ArithmeticException异常。