有关Java中的异常和异常处理
学习目标
- 掌握异常的两大分类
- 掌握异常的处理
- 掌握自定义异常
1.概念
引入(了解):Java语言具备健壮性:
● 1. “异常处理机制”: 一段程序报错了 在我们处理的前提下 不影响其他程序的正常的执行。
● 2. GC 垃圾处理器 : garbage collection 回收无用的引用关联的对象。
程序报错了 ----> bug
● 1. 错误类型 Error
● 2. 异常类型 Exception
● 以上两者都是程序在运行期间出现的不正常的现象。
● 错误是处理不了的,要么改机器配置 要么改动源码;而异常是可以处理的,这就是本节学习的重点
通俗的说就是:程序执行中不正常的情况
2.Error
● java.lang.Error . 代表程序运行期间的任意一个错误。
● 这些错误一般都是非常严重的问题。一般与机器JVM有关系.
● 一般我们常见的是,java.lang.VirtualMachineError 抛出以表明Java虚拟机已损坏或已耗尽资源以使其继续运行。
层级结构:
public class Error extends Throwable{}
//自动的拥有的父级提供的属性和方法
//创建子类对象 必然会执行父类的构造-----> Error类的构造方法 与Throwable 匹配
3.Exception
● java.lang.Exception. 程序运行期间 出现的不正常的现象。
● jvm规则: 可以使用异常处理机制处理异常。
层级结构:
public class Exception extends Throwable{}
4.Error VS Exception
1.相同点
- 都继承了同一个父类 java.lang.Throwable
- 都是程序里面不正常的现象。
2.不同点
- 对于任意一个Error 不能处理。jvm默认不处理。一般都与jvm有关。
- 对于任意一个异常 都可以处理。jdk提供了异常处理机制,增强代码健壮性。
- 异常: 分为2类 编译时异常 运行时异常
5.异常的分类
-
Throwable: 总父类
-
Error: 表示错误
- 特点为无法解决也无法提前避免
- 通常由非常严重的底层错误或者硬件问题导致
-
Exception: 表示异常
-
特点为可以解决也可以提前避免
-
通常由代码导致
-
分类:
-
RuntimeException: 运行时异常, 也称为未检查异常|未检异常等
-
特点为 编译成功,运行报错, 可以选择性处理
-
常见的运行时异常:
-
-
-
1. java.lang.ArithmeticException: 数学运算异常
2. java.lang.NullPointerException: 空指针异常
3. java.lang.StringIndexOutOfBoundsException: 字符串下标越界异常
4. java.lang.ArrayIndexOutOfBoundsException: 数组下标越界异常
5. java.lang.IndexOutOfBoundsException: 下标越界异常(底层中)
6. java.lang.ClassCastException: 类型转换异常
7. java.lang.NumberFormatException: 数据类型转换异常
8. java.util.ConcurrentModificationException: 并发修改异常(集合指针遍历)
...
-
非运行时异常, 也称为已检查异常|已检异常等
- 特点编译失败, 必须处理
- 不是运行时异常, 一定是非运行时异常
6.异常处理
异常处理方式
● 捕捉处理异常 (积极处理)
● 上抛异常 (消极处理)
6.1 捕获异常
1.语法
try{
//可能存在异常的代码
}catch(异常类名1 引用名){
//异常的处理代码
}catch(异常类名2 引用名){
//异常的处理代码
}..
1.1 语法1
try{
//有可能会出现异常的代码
//对于程序员而言 不要把所有的代码放到try 区分稳定代码以及非稳定的代码
}catch(捕获到具体异常类型 变量名称/异常引用/对象){// (形参)
//对异常引用/对象 进行处理
//1. SOUT
//2. 调用异常类型的功能方法
//3. return
//4. 使用日志框架将不同级别的信息存储到不同的级别的日志文件中 最常用
//5. 异常信息传递
// 5.1 将子级异常类型的信息传递到父级 Casued by 引起异常的根本原因是原因
// 5.2 分层开发: dao--->service---->controller (SpringMVC的框架对异常进行全局处理)
//6.一些业务逻辑....
}finally{
//不管程序是否存在异常 finally代码块的逻辑肯定是要执行的
//1.释放物理资源 关闭一些资源
//Scanner.close()
//2. 在线程里面 使用锁机制实现线程安全 先上锁 后面要解锁
}
1.2 语法2
try{
}catch(){
}
1.3 语法3
try{
}catch(){
}catch(){
}
.....
//可以有多个catch块
try{
}catch(异常类型1 | 异常类型2| ... 变量){
}
1.4 语法4
try{
}finally{
}
//先上锁 后面要解锁
2 使用
以处理运行时异常为例。代码演示使用方法,用于理解,并不特别规范
2.1 try…catch…finally
public static void main(String[] args) {
//异常处理机制: 一段程序报错了 不影响其他程序正常执行
//对运行时异常执行处理
//try...catch...finally
try {
//有可能会出现异常逻辑
System.out.println(3 / 0);
//测试遍历数组元素
int[] array = {1, 2, 3, 4};
for (int num : array) {
System.out.println(num);
}
} catch (ArithmeticException e) {//捕获到具体的异常类型 try里面有可能出现的异常
System.out.println("除数不能为0");
//在catch里面 一定要编写一些逻辑代码 sout
} finally {
//可以使用 可以省略finally
System.out.println("finally..............");
}
//在try 异常之后的代码 不会执行的(从上到下)
//在一个线程中 出现了异常 线程终止了 异常之后的代码 不执行了
}
2.2 try…catch…catch
public static void main(String[] args) {
//使用异常处理机制
//1.可以使用多个catch块捕获不同的异常类型
//2.在最后一个catch块中使用父级维护 捕获没有具体捕获到异常类型(多态) 从小到大
//3.在多个catch块中 catch块的逻辑有且只允许一个(try第一次抛出的异常)
try {
String str = null;
System.out.println(str.equals("hello"));//抛出异常
System.out.println("1111111111111111111111");
System.out.println(3 / 0);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} catch (NullPointerException e) {
System.out.println("变量值为null");
} catch (RuntimeException e) {
System.out.println("程序出错了...");
}
System.out.println("----------------------");
//1. 可以使用1个catch语句块替换多个catch
//2. 使用| 异常类型必须是同级别的
try {
String str = null;
System.out.println(str.equals("hello"));//抛出异常
System.out.println("1111111111111111111111");
System.out.println(3 / 0);
} catch (NullPointerException | ArithmeticException | ArrayIndexOutOfBoundsException e) {
System.out.println("程序出错了...");
} catch (RuntimeException e) {
}
}
2.3 try…finally
private static void demo3() {
Lock lock = new ReentrantLock();
try {
lock.lock();//上锁了
//jdshdgsdshdshgds
//jdshdgsdshdshgds
//jdshdgsdshdshgds
//jdshdgsdshdshgds
//编写程序 就有可能会出现运行时异常----> 不做处理----> 当前线程终止了
} finally {
lock.unlock();
}
}
private static void demo1() {
//try....finally
Scanner input = new Scanner(System.in);
try {
int num = input.nextInt();//不需要做任何处理----> 运行时异常 不能处理
} finally {
System.out.println("1111111111111111111");
input.close();//不管上面的代码是否存在异常 都要释放资源
}
//jdk1.7+ 提供了更加优雅的方式 释放流资源
//try....with...resources 自动的调用close 前提: 类型必须实现Closeable
//try(){
// }
System.out.println("22222222222222222");
}
private static void demo2() {
Scanner input = new Scanner(System.in);
Scanner input1 = new Scanner(System.in);
MyClass myClass = new MyClass();
//try(引用1;引用2)
try (input; input1; myClass) {//自动的调用input/input1的close
int num = input.nextInt();
}
}
2.4 catch块
需要了解,在catch代码块中,我们可以编写什么样的代码,对异常对象进行相关的处理?
private static void demo1() {
try {
//有可能会出现异常逻辑
System.out.println(3 / 0);
} catch (ArithmeticException e) {
//对异常对象/引用执行处理
//System.out.println("报错了.....");
//1. sout
//2. 调用异常类型的功能方法 e
//System.err.println("获得异常的详细的字符串:"+e.getMessage());
//e.printStackTrace();
//3. return 方法有返回值类型的方法
// return;
//4. 使用日志组件 将异常信息写入具体不同级别的文件中 info debug error warn fatal 学会在服务器中查看日志文件内容
//Log4j2 logback
//5.进行异常信息传递 Caused by throw将低级别的异常引用传递到高级别的异常对象
//throw 手动抛出具体的异常对象----> new ----> throw new 异常类型构造;
//RuntimeException exception = new RuntimeException("异常信息传递了",e);
throw new RuntimeException("除数为0",e);//手动产生异常 当前线程就终止了
//e.printStackTrace();
//6.不同业务代码实现
}
System.out.println("---------------------");
//System.out.println(3/0);
}
2.5 finally中禁止使用return
private static int demo2() {
try {
System.out.println(3 / 0);
return 100;//当前demo2就结束
} catch (RuntimeException e) {
System.out.println("报错了....");
e.printStackTrace();
} /*finally {
return 300; //都会执行 禁止在finally中使用return
}*/
return 200;
}
6.2 抛出异常 throws
● 消极处理异常的方式。 并没有真正的处理异常
● 使用throws关键字把异常类型抛出给上级/调用者。谁是上层调用者? 哪个方法在调用这个方法,那个方法就是上层调用者
● 在方法签名后面 使用throws关键字抛出多个异常类型。
-
上抛异常 (消极处理)
- 思路: 自身不解决问题, 将问题上抛至调用者, 由调用者承担处理异常的责任
访问修饰符 返回值类型 方法名(形参列表) throws 异常类名1,异常类名2,.. {
}
-
特点: 只能暂时规避处理异常的责任, 无法根治问题, 如果所有方法都未解决异常, 则最终会上抛至虚拟机, 程序终止
-
使用场景:
- 调用抛出非运行时异常的方法时, 暂时规避编译报错使程序运行
- 约束调用者必须处理某些异常
-
使用细节:
- 父类类名可以兼容处理子类异常对象
- 调用者需要处理的异常类型由被调用方法
throws
上抛的类型决定 - 可以上抛方法内并未存在的异常类型
7.throw
- throw是抛出的意思。
- 在方法体里面 使用throw 抛出异常对象。
- throw不是处理异常。throw是产生异常的。
- throw new 异常类构造;
- 一般经常作为条件预判使用,或者在catch块里面 实现异常对象传递。
- 在方法体里面 遇见throw 代表当前线程终止。
throw与throws的区别总结
特性 | throw | throws |
---|---|---|
位置 | 在方法体内部使用 | 在方法签名后面使用 |
语法 | throw new ExceptionType("Error message"); | void methodName() throws ExceptionType1, ExceptionType2 { ... } |
作用 | 用于抛出一个具体的异常对象 | 用于声明方法可能抛出的异常类型 |
使用场景 | 当程序遇到某种错误条件时,手动抛出异常 | 当方法内部可能抛出异常,但不处理时,声明该方法可能抛出的异常类型 |
异常处理 | 直接产生异常,通常需要在方法内部处理或由调用者处理 | 声明异常,调用者需要处理或继续声明抛出 |
8.自定义异常
● jdk中 提供了很多异常类型。但是每个异常都是满足特定的场景。解决特定的问题。
● 在开发中 有很多业务 出现的问题, jdk没有提供对应的异常类型解决。如果不在乎名称的问题,直接使用jdk的内置异常类型也是可以的。但是总之不是很优雅。
● 假如用户支付余额不足异常。如果使用NPE等异常,就显得不那么合适。因此,我们有需要自定义一些和业务相关的异常类型,满足个性化设计。
- 自定义运行时异常: 继承
RuntimeException
, 书写构造
/**
* 自定义运行时异常
*/
public class MyRuntimeException extends RuntimeException {
//无参构造
public MyRuntimeException(){}
//有参构造
public MyRuntimeException(String message) {
super(message);
}
}
- 自定义非运行时异常: 继承
Exception
, 书写构造
/**
* 自定义非运行时异常
*/
public class MyException extends Exception {
//无参构造
public MyException(){}
//有参构造
public MyException(String message) {
super(message);
}
}
有参构造作用: 最终调用
Throwable
中的有参构造给detailMessage
详细信息属性赋值 , 该属性的值可通过getMessage()
获取