异常

异常

1. 异常概念

1.1 生活中的异常

异常:异于平常,和平时正常得情况不一样,有问题,有故障,一般情况下是出现了某种错误。

例如:小明每天正常上班,需要做40分钟地铁,但是有一天发生了地铁故障/有人跳轨,结果导致地铁暂停

生活中如果遇到了此类问题,要进行解决。

小明的解决方法是:

  • 出了地铁站,打车前往公司;

  • 出了地铁站,规划其他方式前往公司!

  • 再不行,和领导请假会晚一些。

    1.2 程序中的异常

    程序中的异常:在程序运行过程中,出现了异常事件,通常是由外部输入或硬件错误导致的,异常会导致程序中断。

    Scanner input = new Scanner(System.in);
    		
    System.out.print("请输入被除数:");
    int num1 = input.nextInt();
    System.out.print("请输入除数:");
    int num2 = input.nextInt();
    
    int result = num1 / num2;
    System.out.printf("%d / %d = %d", num1, num2, result);
    

    正常情况下,上述程序可以完成连个数值的除法计算。

    但如果出现了下方的情况,那就是程序中出现了异常(Exception)。

    请输入被除数:4
    请输入除数:0
    Exception in thread "main" java.lang.ArithmeticException: / by zero
    	at demo1.Demo1.main(Demo1.java:15)
    
    Scanner input = new Scanner(System.in);
    		
    System.out.print("请输入被除数:");
    int num1 = input.nextInt();
    System.out.print("请输入除数:");
    int num2 = input.nextInt();
    if (num2 == 0) {
        System.out.println("对不起!除数不能为0!");
        return;
    }
    
    int result = num1 / num2;
    System.out.printf("%d / %d = %d", num1, num2, result);
    

    请输入被除数:a
    Exception in thread "main" java.util.InputMismatchException
    	at java.util.Scanner.throwFor(Scanner.java:864)
    	at java.util.Scanner.next(Scanner.java:1485)
    	at java.util.Scanner.nextInt(Scanner.java:2117)
    	at java.util.Scanner.nextInt(Scanner.java:2076)
    	at demo1.Demo1.main(Demo1.java:11)
    
    Scanner input = new Scanner(System.in);
    
    System.out.print("请输入被除数:");
    // hasNextInt() : boolean  判断当前输入的内容是否是整数
    // hasNextDouble() : boolean 判断当前输入的内容是否是小数
    if (input.hasNextInt()) {
        int num1 = input.nextInt();
    
        System.out.print("请输入除数:");
        if (input.hasNextInt()) {
            int num2 = input.nextInt();
            if (num2 == 0) {
                System.out.println("对不起!除数不能为0!");
                return;
            }
    
            int result = num1 / num2;
            System.out.printf("%d / %d = %d", num1, num2, result);
        } else {
            System.out.println("请输入整数!");
        }
    } else {
        System.out.println("请输入整数!");
    }
    

    上述的方式的确可以解决掉问题,但使用 if 来解决异常情况,非常低端,我们无法做到对所有异常情况进行查缺补漏。并且 if 写多了,整个代码可读性变得非常差。

2. Java异常处理机制

在 Java 中提供了大量的异常类型来实现异常的分类,并且提供了几个关键字来实现异常处理。


  • Exception:异常类型的顶级父类
    • RuntimeException:运行时异常,在运算时可能出现的异常
      • inputMismactchException:输入不匹配异常
      • NullPointerException:空指针异常,引用数据类型的引用变量赋值为 null
      • ArithmeticException:算术异常
  • Error:错误,只有改代码或是更换硬件、环境才可以解决,程序无法自行修复
    • IOError:输入输出流错误
    • NoClassDefFoundError:找不到指定的类错误
    • NoSuchMethodError:找不到指定的方法错误
    • StackOverflowError:栈溢出错误
    • OutOfMemoryError:内存溢出错误 OOM

try:尝试,尝试捕获程序中异常

catch:抓住,捕获异常

finally:最终的

throw:抛出,抛出异常

throws:声明异常


默认的情况下,JVM 在执行程序时,如果发生了异常,会立刻结束当前程序,然后根据异常的情况创建对应的异常对象,然后将该异常对象反馈(抛)给你,如果我们自身没有编写任何处理程序,JVM 会将异常对象的堆栈跟踪信息打印在控制台上。

3. try-catch

try { // try 块
	// 可能会出现问题的代码
} catch (异常类型 形参名) { // catch 块会声明它可以抓住哪种异常
	// 处理该异常的代码
}

使用 try-catch 来解决异常问题,会出现三种情况:

  1. 没有异常出现,程序正常执行,catch块内容不会生效
  2. 出现了 catch 块可以捕捉的异常,程序从出现异常位置结束,直接跳转到 catch 块中执行异常处理
  3. 出现了 catch 块无法捕捉的异常,此时相当于没有添加任何异常处理,JVM 默认在控制台上打印堆栈跟踪信息

4. 多重catch

try {
	// 可能会出现问题的代码
} catch (异常类型 形参名) { // catch 块会声明它可以抓住哪种异常
	// 处理该异常的代码
} catch (异常类型 形参名) { // catch 块会声明它可以抓住哪种异常
	// 处理该异常的代码
} ....

// .........................................
    
try {
    // 可能会出现问题的代码 
} catch (异常类型1 | 异常类型2 形参名)
    // 处理该异常的代码

它的处理逻辑和多重 if 类似,是自上而下执行 catch 检查,只要有一个 catch 块可以处理出现的异常,那么后续的 catch 块不再生效。


大多数情况下,我们往往会使用下方的写法:

  • 所有的异常都可以被捕获到,不用担心有所遗漏
  • 只写一个异常类型,非常简洁
try {
 	// 可能会出现问题的代码 
} catch (Exception e) { // catch 块会声明它可以抓住哪种异常
	// 处理该异常的代码
}

注意:如果 catch(Exception e) 出现在了多重 catch 块中,记得放在最后(联想多重 if 问题)。

5. try-catch-finally

以后一般会用 finally 解决流资源的关闭。

try {
	// 可能会出现问题的代码
} catch () {
	// 处理该异常的代码
} finally {
	// 无论是否出现异常都要求执行代码
}

[面试题] 如果 finally 和 return 同时出现,执行顺序是什么样的?

会先执行 finally 然后在执行 return。


try {
	// 可能会出现问题的代码
} finally {
	// 无论是否出现异常都要求执行代码
}

当你不想处理异常时,但有些代码又必须执行,可以简化为 try-finally 的写法。

try 必须和 catch 或 finally 组合使用,不能单独使用

6. 常见的异常处理代码

  1. 在 catch 块中添加输出语句来提醒错误

    System.out.printn();

  2. 在 catch 块中添加错误输出语句来提醒错误

    System.err.println();

  3. 打印异常的堆栈跟踪信息

    printStackTrace()

    java.util.InputMismatchException 异常类型
    	下方是异常出现的定位,越靠上方,代表是越接近根源的位置(根本原因)
    	at java.util.Scanner.throwFor(Scanner.java:864)
    	at java.util.Scanner.next(Scanner.java:1485)
    	at java.util.Scanner.nextInt(Scanner.java:2117)
    	at java.util.Scanner.nextInt(Scanner.java:2076)
    	越靠下方的是越直接的位置(直接原因)
    	at 全类名.方法名(哪一行)
    	at demo4.Demo1.main(Demo1.java:12)
    
    java.lang.ArithmeticException 异常类型: / by zero 异常提示信息
    	at demo4.Demo1.main(Demo1.java:16)
    
  4. 获取异常的提示信息

    getMessage()

  5. 记录日志,将你的程序运行的过程存储成日志文件

  6. 根据你的需求,将一些错误的赋值归位

7. 抛出异常

在某个方法中,如果发现有异常的情况,可以添加抛出异常代码。

当程序中出现了 throw,等价于编写了一个 return,只不过 throw 是非正常结束当前方法

public xx xx() {
    // throw new XXXException(异常提示信息);
    throw 异常对象;
}

例如:

public class Student {
	public void setGender(String gender) {
		if (!("男".equals(gender) || "女".equals(gender))) {
			// 抛出异常
			throw new RuntimeException("性别非法!");
		}
		this.gender = gender;
	}
}
Exception in thread "main" java.lang.RuntimeException: 性别非法!
	at demo5.Student.setGender(Student.java:20)
	at demo5.Test.main(Test.java:7)

return:结束当前方法,返回到方法的调用者位置。

throw:向调用者抛出异常对象,结束当前方法。如果抛出的异常没有处理,那么 JVM 会默认采用它的方案解决。

8. 声明异常

public xxx xx(形参) throws 异常类型1, 异常类型2, ... {
    // 方法体
}

[面试题] throw 和 throws 有什么区别?

throw:

  • 表示方法内抛出某种异常对象
  • 如果异常对象是非 RuntimeException 则需要在方法声明时加上该异常的抛出,即需要加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错。
  • 执行到 throw 语句则后面的语句块不再执行

throws:

  • 方法的参数后加上 throws 表示这个方法可能抛出某种异常,需要由方法的调用者进行异常处理。(提醒作用)
  • throws 分为自行声明及被迫声明
    • 自行声明:当你程序中出现的是运行时异常时,你想提醒调用我们方法的调用者注意处理该异常,可以添加。
    • 被迫声明:当你程序中出现的是非运行时异常(受检异常),必须在方法上声明该异常类型,而且调用者在调用该方法时,也必须处理(try-catch)或再次声明(继续 throws)。

9. 自定义异常

创建属于自己的异常类型。

  1. 创建异常类型
  2. 继承合适的异常类型父类
  3. 定义无参和带参构造方法
public class IllegalGenderException extends RuntimeException {

	public IllegalGenderException() {
		super();
	}

	public IllegalGenderException(String message) {
		super(message);
	}
	
}

10. 捕捉异常

在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。

潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。

当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。

运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。

当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值