【java】第八章、异常处理

异常概述和异常的分类

异常概述

在这里插入图片描述

异常分类

在java的类库中,每个包中都定义了异常类,而这些所有的类都是顶层Throwable类的子类***
Throwable派生出了两个子类。其中,Error类及其子类用来描述
java运行系统中的一些内部错误以及资源耗尽的错误
*,这类错误较严重,而且我们是无法解决的;而Exception类被称为非致命性的错误,是可以通过捕捉处理 使程序继续执行的。
Exception类又可以根据错误发生的原因分为:运行时异常(RuntimeException)和非运行时异常
在这里插入图片描述

Exception类

在这里插入图片描述
Exception类的子类也有很多
RuntimeException(运行时异常)及其子类都属于这种运行时的异常,例如:空指针异常、数组下标越界…这些异常一般是由程序逻辑错误引起
RuntimeException是一系列异常的父类
在这里插入图片描述
在这里插入图片描述

package classification_of_exception;

public class Demo {

	public static void main(String[] args) {
		Object obj = null;
		System.out.println(obj.hashCode());
	}
}

在这里插入图片描述
抛出空指针异常:说明我这个空对象是不能被使用的

package classification_of_exception;

public class Demo {

	public static void main(String[] args) {
		int arr[] = new int[6];
		System.out.println(arr[10]);
	}
}

在这里插入图片描述

package classification_of_exception;

public class Demo {

	public static void main(String[] args) {
		int a = 1 / 0;
	}
}

在这里插入图片描述

Error类

在这里插入图片描述
如何解决Error的问题?我们在代码中是无法解决的,只能从外部来解决
制造一个Error的错误:让它生成一个不能被解析的字节码文件
在java中,用try…catch语句来处理异常(有异常的代码放在try中,catch()里写异常处理的类型)
但是,我们知道,Error级的错误是不能被处理的,即使加上try…catch语句,这个错误依然存在
只有重新编写这个程序,这个错误才能被解决

package classification_of_exception;

public class Demo {

	public static void main(String[] args) {
		try {
			int a = 1 / 1
			System.out.println(a);
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

捕捉并处理异常(try…catch语句)

捕捉异常

在这里插入图片描述

package try_catch;

public class Demo {

	public static void main(String[] args) {
		try {
			int a = 1 / 0;
		}catch(ArithmeticException e) {
			System.out.println("发生了算数异常,请管理员及时处理");
		}
	}
}

在这里插入图片描述
此例是算数异常,但异常有很多种类,我们可以根据这些异常的种类 来对所有异常进行逐个处理
在这里插入图片描述
多重try_catch代码块可以同时对多个异常进行处理


public class Demo {

	public static void main(String[] args) {
		try {
//			int a = 1 / 0;
			Object obj = null;
			obj.hashCode();
		}catch(NullPointerException e) {
			System.out.println("发生了空指针异常!");
		}
		 catch(ArithmeticException e) {
			System.out.println("发生了算数异常,请管理员及时处理");
		}
		 catch(ClassCastException e) {
			System.out.println("发生了类转换异常!");
		}
	}
}

在这里插入图片描述

异常的中断机制

在这里插入图片描述

package try_catch;

public class Demo {

	public static void main(String[] args) {
		try {
			System.out.println("输出1行");
			System.out.println("输出2行");
			System.out.println("输出3行");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

package try_catch;

public class Demo {

	public static void main(String[] args) {
		try {
			System.out.println("输出1行");
			int a = 1 / 0;
			System.out.println("输出2行");
			System.out.println("输出3行");
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述
在哪行发生异常,程序就在哪里中断(也就是说,当它出现异常时,它后面的代码会被屏蔽掉
那如果在循环中出现了异常,会不会把循环中断掉呢?
正常显示结果如下:

package try_catch;

public class Demo {

	public static void main(String[] args) {
			for(int i = 0; i < 5; i++) {
				System.out.println("输出" + i + "行");
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

package try_catch;

public class Demo {

	public static void main(String[] args) {
		try {
			for(int i = 0; i < 5; i++) {
				System.out.println("输出" + i + "行");
				int a = 1 / i;           //制造异常
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述
这是异常的中断机制,它将for循环彻底停止掉了
那如何让for循环即使发生了一个异常,也能往下继续走下去呢?
在循环内部添加try…catch语句,(shift + alt + z自动添补try…catch语句)
这样,在i等于0的时候会抛出异常,处理完毕异常之后,又会回到for循环中

package try_catch;

public class Demo2 {

	public static void main(String[] args) {
		for(int i = 0 ; i < 5; i++) {
			try {                //将异常处理 放在循环里面,循环就可以得到继续
				System.out.println("输出" + i + "行");
				int a = 1 / i;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

在这里插入图片描述

捕捉异常(finally代码块)

在这里插入图片描述
完整的异常处理语句就应该包含finally代码块
finally就是一个收尾的效果,它里面是最后一定会执行的代码
在这里插入图片描述
正常显示结果如下:

package finally_block;

public class Demo {

	public static void main(String[] args) {
		try {
			System.out.println("打开连接池");
//			int a = 1 / 0;
			System.out.println("通过连接池读取数据");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("关闭连接池");
		}
	}
}

在这里插入图片描述

package finally_block;

public class Demo {

	public static void main(String[] args) {
		try {
			System.out.println("打开连接池");
			int a = 1 / 0;
			System.out.println("通过连接池读取数据");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("关闭连接池");
		}
	}
}

在这里插入图片描述
因为finally语句永远会执行,所以它常常用来做一些关闭的操作
在这里插入图片描述

1、finally块中发生异常

package finally_block;

public class Demo {

	public static void main(String[] args) {
		try {
			System.out.println("打开连接池");
			int a = 1 / 0;
			System.out.println("通过连接池读取数据");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			int b = 1 / 0;
			System.out.println("关闭连接池");
		}
	}
}

在这里插入图片描述

2、 在前面的代码中用了System.exit()

这个代码是强制中断当前的程序

package finally_block;

public class Demo {

	public static void main(String[] args) {
		try {
			System.out.println("打开连接池");
			System.exit(0);     //强制停止当前的程序
			System.out.println("通过连接池读取数据");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("关闭连接池");
		}
	}
}

在这里插入图片描述

3、 程序所在线程死亡

正常情况下:

package finally_block;

import java.util.Scanner;

public class Demo {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		try {
			System.out.println("打开连接池");
			sc.nextLine();
			System.out.println("通过连接池读取数据");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("关闭连接池");
		}
	}
}

在这里插入图片描述
手动让我的线程死亡的情况:

package finally_block;

import java.util.Scanner;

public class Demo {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		try {
			System.out.println("打开连接池");
			sc.nextLine();
			System.out.println("通过连接池读取数据");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			System.out.println("关闭连接池");
		}
	}
}

在这里插入图片描述
通过sc.nextLine();去获取当前输入的值,但要是一直不输入这个值的话,程序就会一直处在等待的情况下
在我不输入任何值的情况下,直接点那个红点,程序就结束了。程序结束之前,这个finally的代码不会执行到,这就是程序所在线程死亡(我是手动让我的线程死亡掉的)

4、关闭CPU

使用throw关键字抛出异常(在方法中抛出异常)

在这里插入图片描述
我们可以把人数为负数的情况当成一个异常:用到throw关键字,将这个情况抛出,变成一个异常
手动制造一个异常
他这个程序遇到throw语句时就会立即终止,而他后面的语句都不会被执行,会直接发生throw new后面这个创造出的异常
在这里插入图片描述
自己去抛出异常,并在后面用Exception e拦截

package throw_Exception;

public class Demo {

	public static void main(String[] args) {
		int count = -100;
		try {
			if(count < 0) {
				throw new ArithmeticException("人员数量是负数" + count);
			}
			System.out.println("当前统计的人数为:" + count);
		} catch (Exception e) {        //拦截异常
			e.printStackTrace();
			System.out.println("捕捉到了异常");
		}
	}
}

在这里插入图片描述
throw关键字还有一个特性:它可以更改我们的异常

package throw_Exception;

public class Demo2 {

	public static void main(String[] args) {
		try {
			int a = 1, b = 0;
			int c = a / b;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

package throw_Exception;

public class Demo2 {

	public static void main(String[] args) {
		try {
			int a = 1, b = 0;
			if(b == 0) {
				throw new NullPointerException("b等于0,发生异常!");
			}
			int c = a / b;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述
说明我这个throw在异常发生之前拦截这个异常,然后交给另外一个人(将算数异常变成空指针异常)

使用throws关键字抛出异常

在这里插入图片描述
在这里插入图片描述
它用在方法之后,然后再在后面写上各种异常类型,如果我们将可能发生的异常写在throws后面,那么在方法中就不用try…catch语句去捕获这些异常了
但是,在调用方法时,要用try…catch语句对这个方法进行异常处理

package throw_Exception;

public class Demo3 {
	public static void show() throws InterruptedException, NullPointerException, Exception {
		for(int i = 0; i < 10; i++) {
			System.out.println(i);
			Thread.sleep(1000);         //休眠一秒
		}
	}
	
	public static void main(String[] args) {
		try {
			show();
		} catch (NullPointerException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述
在这里我们调用一个线程的方法,程序运行时碰到这行代码则会休眠1秒,之后再继续运行
但这里我们看到,它要求我们必须进行异常的处理,所以在这个方法后面追加了一个异常
另外,在main函数里的show()方法,它也要求我们进行异常处理
这个show()方法将异常交给上层代码来处理上层代码调用这个方法时,就必须对它抛出的异常进行捕捉并处理
这就是休眠1秒的效果,但你要使用这行代码则必须抛出这样一个异常(同理,可以在后面抛出更多的异常)
这就是同时抛出多个异常,然后在上层代码(外层代码)来捕获并处理这些异常的情况

package throw_Exception;

public class Demo3 {
	public static void show() throws InterruptedException, NullPointerException, Exception {
		for(int i = 0; i < 10; i++) {
			System.out.println(i);
			Thread.sleep(1000);         //休眠一秒
		}
	}
	
	public static void main(String[] args) throws NullPointerException, InterruptedException, Exception {
		show();
	}
}

我们在main方法这里,将异常抛出,这么写也是可以的,现在,处理这些异常的则是java虚拟机
java虚拟机会自动处理main方法所抛出的异常
这么写虽简单,但有缺陷:java虚拟机会处理这些异常,我们处理不了了
还是用try…catch更好,因为我们可以对每个异常进行不同的捕捉并处理
在这里插入图片描述

自定义异常

在这里插入图片描述
它不属于任何一个异常,那么我们就单独为它创造一个异常(也就是自己创建一个API中没有的异常,然后用这个异常来处理出现的这些特殊的异常场景)
在这里插入图片描述

package self_defined_exception;

public class NonHumansException extends Exception{

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

package self_defined_exception;

public class Demo {

	public static void main(String[] args) {
		String playerType = "human";
		try {
			if(!playerType.equals("human")) {
				throw new NonHumansException("有非人类选手:" + playerType);
			}
			System.out.println("开始比赛");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述

package self_defined_exception;

public class Demo {

	public static void main(String[] args) {
		String playerType = "monkey";
		try {
			if(!playerType.equals("human")) {
				throw new NonHumansException("有非人类选手:" + playerType);
			}
			System.out.println("开始比赛");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述
自定义异常类 去继承这个异常的父类,这样我们就创建好了一个自定义的异常了

package self_defined_exception;

import javax.swing.JOptionPane;

public class NonHumansException extends Exception{
	String message;               //定义成员变量
	
	public NonHumansException(String message) {
		super(message);
		this.message = message;
	}
	
	@Override                    //重写 显示异常信息 的方法
	public void printStackTrace() {
		super.printStackTrace();
		JOptionPane.showMessageDialog(null, message, "发生异常", JOptionPane.ERROR_MESSAGE);
	}
}
package self_defined_exception;

public class Demo {

	public static void main(String[] args) {
		String playerType = "monkey";
		try {
			if(!playerType.equals("human")) {
				throw new NonHumansException("有非人类选手:" + playerType);
			}
			System.out.println("开始比赛");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述
除了这种它能报出特殊异常名字的效果以外,在自定义异常中添加一些特殊的效果,比如我们把这个自定义异常重新加工一下
我们可以看到,不仅控制台输出了异常的日志信息同时弹出一个窗体(这个窗体中也写了我这个日志中的错误内容)
这种错误提示效果是原来异常中没有的一个效果,这就是自定义异常中扩展出来的功能

异常使用原则

在这里插入图片描述
捕捉到的异常一定要处理,捕捉到了一个异常但是不做任何处理,这种写法是没有意义的

package principle_of_use_of_exception;

public class Demo {

	public static void main(String[] args) {
		try {
			int a = 1 / 0;
		} catch (Exception e) {
			//什么都没做
			System.out.println("发生了异常");
		}
		System.out.println("--  end  --");
	}
}

在这里插入图片描述
在这里插入图片描述
虽然通过异常可以增强程序的健壮性,但是同样的,如果使用了过多不必要的异常处理的话,可能会影响到程序的执行效率(消耗大量的系统资源)
在这里插入图片描述
在一个try语句代码块中,放置了大量代码的话,这种写法看上去简单,把可能出现异常的语句都放在了try语句中,然后用这一个try…catch语句来捕捉代码里出现的所有异常
这样的写法会大大增加try语句代码块中出现异常的可能性,从而会导致分析异常原因的难度也会大大增加
在这里插入图片描述
如果父类方法中抛出了多个异常的话,那么子类覆盖这些方法的同时,也要抛出相同的异常或者是其异常的子类

package principle_of_use_of_exception;

import java.io.FileNotFoundException;

public class Parent {
	public void action() throws FileNotFoundException {

	}
}

class Child extends Parent {
	public void action() throws FileNotFoundException {

	}
}
package principle_of_use_of_exception;

import java.io.FileNotFoundException;

public class Parent {
	public void action() throws Exception {

	}
}

class Child extends Parent {
	public void action() throws FileNotFoundException {

	}
}

在这里插入图片描述

package principle_of_use_of_exception;

public class Parent {
	public void action() throws NullPointerException {

	}
}

class Child extends Parent {
	public void action() throws RuntimeException {

	}
}

RuntimeException的子类NullPointerException

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值