目录标题
异常的基本概念
异常是导致程序中断运行的一种指令流。如果不对异常进行正确的处理,则可能导致程序的中断执行,造成不必要的损失,所以在程序的设计中必须要考虑各种异常的发生,并正确的做好相应的处理,这样才能保证程序的正常执行。
public class ExceptionDemo01{
public static void main(String args[]){
System.out.println("********** 计算开始 ***********") ;
int i = 10 ; // 定义整型变量
int j = 0 ; // 定义整型变量
int temp = i / j ; // 此处产生了异常
System.out.println("两个数字相除的结果:" + temp) ;
System.out.println("********** 计算结束 ***********") ;
}
};
产生异常之后的语句没有执行。
一旦产生异常,异常之后的语句并不会执行,而是直接结束程序,并将错误报告出来给用户。
异常的基本处理格式
try中捕获异常,捕获之后与catch中的异常相匹配,如果匹配成功则执行catch中的语句;不管匹配成功不成功最后都执行finally语句,从finally语句中出去。
public class ExceptionDemo02{
public static void main(String args[]){
System.out.println("********** 计算开始 ***********") ;
int i = 10 ; // 定义整型变量
int j = 0 ; // 定义整型变量
try{
int temp = i / j ; // 此处产生了异常
System.out.println("两个数字相除的结果:" + temp) ;// 未执行
System.out.println("----------------------------") ; // 未执行
}catch(ArithmeticException e){
System.out.println("出现异常了:" + e) ;
}
System.out.println("********** 计算结束 ***********") ;
}
};
产生异常之后的语句没有执行。
异常类的继承结构
一般情况下,开发者习惯于将Exception和Error统称为异常。
java异常处理机制
注意点:
捕获更粗的异常,要放在更细的异常之后。
public class ExceptionDemo06{
public static void main(String args[]){
System.out.println("********** 计算开始 ***********") ;
int i = 0 ; // 定义整型变量
int j = 0 ; // 定义整型变量
try{
String str1 = args[0] ; // 接收第一个参数
String str2 = args[1] ; // 接收第二个参数
i = Integer.parseInt(str1) ; // 将第一个参数由字符串变为整型
j = Integer.parseInt(str2) ; // 将第二个参数由字符串变为整型
int temp = i / j ; // 此处产生了异常
System.out.println("两个数字相除的结果:" + temp) ;
System.out.println("----------------------------") ;
}catch(ArithmeticException e){ // 捕获算术异常
// System.out.println("算术异常:" + e) ;
e.printStackTrace() ;
}catch(NumberFormatException e){ // 捕获数字转换异常
System.out.println("数字转换异常:" + e);
}catch(ArrayIndexOutOfBoundsException e){ // 捕获数组越界异常
System.out.println("数组越界异常:" + e) ;
}catch(Exception e){
System.out.println("其他异常:" + e) ;
}
System.out.println("********** 计算结束 ***********") ;
}
};
如果不遵循这个更粗的异常放在更细的异常之后,会怎么样?
编译就会报错:exception has already been caught。
public class ExceptionDemo07{
public static void main(String args[]){
System.out.println("********** 计算开始 ***********") ;
int i = 0 ; // 定义整型变量
int j = 0 ; // 定义整型变量
try{
String str1 = args[0] ; // 接收第一个参数
String str2 = args[1] ; // 接收第二个参数
i = Integer.parseInt(str1) ; // 将第一个参数由字符串变为整型
j = Integer.parseInt(str2) ; // 将第二个参数由字符串变为整型
int temp = i / j ; // 此处产生了异常
System.out.println("两个数字相除的结果:" + temp) ;
System.out.println("----------------------------") ;
}catch(Exception e){
System.out.println("其他异常:" + e) ;
}catch(ArithmeticException e){ // 捕获算术异常,上面Exception已经捕获了异常,这里的异常永远不会执行到
// System.out.println("算术异常:" + e) ;
e.printStackTrace() ;
}
System.out.println("********** 计算结束 ***********") ;
}
};
问题1:
既然所有的异常都可以使用Exception接收(都可以发生向上转型关系),那么所有的异常直接使用Exception进行接收不是更方便么?
public class ExceptionDemo08{
public static void main(String args[]){
System.out.println("********** 计算开始 ***********") ;
int i = 0 ; // 定义整型变量
int j = 0 ; // 定义整型变量
try{
String str1 = args[0] ; // 接收第一个参数
String str2 = args[1] ; // 接收第二个参数
i = Integer.parseInt(str1) ; // 将第一个参数由字符串变为整型
j = Integer.parseInt(str2) ; // 将第二个参数由字符串变为整型
int temp = i / j ; // 此处产生了异常
System.out.println("两个数字相除的结果:" + temp) ;
System.out.println("----------------------------") ;
}catch(Exception e){
System.out.println("其他异常:" + e) ;
}
System.out.println("********** 计算结束 ***********") ;
}
};
当所有的异常的处理方式都是一样的时候,就可以直接使用Exception进行捕获,当然在一个比较细致的开发中,是不建议这样使用的,所有的异常最好分别进行捕获。
问题2:
既然捕获Exception是最方便的,那直接捕获Exception的父类Throwable是不是更方便?
首先,这样的做法是可以的,也确实更方便了,但是也有问题,因为try中永远只会抛出Exception的子类,而Throwable的子类不仅仅有Exception还有Error。所以,使用Throwable捕获到的异常会不是很明确。
关键字throws与throw的作用
throws的作用
在定义一个方法的时候可以使用throws关键字声明,使用throws声明的方法表示此方法不处理异常,而交给方法的调用处进行处理。
假设定义一个除法的方法div(),对于除法操作来说操作的时候可能出现异常也可能不出现异常,多以对于这样的方法最好将它使用throws关键字进行声明,一旦出现了异常,则应该交给调用处处理。
class Math{
public int div(int i,int j) throws Exception{ // 定义除法操作,如果有异常,则交给被调用处处理
int temp = i / j ; // 计算,但是此处有可能出现异常
return temp ;
}
};
public class ThrowsDemo01{
public static void main(String args[]){
Math m = new Math() ; // 实例化Math类对象
System.out.println("除法操作:" + m.div(10,2)) ;
}
};
因为div方法使用了throws关键字声明,所以在调用此方法的时候,必须进行异常处理。否则会出现编译时错误
class Math{
public int div(int i,int j) throws Exception{ // 定义除法操作,如果有异常,则交给被调用处处理
int temp = i / j ; // 计算,但是此处有可能出现异常
return temp ;
}
};
public class ThrowsDemo01{
public static void main(String args[]){
Math m = new Math() ; // 实例化Math类对象
try{
System.out.println("除法操作:" + m.div(10,2)) ;
}catch(Exception e){
e.printStackTrace() ; // 打印异常
}
}
};
如果在主方法上也使用throws关键字声明,是不是意味着主方法也可以不处理异常?是否可行?
class Math{
public int div(int i,int j) throws Exception{ // 定义除法操作,如果有异常,则交给被调用处处理
int temp = i / j ; // 计算,但是此处有可能出现异常
return temp ;
}
};
public class ThrowsDemo02{
// 在主方法中的所有异常都可以不使用try...catch进行处理
public static void main(String args[]) throws Exception{
Math m = new Math() ; // 实例化Math类对象
//System.out.println("除法操作:" + m.div(10,2)) ;
System.out.println("除法操作:" + m.div(10,0)) ;
}
};
下属做事情出错,直属领导负责,这个领导负责不了进行上报,都处理不了都会逐级上报,异常处理也是这样的机制。
上面的代码中主方法不处理任何异常了,而是交给最大的头,JAVA中最大的头就是JVM,交给JVM处理了,在主方法使用throws关键字声明,则表示一切的异常交给JVM去处理,默认的处理方式也是JVM进行处理的,也就是不做任何异常处理的时候,默认就是如果出现了异常,是jvm直接去处理的。
throw的作用
throw关键字的作用是在程序中抛出一个异常。抛出的时候抛出的是一个异常类的实例化对象。
在异常的处理中,try语句中要捕获的是一个异常的对象,那么此异常对象也可以自己抛出。
public class ThrowDemo01{
public static void main(String args[]){
// 有异常了必须进行处理
throw new Exception("自己抛着玩的。") ; // 抛出异常的实例化对象
}
}
};
public class ThrowDemo01{
public static void main(String args[]){
try{
throw new Exception("自己抛着玩的。") ; // 抛出异常的实例化对象
}catch(Exception e){
System.out.println(e) ;
}
}
};
例子2:
class Math{
public int div(int i,int j) throws Exception{ // 定义除法操作,如果有异常,则交给被调用处处理
System.out.println("***** 计算开始 *****") ;
int temp = 0 ; // 定义局部变量
try{
temp = i / j ; // 计算,但是此处有可能出现异常
}catch(Exception e){ // 抛出异常,调用处会捕获这里抛出的异常
throw e ;
}finally{ // 不管是否有异常,都要执行统一出口
System.out.println("***** 计算结束 *****") ;
}
return temp ;
}
};
public class ThrowDemo02{
public static void main(String args[]){
Math m = new Math() ;
try{
System.out.println("除法操作:" + m.div(10,0)) ;
}catch(Exception e){
System.out.println("异常产生:" + e) ;
}
}
};
当有异常产生的时候,会先执行finally,执行完finally再返回方法调用处,在方法调用处处理该异常。
所以,使用throws声明的方法中的try–catch(throw e)—fially执行顺序是,try中捕获到了异常后执行catch抛出异常,然后执行finally,最后返回方法调用处,执行方法调用处的try-catch操作。
Exception与RuntimeException的区别
parseInt()
方法声明时候抛出了NumberFormatException
这个异常就是RuntimeException
的子类。
public class RuntimeExceptionDemo01{
public static void main(String args[]){
String str = "123" ; // 定义字符串,全部由数字组成
int temp = Integer.parseInt(str) ; // 将字符串变为int类型
System.out.println(temp * temp) ; // 计算乘方
}
};
问题:
parseInt()
方法声明时候也使用throws
抛出了异常,为什么可以不使用try-catch
处理可以直接运行成功呢?不是说方法使用throws
关键字声明以后,产生的异常会抛出给调用处需要在调用处处理,不管程序是否会发生都需要使用try--catch
进行处理否则编译会报错提示必须捕获该异常“must be caught or decleared to be thrown”
么?为什么这个例子没有try-catch
捕获也没有报错直接运行成功了?
也就是说对于Exception
的异常来说,如果不进行try---catch
处理程序直接中断执行,异常退出了。
但是对于RuntimeException
的异常来说,可以不进行try--catch
处理,这个过程不是硬性的要求,因为JVM
默认会对RuntimeException
的异常进行处理。
没有异常的时候程序正常执行,有异常发生后,jvm
直接打印了该异常,程序也中断执行了。
所以总结来说:
对于Exception
,就算程序没有发生异常,不try-catch
处理编译直接回报错;
对于RuntimeException
可以不使用try-catch
进行捕获处理,当程序没有发生异常的时候,程序正常执行;当有异常发生以后程序抛出异常给了JVM
,jvm
打印了该异常信息,程序中断执行了。所以对于RuntimeException
的异常最好也使用try-catch
进行处理。
ArithmeticException 算术异常也是RuntimeException。
自定义异常类
只要继承Exception类就可以实现自定义异常类。java中提供的都是标准异常,如果要定义自己的异常信息就可以自定义异常。
只要直接继承Exception类即可。
class MyException extends Exception{ // 自定义异常类,继承Exception类
public MyException(String msg){
super(msg) ; // 调用Exception类中有一个参数的构造方法,传递错误信息
}
};
public class DefaultException{
public static void main(String args[]){
try{
throw new MyException("自定义异常。") ; // 抛出异常
}catch(Exception e){
System.out.println(e) ;
}
}
}
断言
断言是在JDK1.4之后出现的,断言就是断定某一个操作的结果肯定是正确的,如果程序执行到出现断言语句的时候发现结果不正确了,则会出现错误的信息。
public class Test{
public static void main(String args[]){
int x[] = {1,2,3} ; // 定义数组,长度为3
assert x.length==0 ; // 此处断言数组的长度为0
}
};
没生效?是的。
断言本身不会影响程序的执行,但是如果想让一个断言起作用,则必须对断言进行验证:
-enableassertions 可以简写为-ea
验证上面的Test程序,java -ea Test
出现错误:
public class Test{
public static void main(String args[]){
int x[] = {1,2,3} ; // 定义数组,长度为3
assert x.length==3 ; // 此处断言数组的长度为3,断言正确
}
};
断言就是验证,上面的断言信息是java默认的断言信息,断言的错误信息也可以自定义:
public class Test{
public static void main(String args[]){
int x[] = {1,2,3} ; // 定义数组,长度为3
assert x.length==0 : "数组长度不为0" ; // 此处断言数组的长度为0
}
};