Skip to content

生活中总是会存在各种突发情况......

程序猿和小爪约好了一起去爬山,美女相伴,猿生好不惬意!忽然下起了瓢泼大雨(这就是所谓的异常情况)

程序猿傻眼了,然后眼看着小爪淡定的从背包里掏出雨衣穿上(这就是对异常的处理)

1 异常介绍

1.1 什么是异常

异常是程序在运行期间发生的不正常的事件,它会中断指令的正常执行流程。

异常是很难完全避免的,设计良好的程序应该在异常发生时,提供处理这些不正常事件的方法,使程序不会因为异常的发生而中断或产生不可预见的结果。

Java语言使用异常处理机制,为程序提供异常处理的能力

1.2 异常分类

img

1)Java中的异常分为两大类

错误(Error):

JVM系统内部错误或资源耗尽等严重情况,属于JVM需要负担的责任。

这一类异常事件无法恢复或不可捕获,将导致应用程序中断。程序(员)对其不做处理

癌症晚期,无法进行治疗

异常(Exception):

因编程错误、不严谨或偶然的外在因素导致的问题。

所有异常类的父类,其子类对应了各种各样可能出现的异常事件

程序员通常可以处理,可以显式地声明或捕获。

这类异常如果处理合理,程序有机会恢复至正常运行状态

小感冒,吃点药就好了

2)Exception也分为两类

非受检(unchecked)异常:

编译器不要求必须处理的异常,也叫运行时异常。

指程序的逻辑错误,是程序员应该避免的,会在程序运行过程中触发,比如:

错误的类型转换:java.lang.ClassCastException

数组下标越界:java.lang.ArrayIndexOutOfBoundsException

空指针访问:java.lang.NullPointerException

算术异常(除0溢出):java.lang.ArithmeticException

等等

受检(checked)异常:

编译器要求必须处理的异常

比如:

没有找到指定名称的类:java.lang.ClassNotFoundException

访问不存在的文件:java.io.FileNotFoundException

操作文件时发生的异常:java.io.IOException

操作数据库时发生的异常:java.sql.SQLException

等等

java
public class App1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		// java.lang.ArithmeticException
		int a = 10 / 0;
		
		// java.lang.NullPointerException
		String str = null;
		str.length();
		
		//java.lang.OutOfMemoryError
		// -Xms10m -Xmx10m
		List<Integer[]> list = new ArrayList<>();
		while(true) {
			list.add(new Integer[1024]);
		}
		
	}

}

2 异常处理机制

Java提供了处理异常的关键字:try,catch,finally,throw,throws

程序在执行过程中,如果出现异常,会自动生成一个异常类对象,该异常对象将被自动提交给JVM,这个过程称为抛出(throw)异常。

当JVM接收到异常对象时,会寻找能处理这一异常的代码,并把当前异常对象交给其处理,这一过程称为捕获(catch)异常和处理异常。

如果JVM找不到可以捕获异常的代码,则运行时系统将终止,相应的Java程序也将退出。

2.1 捕获异常并处理

当发生异常时,由自身方法捕获,并提供处理代码

1)基本语法

java
try{
//可能产生异常的代码
}[catch( ExceptionName1 e ){
//异常的处理代码

}catch( ExceptionName2 e ){
//异常的处理代码
}][ finally{
//都会执行的语句
}]

try 代码段包含可能产生异常的代码;

当异常发生时,程序会中止当前的流程去执行相应的catch代码段;

finally段的代码无论是否发生异常都将执行;

catch和finally两者至少出现一个

2)catch

catch语句块中是对异常进行处理的代码,每个try语句块可以跟随多个catch语句,用于处理可能产生的不同类型的异常对象。

在catch中声明的异常对象封装了异常事件发生的信息,可以使用该对象的一些方法来获取这些信息。

如:getMessage() :获得有关异常事件的字符串信息。

printStackTrace() :用来跟踪异常发生时执行堆栈的内容。

使用多重 catch 语句时,异常子类一定要位于异常父类之前。

如果异常发生,则运行对应catch块的代码(从前到后匹配),如果没有异常产生,所有catch块的代码都会被忽略

java
public class App2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		try{
//			int a = 10 / 0;
			
			String str = null;
			str.length();
			
			//try中,某个语句发生异常,直接跳转到相关的catch处,不会再执行异常语句后的代码
			int[] arr = new int[5];
			System.out.println(arr[6]);
			
			int a = 10 / 0;
			
		}
		//try-catch可以捕获多种异常,但是不能将更高一级的异常(父类的异常)写在前面
//		catch(Exception ex){
//			System.out.println(ex.getMessage());
//		}
		catch(ArithmeticException ex){
			//获取异常的信息
			System.out.println(ex.getMessage());
			//获取堆栈中异常详细信息
			System.out.println(ex.getStackTrace());
		}catch(ArrayIndexOutOfBoundsException ex){
			System.out.println(ex.getMessage());
			System.out.println(Arrays.toString(ex.getStackTrace()));
		}catch(Exception ex){
			System.out.println(ex.getMessage());
		}
		System.out.println("continue");
	}

}
java
public class App3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		try{
			int a = 10 / 0;
			
			int[] arr = new int[5];
			arr[6] = 10;
		}
		//java7后,可以将多个异常写在一个catch里,多个异常类型之间使用"|"分隔
        // 这些异常必须是同级的
		catch(ArithmeticException | 
				ArrayIndexOutOfBoundsException ex){
			System.out.println(ex.getMessage());
		}
		
	}

}

3)finally

finally语句为异常处理提供一个统一出口,能够对程序的状态作统一的管理。

无论在try代码块中是否发生了异常事件,finally块中的语句都会被执行。

通常在finally语句块中可以进行资源的清理、释放工作,如:关闭打开的文件删除临时文件,关闭数据库的连接等

finally语句是可选的

java
public class App4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		try{
			int a = 10 / 0;
		}catch(ArithmeticException ex){
			System.out.println(ex.getMessage());
            // 即使使用了return,finally里代码也会执行
			// return;
            System.exit(-1);
		}catch(Exception ex){
			return;
		}finally{
			System.out.println("程序咋写呢嘛,又异常");
		}
		
		System.out.println("hello");
		
	}

}

语法上,try-catch-finally结构中,catch可以不写,仅出现try-finally,如果出现异常,finally中代码也会执行,只是无法捕获异常

java
try {
    int a = 10 / 0;
}finally {
    System.out.println("haha");
}

如果try或catch中出现了return语句,finally里中代码是否执行?

如果使用了System.exit()方法呢?

2.2 声明抛出异常

当某段程序有可能发生异常时,则在声明中抛出异常。如果运行发生异常,则自动抛给调用者处理,如果调用者仍然没有处理,则一直往上抛出,直到抛给JVM

审理案件时,县一级的法院处理不了,就提交给更高一级法院处理

1)throws

在定义方法时,可以使用throws关键字声明抛出异常,用于声明一个方法可能会抛出的异常。

表示当此方法发生异常时,本身不作任何处理,而交给方法的调用者。

throws的基本语法

java
[修饰符] 返回值类型 方法名([形式参数列表]) throws 异常类1, 异常类2....{

}

注意:如果一个方法声明抛出的是受检异常,则在调用这个方法的地方必须处理该异常

java
public class App5 {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub

		try{
			div2(100, 0);
		}catch(ArithmeticException ex){
			System.out.println(ex.getMessage());
			
		}		
	}
	
	
	//方法中可以声明多个抛出的异常,之间使用","分隔
	private static void div2(int a, int b) 
			throws ArithmeticException, Exception{
		System.out.println(a / b);
	}

}

2)throw

用于在方法内部的代码中主动抛出一个指定类型的异常。

注意:如果方法代码中自行抛出的异常是受检异常,则该方法必须处理该异常。

java
public class App6 {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub

		try{
			div(100, 0);
		}catch(ArithmeticException ex){
			System.out.println(ex.getMessage());
		}
	}
	
	private static int div(int a, int b){
		if(b == 0){
			throw new ArithmeticException("除数不能为0");
		}
		return a / b;
	}
}

3 自定义异常

自定义异常一般可以继承Exception或者RuntimeException

image.png

java
public class MyException extends RuntimeException{

	private int code;
    private String msg;
	
	
	public MyException(int code, String msg){
		this.code = code;
        this.msg = msg;
	}
	
	public int getCode(){
		return this.code;
	}
    
    public String getMsg(){
		return this.msg;
	}
	
}
java
public class LoginService {

	public void login(String name, String password) {
		
		if(!(name.equals("admin") && password.equals("123"))) {
			throw new MyException(100, "用户名或者密码不正确");
		} 
        // 其他逻辑
        System.out.println("登录成功");
	}
}