大家好,又见面了,我是你们的朋友全栈君。
在程序运行过程中出现错误,导致程序出现非预期场景。异常处理可以保证出现错误后,控制接下来的程序流程,是选择定位错误信息,还是抛出异常或捕获异常、还是避免程序非正常退出,都取决于我们。
Java把异常作为一种类,当做对象来处理。所有异常类的基类是Throwable类,两大子类分别是Error和Exception。这些异常类可以分为三种类型:系统错误、异常和运行时异常。系统错误由Java虚拟机抛出,用Error类表示。Error类描述的是内部系统错误,例如Java虚拟机崩溃。这种情况仅凭程序自身是无法处理的,在程序中也不会对Error异常进行捕捉和抛出。
异常(Exception)又分为RuntimeException(运行时异常)和CheckedException(检查异常),两者区别如下:
两种异常的处理方式:若是运行时异常,则表明程序出错,应该找到错误并修改,而不是对其捕获。若是检查异常,遵循该原则:谁知情谁处理,谁负责谁处理,谁导致谁处理。处理就是对其捕获并处理。
异常处理的5个关键字:try、catch、throw、throws和finally。关于它们的用法和注意点,会在下面一一介绍。
Java的异常处理模型基于三种操作: 声明异常、抛出一个异常和捕获一个异常。
//不捕获,而是声明该异常,交给调用者处理
public static void method() {
/*try-catch块捕获异常*/
if(5 > 3) {
try {
throw new Exception(); //抛出异常
} catch (Exception e) {
e.printStackTrace();//捕获异常后的处理
}
}
}
在Java中,当前执行的语句必属于某个方法,Java解释器调用main方法执行开始执行程序。若方法中存在检查异常,如果不对其捕获,那必须在方法头中显式声明该异常,以便于告知方法调用者此方法有异常,需要进行处理。 在方法中声明一个异常,方法头中使用关键字throws,后面接上要声明的异常。若声明多个异常,则使用逗号分割。 例如:
public static void method() throws IOException, FileNotFoundException{
//something statements
}
【注意】若是父类的方法没有声明异常,则子类继承方法后,也不能声明异常。
public static double method(int value) {
if(value == 0) {
throw new ArithmeticException("参数不能为0"); //抛出一个运行时异常
}
return 5.0 / value;
}
大部分情况下都不需要手动抛出异常,因为Java的大部分方法要么已经处理异常,要么已声明异常。所以一般都是捕获异常或者再往上抛。
try {
//包含可能会出现异常的代码以及声明异常的方法
} catch (ClassCastException e) {
//捕获指定异常并进行处理
}catch(Exception ex) {
//捕获指定异常并进行处理
}
若执行try块的过程中没有发生异常,则跳过catch子句。若是出现异常,try块中剩余语句不再执行。开始逐步检查catch块,判断catch块的异常类实例是否是捕获的异常类型。匹配后执行相应的catch块中的代码。如果异常没有在当前的方法中被捕获,就会被传递给该方法的调用者。这个过程一直重复,直到异常被捕获或被传给main方法(交给JVM来捕获)。
try {
File f =new File("C:\\ProgramFile\\test.txt");
FileInputStream fis = new FileInputStream(f);
} catch (FileNotFoundException e) {
//子类异常
e.printStackTrace();
} catch(IOException ie) {
//父类异常
ie.printStackTrace();
} catch(Exception e) {
//基类运行时异常
e.printStackTrace();
}
这里只是用于演示catch块捕获的顺序。捕获多个异常时,可以使用catch(Exception1 | Exception2| Exception3)的形式来优化捕获异常的代码结构。 将声明异常、抛出异常和捕获异常综合在一起。 例如:
public static void main(String[] args) {
for(int i = 2; i < 100; i++) {
//对运行时异常,可以选择捕获也可以不选择捕获
if(isPrime(i)) {
System.out.print(i + " ");
}
}
} //检测是否为质数
public static boolean isPrime(int num) throws IllegalArgumentException{
//抛出一个运行时异常
if(num < 2) throw new IllegalArgumentException("质数不能小于2");
for(int i = 2; i < num; i++) {
if(num % i == 0) {
//若能被1和本身之外的数整除,则非质数
return false;
}
}
return true;
}
因为抛出的是运行时异常,可以选择捕获或者不捕获。但如果抛出检查异常,在编译时就必须选择捕获或者声明。
//第一种形式 式
try {
//执行程序代码,可能会出现异常
}catch(Exception e) {
//捕获异常并处理
}finally {
//必执行的代码
}
//第二种形
try {
//执行程序代码,可能会出现异常
}finally {
//必执行的代码
}
Scanner sc = new Scanner(System.in);
int a = 0; //保证局部变量a在各个块中可用
try {
a = sc.nextInt();
if(a < 0) throw new IllegalArgumentException();
System.out.println("执行完try语句。a:" + a);
}catch(IllegalArgumentException e){
System.out.println("执行catch语句");
System.out.println("数值小于0,不符合。已设为默认值 1");
a = 1;
}finally {
System.out.println("执行finally语句。a:" + a);
}
输出并分析:
//未引发异常 //引发异常并捕获
5 -5
执行完try语句。a:5 执行catch语句
执行finally语句。a:5 数值小于0,不符合。已设为默认值 1
执行finally语句。a:1
//以Lock加锁为例,演示try-finally
ReentrantLock lock = new ReentrantLock();
try {
//需要加锁的代码
}finally {
lock.unlock(); //保证锁一定被释放
}
finally遇见如下情况不会执行: 1、在前面的代码中用了System.exit()退出程序。 2、finally语句块中发生了异常。 3、程序所在的线程死亡。 4、关闭CPU。
通过继承Exception类来定义一个异常类。Java已经提供了很多异常类,尽量使用这些异常类而不要创建自己的异常类。除非Java的异常类不能很好地描述问题时,才自定义异常来进行准确描述。对于自定义异常,不需要太多功能,类名能准确描述问题是关键。 例如:
//判断长度是否合法的自定义异常
public class LengthException extends Exception {
public LengthException() {
}
public LengthException(String s) {
super(s);
}
@Override
public String getMessage() {
return super.getMessage();
}
}
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/106681.html原文链接:https://javaforall.cn