异常(Exception)

本文详细介绍了Java中的异常处理,包括异常的概念、try-catch块的使用、常见异常类型、多catch块、声明和抛出异常、自定义异常以及异常处理的重要性。
摘要由CSDN通过智能技术生成

一、异常的概念

生活中的异常

正常情况下,小李每天开车去上班,耗时大约半个小时。
在这里插入图片描述
但是,异常情况迟早要发生!
在这里插入图片描述

程序中的异常

以下程序运行时会出现错误吗?

public class TestException {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("请输入被除数:");
        int num1 = in.nextInt();
        System.out.print("请输入除数:");
        int num2 = in.nextInt();
        System.out.println(String.format("%d / %d = %d",num1, num2, num1 / num2));
        System.out.println("感谢使用本程序!");
    }
}

上述代码会出现一系列的异常情况,比如输入的除数是0等。
为了解决此类问题我们尝试通过if-else来解决异常问题。

public class Test {

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        System.out.print("请输入被除数:");
        int num1 = in.nextInt();
        System.out.print("请输入除数:");
        int num2 = 0;
        if (in.hasNextInt()) {// 如果输入的除数是整数
            num2 = in.nextInt();
            if (0 == num2) {// 如果输入的除数是0
                System.err.println("输入的除数是0,程序退出");
                System.exit(1);
            }
        } else {// 如果输入的除数不是整数
            System.err.println("输入的除数不是整数,程序退出。");
            System.exit(1);
        }
        System.out.println(String.format("%d / %d = %d",num1, num2, num1 / num2));
        System.out.println("感谢使用本程序!");
    }
}

但是上述使用if-else的方式有弊端:
1、代码臃肿。
2、程序员要花很大精力“堵漏洞”。
3、程序员很难堵住所有”漏洞“。
这是我们就需要使用异常机制。

什么是异常

概念:异常是指程序在运行过程中所发生的不正常的事件,它会中断正在运行的程序;通俗来说就是在程序运行过程中,出现的不正常情况叫做异常。
在这里插入图片描述
生活中面对异常通常会这样处理
在这里插入图片描述
生活中,根据不同的异常进行相应的处理,而不会就此中断我们的生活。

二、异常处理

什么是异常处理

Java编程语言使用异常处理机制为程序提供了错误处理的能力。
在这里插入图片描述

Java中如何进行异常处理

  • Java中异常处理是通过5个关键字来实现的:try、catch、finally、throw、throws
    在这里插入图片描述

try-catch块

情况一:正常

示例代码:

public void method() {
    try {
        // 代码段(此处不会产生异常)
    } catch (异常类型 ex) {
        // 对异常进行处理的代码段
    }
    // 代码段
}

在这里插入图片描述

情况二:出现异常

示例代码:

public void method() {
    try {
        // 代码段1
        // 产生异常的代码段2
        // 代码段3
    } catch (异常类型 ex) {
        // 对异常进行处理的代码段4
    }
    // 代码段5
}

在这里插入图片描述

  • printStackTrace的堆栈跟踪功能显示出程序运行到当前类的执行流程。
    在这里插入图片描述
情况三:异常类型不匹配

示例代码:

public void method() {
    try {
        // 代码段1
        // 产生异常的代码段2
        // 代码段3
    } catch (异常类型 ex) {
        // 对异常进行处理的代码段4
    }
    // 代码段5
}

在这里插入图片描述

调用方法输出异常信息
方法名说明
void printStackTrace()输出异常的堆栈信息
String getMessage返回异常信息描述字符串,是printStackTrace()输出信息的一部分

常见的异常类型

异常类型说明
Exception异常层次结构的父类
ArithmeticException算术错误情形,如以零作除数
ArrayIndexOutBoundsException数组下标越界
NullPointerException尝试访问null对象成员
ClassNotFoundException不能加载所需的类
IIIegalArgumentException方法接收到非法参数
ClassCastException对象强制类型转换出错
NumberFormatException数字格式转换异常,如把“abc”转换成数字

try-catch-finally

  • 在try-catch块后加入finally块
    1、是否发生异常都执行。
    2、不执行的唯一情况:
    在这里插入图片描述
  • 存在return的try-catch-finally块

示例代码:

public void method() {
    try {
        // 代码段1
        // 产生异常的代码段2
    } catch (异常类型 ex) {
        // 对异常进行处理的代码段3
        // 执行return退出方法
    }
    // 代码段4
}

在这里插入图片描述

  • 当try-catch块中存在return语句,是否执行finally块,其中包含了四种情况:

情况一(try中有return,finally中没有return):

public class TryTest{
	public static void main(String[] args){
		System.out.println(test());
	}
	
	private static int test(){
		int num = 10;
		try{
			System.out.println("try");
			return num += 80;
		}catch(Exception e){
			System.out.println("error");
		}finally{
			if (num > 20){
				System.out.println("num>20 : " + num);
			}
			System.out.println("finally");
		}
		return num;
	}
}
// 输出结果如下:
// try
// num>20 : 90
// finally
// 90
// 分析:显然“return num += 80”被拆分成了“num = num+80”和“return num”两个语句,线执行try中的“num = num+80”语句,将其保存起来,在try中的”return num“执行前,先将finally中的语句执行完,而后再将90返回。

情况二(try和finally中均有return):

public class TryTest{
	public static void main(String[] args){
		System.out.println(test());
	}

	private static int test(){
		int num = 10;
		try{
			System.out.println("try");
			return num += 80;
		}catch(Exception e){
			System.out.println("error");
		}finally{
			if (num > 20){
				System.out.println("num>20 : " + num);
			}
			System.out.println("finally");
			num = 100;
			return num;
		}
	}
}
// 输出结果如下:
// try
// num>20 : 90
// finally
// 100
// 分析:try中的return语句同样被拆分了,finally中的return语句先于try中的return语句执行,因而try中的return被”覆盖“掉了,不再执行。

情况三(finally中改变返回值num):

public class TryTest{
	public static void main(String[] args){
		System.out.println(test());
	}
 
	private static int test(){
		int num = 10;
		try{
			System.out.println("try");
			return num;
		}catch(Exception e){
			System.out.println("error");
		}finally{
			if (num > 20){
				System.out.println("num>20 : " + num);
			}
			System.out.println("finally");
			num = 100;
		}
		return num;
	}
}
// 输出结果如下:
// try
// finally
// 10
// 分析:虽然在finally中改变了返回值num,但因为finally中没有return该num的值,因此在执行完finally中的语句后,test()函数会得到try中返回的num的值,而try中的num的值依然是程序进入finally代码块前保留下来的值,因此得到的返回值为10。

情况四(将num的值包装在Num类中):

public class TryTest{
	public static void main(String[] args){
		System.out.println(test().num);
	}
 
	private static Num test(){
		Num number = new Num();
		try{
			System.out.println("try");
			return number;
		}catch(Exception e){
			System.out.println("error");
		}finally{
			if (number.num > 20){
				System.out.println("number.num>20 : " + number.num);
			}
			System.out.println("finally");
			number.num = 100;
		}
		return number;
	}
}
 
class Num{
	public int num = 10;
}
// 输出结果如下:
// try
// finally
// 100
// 从结果中可以看出,同样是在finally中改变了返回值num的值,在情况三中,并没有被try中的return返回(test()方法得到的不是100),但在这里却被try中的return语句返回了。

总结:

try语句在返回前,将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的语句,而后分为以下三种情况:
情况一:如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。
情况二:如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。
情况三:如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况:
1)如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。
2)如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。

多重catch块

  • 引发多种类型的异常
    1、排列catch语句的顺序:先子类后父类。
    2、发生异常时按照顺序逐个匹配。
    3、只执行第一个与异常类型匹配的catch语句。

示例代码:

public class TestException {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        try {
            System.out.print("请输入被除数:");
            int num1 = in.nextInt();
            System.out.print("请输入除数:");
            int num2 = in.nextInt();
            System.out.println(String.format("%d / %d = %d",
                    num1, num2, num1 / num2));
            System.out.println("前面没有出现异常");
        }catch(ArithmeticException e){
            System.out.println("数学异常,除数不能为0");
            e.printStackTrace();
        }catch(InputMismatchException e){
            System.out.println("输入的参数值类型不匹配");
            e.printStackTrace();
        }catch(NullPointerException e){
            System.out.println("空指针异常");
            e.printStackTrace();
        }
        System.out.println("感谢使用本程序!");
    }
}

优点: 可以针对每一种具体的异常做出相应的更丰富的处理。
注意: 当使用多重的catch的时候一定要注意相关异常的顺序,将子类放在最前面的catch,父类放在后面的catch。
执行过程中可能存在的情况:
1、正常执行,只执行try中的代码。
2、遇到异常情况,会处理try中异常代码之前的逻辑,后面的逻辑不会执行,最后会执行catch中的代码。
3、使用多重catch的时候,会遇到异常子类不匹配的情况,此时依然会报错,因此建议在catch的最后将所有异常的父类写上。

声明异常(throws)

在异常情况出现的时候,可以使用try。。。catch。。。finally的方式对异常进行处理,除此之外,可以将异常抛出,由外部进行处理:
1、在方法调用过程中,可以存在N多个方法之间的调用,此时假如这个方法中都包含了异常情况
那么就需要在每个方法中都进行try。。。catch,另外一种比较简单的方式,就是在方法的最外层处理调用一次即可,使用throws的方法,对所有执行过程中的所有方法出现的异常进行统一集中处理。
2、如何判断是使用throws还是使用try。。。catch?
最稳妥的方式是在每个方法中都进行异常的处理;偷懒的方式是判断在整个调用的过程中,外层的调用方法是否对异常的处理,如果有,直接使用throws,如果没有, 那么就要使用try。。。catch。。。
在这里插入图片描述

抛出异常(throw)

在这里插入图片描述

三、异常的分类

在这里插入图片描述

四、自定义异常

何时需要自定义异常?
当JDK中的异常类型不能满足程序的需要时,可以自定义异常类。
使用自定义异常的步骤:

  1. 定义异常类。
  2. 编写构造方法,继承父类的实现。
  3. 实例化自定义异常对象。
  4. 使用throw抛出。

注意:自定义的异常类需要继承Exception或者RuntimeException。
示例代码:

public class GenderException extends Exception{

    public GenderException(){
        System.out.println("性别异常");
    }

    public GenderException(String msg){
        System.out.println(msg);
    }
}

五、总结

1、相同的代码在运行的时候,根据输入的参数或者操作的不同,有可能会发生异常,有可能不会发生异常,应该在写代码的过程中尽可能的保证代码的正确性,不要到处都是bug。
2、如果要解决代码中出现的异常,需要添加非常复杂的代码逻辑来进行判断,会使代码变得非常臃肿,不利于维护,可读性比较差。因此,推荐大家使用异常机制来处理程序运行过程中出现的问题。
3、程序在运行过程中如果出现了问题,会导致后面的代码无法正常执行,而使用异常机制之后可以对异常情况进行处理,同时后续的代码会继续执行,不会中断整个程序。
4、在异常的处理过程中,不要只是简单的输出错误,要尽可能的将详细的异常信息进行输出;
e.printStackTrace():打印异常的堆栈信息,可以从异常信息的最后一行开始追踪,寻找自己编写的Java类。

  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值