一、异常体系的引入和优点
1.引入:
异常是导致程序中断执行的一种指令流,程序中如果出现异常而没有及时处理就会导致程序终止执行。Java语言提供了一套完整的异常处理机制。正确运用这套机制,有利于提高程序的健壮性。所谓程序的健壮性,就是指程序在多数情况下能够正常运行,返回预期的正确结果;如果偶尔遇到异常情况,程序也能采取周到的解决措施。而不健壮的程序则没有事先充分预计到可能出现的异常,或者没有提供强有力的异常解决措施,导致程序在运行时,莫名其妙地被终止,或者返回错误的运行结果。
2.优点:
(1)把各种不同类型的异常情况进行分类,用Java类来表示异常情况,这种类被称为异常类。把异常情况表示成异常类,可以充分发挥类的可扩展和可重用的优势。
(2)异常流程的代码和正常流程的代码分离,提高了程序的可读性,简化了程序的结构。
(3)可以灵活地处理异常,如果当前方法有能力处理异常,就捕获并处理它,否则,只需抛出异常,由方法调用者来处理它。
方法调用者:methodB调用了methodA,则methodB被称为methodA的方法调用者。
二、异常的分类
异常主要分为Error类和Exception类,它们的父类都是Throwable类。
Error类异常,是非受查异常,Java编译器并不会对这类异常进行检查。出现Error类的程序不得不终止,它主要指Java运行时内部错误和资源耗尽错误。如:递归调用占用了所有的空间。程序自己终止。程序员只能检查代码,使内部不出错。
而Exception异常又分为IOException(受查异常)和RuntimeException(非受查异常)。
受查异常:表示程序可以处理的异常,如果抛出异常的方法体不能处理它,那么方法的调用者将处理它。只要通过处理,就可能使程序恢复运行的异常。
非受查异常:Java编译器不会对这类异常进行检查。
每种异常都有它们各自对应的子类。如:
Throwable:(1)Error (2)Exception
(1)Error(非受查异常) : AWTError和VirtualMachineError
(2)Exception:IOException(受检查异常) RuntimeException(非受查异常)
IOException(操作输入流和输出流时可能出现的异常): EOFException FileNotFoundException
RuntimeException(运行时异常): IndexOutOfBoundsException 下标越界异常
ArithmeticException 数学异常
NullPointerException 空指针异常
ClassCastException 类型转换异常
IllegalArgumentException 非法参数异常
(3)用户自定义异常。通常可以是用new RuntimeException的子类定义。
三、异常处理的格式和语法
3.1 通常有try...catch...,try...finally和try...catch...finally三种格式。
try{
可能会出现异常情况的代码;
}catch(SQLException e){
处理操作数据库出现的异常;
}catch(IOException){
处理操作输入流和输出流出现的异常;
}finally{
最后都必须执行的代码;
}
或者
try{
有可能出现异常的代码;
}catch(异常类 异常类的对象){
处理异常的代码;
}.......]
[finally {
最后执行的代码
}]
或者
try{
可能会出现异常的代码;
}finally{
最后执行的代码;
}
注意:
1.当使用try...catch..finally语句时,finally会作为程序统一出口。
2.finally语句不会执行的情形:a.在它执行前,调用了系统退出函数System.exit(int 退出码)。
public static void exit(int status);//参数status表示程序终止的状态码,通常零表示正常终止,非零表示异常终止。
b.外部出现不可逆因素。如:断电
不会执行finally的代码块的代码示例:
public void methodA() {
try {
System.out.println(10 / 0);
} catch (ArithmeticException e) {
System.out.println("异常:");
e.printStackTrace();
System.exit(2);
} finally {
System.out.println("finally");
}
}//将不会输出finally,因为使用System.out(2)调用系统退出函数。
3.2 用throws子句声明可能会出现异常的方法
如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来抛出异常。throws子句允许抛出多个异常,多个异常间用“,”隔开。
注意:当一个方法用throws声明,调用它的方法应捕获异常。
throws还可以用来声明主方法可能出现的异常,此时产生的异常将由Java虚拟机处理。
package com.xunpu.abnormity;
public class Abnormity {
public static void main(String[] args) throws Exception {
try{
calculate(3,0);
}catch(Exception e){
e.printStackTrace();
}
}
public static int calculate(int x,int y) throws Exception{
return x/y;
}
}
结果:
java.lang.ArithmeticException: / by zero
at com.xunpu.abnormity.Abnormity.calculate(Abnormity.java:13)
at com.xunpu.abnormity.Abnormity.main(Abnormity.java:7)
Process finished with exit code 0
3.3 用throw语句抛出异常
throw直接编写在语句之中,表示人为地将异常抛出。如果现在异常类对象实例化不希望由JVM产生而由用户产生,就可以使用throw语句来完成。但要注意,用throw语句抛出的对象必须是java.lang.Throwable或者其子类(见异常分类图)的实例。
package com.xunpu.abnormity;
public class Abnormity {
public Abnormity(){}
public void methodA(int data) {
int num = 4 / data;
System.out.println(num);
}
public static void main(String[] args) throws Exception {
try {
System.out.println("Begin");
Abnormity s=new Abnormity();
s.methodA(0);//出现异常
System.exit(0);//因为出现异常,所以这句话将不会再执行。
}catch(Exception e){
System.out.println("Wrong");
throw e;//抛出错误信息
}finally {
System.out.println("Finally");
}
System.out.println("End");
}
}
结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.xunpu.abnormity.Abnormity.methodA(Abnormity.java:5)
at com.xunpu.abnormity.Abnormity.main(Abnormity.java:13)
Begin
Wrong
Finally
如果没有throw e这行代码,输出结果将完全不同,它会输出
Begin
Wrong
Finally
End
这是因为throw抛出异常后,当前函数的执行将被停止。而finally是程序异常最后执行的代码,因此只会打印除Finally,而不会打印"End"。
注意:throw和throws子句的区别
(1)使用场景不同:throws用于可能出现异常的方法声明中,而throw出现在代码中,可以用于抛出异常(可以是自定义的,也可以是已知的异常)。
(2)处理异常与否:throws在方法声明中会告诉用户可能产生的异常,同时该方法可能不处理此异常。而throw可以在catch代码块处处理异常。
3.4.异常处理语句的语法规则
(1)try后面可以有零个或多个catch代码块,还可以有零个或至多一个finally代码块。
(2)try在try代码块中定义的变量的作用域为try代码块,在catch代码块和finally代码块中不能访问该变量。要想访问,则最好在try代码块外定义该变量。
(3)当有多个catch语句时,Java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类进行匹配。如果实际的异常对象和catch中的异常对象类型相同,则会执行该catch代码块,其它的catch代码块将不会再执行。
(4)从JDK1.7开始,为简化编程,允许在catch子句中同时捕获多个不同类型的异常,用符号“|”来分割。
(5)如果一个方法可能出现受检查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出。否则会出现编译错误。判断一个方法可能会出现的异常:方法中或调用的其它方法中有throw子句。
(6)从JDK1.7开始,如果在catch子句中捕获的异常被声明为final类型时,那么当catch代码块继续抛出该异常时,可以不用在定
义方法时,用throws子句声明将它抛出。
(7)throw语句(可能在try或catch代码块中)后面不允许再跟其它语句,因为这些语句永远不会被执行。
(8)return语句用于退出本方法,在执行try或catch代码块中的return语句时,如果有finally语句,一定要先执行finally语句中的代码,再退出。虽然finally语句在return语句前执行,但是不能通过finally代码重新给变量赋值的方法改变返回的值。如:
int a=0;
try{
Sample s=new Sample();
s.methodA(0);//出现异常
return a;
}catch(Exception e){
System.out.println("Wrong");
return a;
}finally{
a=6;//虽然执行,但是不影响最后的返回值仍然是0.
}
(9)建议不要在finally语句中使用finally语句,它会导致两种结果:a.覆盖try或catch中的return语句。b.丢失异常。如:
a.覆盖try或catch中的return语句
try {
int a=5;
Sample s=new Sample();
s.methodA(0);//出现异常
return 5;
}catch(Exception e){
throw e;
return a;
}finally {
return 10;
}//最后返回值是10.
b.丢失异常
try {
s.methodA(0);//出现异常
}catch(Exception e){
throw e;
}finally {
return 10;
}//结果没有捕获到方法A的异常。
3.5 与异常相关的方法
3.5.1访问异常信息的方法
getMessage();//返回String类型的异常信息。
printStackTrack();//打印跟踪方法调用栈而获得的详细异常信息。在程序调试阶段,该方法还可以用于跟踪方法。
3.5.2 跟踪丢失的异常
如果catch和finally语句都抛出异常,那么最后只输出finally抛出的异常。这样,catch的异常就会丢失。在JDK1.7中,提供了
public final void addSuppressed(Throwable exception)//把差点丢失的方法保存了起来。
public final Throwable[] getSuppressed();该方法能返回所有保存下来的异常。
四、断言(主要用于程序调试阶段)
1.定义:断言指的是当程序执行到某些语句之后其数据的内容一定是约定的内容。实际上断言的意义不是很大。
2.形式:
assert 条件表达式;
assert 条件表达式:包含错误信息的表达式
2.说明:
条件表达式的值是一个boolean值,当值为false时,就会抛出一个AssertionError,这是一个错误,不是异常。在第二种
形式中,第二个表达式会被转换为错误信息的字符串。
3.参数
当程序运行时,默认断言是关闭的。参数 java -ea:启用断言 java -da: 禁用断言
参数在IDEA运行前的Run菜单下Edit Configurations...的VM options中设置。
java -ea:MyClass1 -da:MyClass2 AppMain //表示在运行AppMain类时,启用MyClass1类的断言,禁用MyClass2类的断言
package com.xunpu.abnormity;
public class Abnormity {
public static void main(String[] args) {
int num=0;
assert num==90:"错误,num应当为90";
System.out.println(num);
}
}
启用断言,结果输出
����: �Ҳ��������������� java
Process finished with exit code 1