文章目录
异常概述和异常的分类
异常概述
异常分类
在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