2.异常类

异常介绍

当程序运行过程中抛出异常后,程序就自动退出,下面的代码就不执行了。如果抛出异常的问题是个致命的问题,就会导致系统崩溃。所以java设计提出了一个异常处理机制来解决这个问题。

异常处理机制:如果程序员认为某段程序可能会出现异常,可以使用try{...}catch{...}来捕获异常。捕获到的异常可以自己需要的方式进行处理。

基本概念

在java语言中,将程序执行中发生的不正常情况称之为"异常"。开发过程中语法错误、编译错误不属于异常;逻辑错误可能导致运行时异常。。

程序运行过程中的异常分为两大类:

  • Error(错误): java虚拟机无法解决的严重问题,程序会崩溃。如:jvm系统内部错误、StackOverflowError栈溢出等。

  • Exception: 其它因编程错误或者偶然的外部因素导致的一般性问题,可以使用针对性的代码处理。如:空指针异常、试图读取不存在的文件、网络连接中断等。

    Exception也分为两大类:

    • 编译异常

    • 运行时异常

异常体系图

  1. Throwable类是所有异常类的基类,它实现了Serializable接口。

  2. Error类是所有Error异常类的基类,非运行时异常直接继承 Exception 或其子类(但不继承 RuntimeException)。这两个类同时继承了Throwable类。

  3. 运行时异常类都会继承RuntimeException类,所以RuntimeException类是所有运行时异常类的基类。

    运行时异常类,编译器检查不出来。一般是指编译时程序逻辑错误,是程序员应该避免其出现的异常。所以一般不作处理,因为这样的类异常很普遍,若全处理可以对程序的可读性和运行效率产生影响。

  4. 编译时(非运行时)异常类都会继承Exception类。编译时(非运行时)异常类是编译器要求必须处理的异常。

常见异常

1. 运行时异常(非受检,RuntimeException 及其子类)

编译器不强制处理,通常是程序员逻辑错误,应通过编码避免。

异常类 含义 典型触发场景
ArithmeticException 算术运算异常 整数除以 0:int a = 1 / 0;(注意:1.0 / 0 得 Infinity,不抛)
NullPointerException 空指针异常 调用 null 对象的方法/字段、访问 null 数组元素、自动拆箱 null 包装类
ArrayIndexOutOfBoundsException 数组下标越界 int[] a = new int[3]; a[3] = 1;
StringIndexOutOfBoundsException 字符串下标越界 "abc".charAt(5)、"abc".substring(2, 5)
IndexOutOfBoundsException 索引越界(父类) 集合越界:list.get(10)
NegativeArraySizeException 数组长度为负 new int[-1];
ArrayStoreException 数组元素类型不兼容(存入的对象类型与数组声明类型不符) Object[] a = new String[2]; a[0] = Integer.valueOf(1);
ClassCastException 类型转换异常 强制转换失败:Object o = "s"; Integer i = (Integer) o;(o == null 时不会 抛,结果为 null)
NumberFormatException 数字格式异常(字符串转数字失败) Integer.parseInt("abc");
IllegalArgumentException 方法接收到不合法参数 Thread.sleep(-1)、new ArrayList<>(-1)
IllegalStateException 对象状态不适合当前操作 Iterator 在 remove() 前未 next()
IllegalMonitorStateException 监视器状态错误 未持有对象锁 就调用 obj.wait()/notify()/notifyAll(),或未在 synchronized 块内
IllegalThreadStateException 线程状态不适合当前操作 对已启动 的线程再次调用 start()
UnsupportedOperationException 不支持该操作 Arrays.asList(...) 返回的固定大小 List 调用 add()
ConcurrentModificationException 并发修改异常 用 for-each 遍历集合时直接 list.remove()(应用 Iterator.remove())
SecurityException 安全异常:安全管理器(SecurityManager)拒绝访问受限资源(文件、网络、反射、系统属性等) 沙箱环境 / 启用 SecurityManager 时访问受限资源
OutOfMemoryError Error 而非 Exception:JVM 无法分配内存 无限创建大对象、堆内存耗尽;不要 catch 它,应排查内存泄漏
StackOverflowError Error:栈深度超限 无限递归(无终止条件)

2. 受检异常(非运行时异常,Exception 的子类但不继承 RuntimeException)

编译器强制要求 :要么 try-catch 捕获,要么在方法签名上 throws 声明,否则编译不通过。

异常类 含义 典型触发场景
IOException I/O 操作异常(父类) 读写文件失败、流已关闭、网络中断
FileNotFoundException 文件未找到(IOException 子类) new FileInputStream("不存在.txt")
EOFException 输入未完成即遇文件结尾(IOException 子类) DataInputStream.readInt() 读到文件末尾仍继续读
ClassNotFoundException 找不到指定类(类加载阶段) Class.forName("com.xxx.NotExist");反射/驱动加载时类不在 classpath
InterruptedException 线程在 sleep/wait/join 中被中断 调用 thread.interrupt() 打断阻塞中的线程;捕获后应 Thread.currentThread().interrupt() 恢复中断标志
CloneNotSupportedException 对象未实现 Cloneable 接口 却调用 clone() class A {} 后 new A().clone()(Object.clone() 是 protected 且检查接口)
NoSuchMethodException 反射时找不到指定方法 clazz.getMethod("notExist")(普通方法调用编译期就检查,不会抛这个)
IllegalAccessException 反射访问 无权访问的成员(非 public 且未 setAccessible(true)) field.get(obj) 访问 private 字段
NoSuchFieldException 反射找不到字段 clazz.getField("notExist")
InstantiationException 反射创建实例失败 clazz.newInstance() 作用于接口/抽象类/无无参构造
SQLException 数据库访问异常 连接失败、SQL 语法错误
ParseException 解析异常(java.text) new SimpleDateFormat("yyyy-MM-dd").parse("abc")

异常处理

当程序发生异常时,分为两种方式处理异常:

try...catch...finally

java 复制代码
try{
     //代码可能有异常 
}catch(Exception e) { //如果有异常,捕获到异常。可以写多个catch。
    //当异常发生时,系统将异常封装成Exception 对象e,传递给catch
    //得到异常对象后,程序员自己处理
}finally{//finally可以不写
	//不管try代码块是否有异常发生,始终要执行finally。
    //所以,通常将释放资源的代码,放在finally
}

注意细节:

  1. 如果异常发生了,try语句块中的异常发生的语句之后的代码不会被执行。会直接进入到catch代码块中,执行完成之后,还会继续执行try...catch之外的代码。
  2. 不管try代码块是否有异常发生,始终要执行finally。有极少数情况不执行:
    • System.exit() / Runtime.halt()
    • JVM 崩溃(如 StackOverflowError 后再次出错、OOM 直接 kill 进程)
    • 线程被强制终止(已废弃的 Thread.stop)
  3. 可以捕获多个异常即写多个catch语句,要求子类在前面,父类异常在后面。如果发生异常,只会进入第一个匹配的 catch 分支。否则父类在前"导致"子类 catch 永远不可达
  4. 可以进行try-finally组合使用,这种组合的意思是不做异常处理,但是要做别的一些事情。由于没有捕获异常,所以异常最终会throw给JVM,程序会直接崩溃。

一道练习题:

java 复制代码
public class Exception2 {
    public static void main(String[] args) {
        System.out.println(method());
    }
    
	public static int method() {
        int i = 1;
        try {
            i++; //2
            String[] strings = new String[3];
            if (strings[1].equals("tom")) {
                System.out.println(strings[1]);
            } else {
                strings[3] = "light";
            }
            return 1;
        } catch (ArrayIndexOutOfBoundsException e) {
            return 2;
        } catch (NullPointerException e) {
            return ++i; //由于finally没有return语句,jvm底层会先将i保存到temp中,执行finally语句块,然后返回temp
        } finally {
            ++i;
            System.out.println("i=" + i);
        }
    }
}

throws

将异常抛出,由上层调用者来处理,最顶级的处理者就是JVM。

在方法声明中使用的throws语句可以声明抛出异常列表(列表中的异常是平级的),throws后面的异常类型可以是方法中产生的异常,也可以是它的父类。

使用细节:

子类重写父类的方法时,子类重写的方法所抛出的异常类型要么和父类抛出异常一致,要么为父类抛出的异常类型的子类型。(子类抛出的异常类型<=父类抛出的异常类型)

运行时异常根本不需要 throws 声明,编译器不检查;若未处理会沿调用栈自动向上传播,最终由 JVM 处理(终止当前线程)。

自定义异常

当程序中出现了某些"错误",但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。

步骤:

1)定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException

2) 如果继承Exception,属于编译异常。如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)

  1. 一般使用throw关键字抛出异常。
java 复制代码
//测试类
public class CustomException {
    public static void main(String[] args) {
        ThrowAgeException ex = new ThrowAgeException();
        try{
            ex.throwsExceptionMethod();
        }catch (AgeException me){
            System.out.println("AgeException:"+me.getMessage());
        }
    }
}
//抛出异常类
class ThrowAgeException {
    public void throwsExceptionMethod() {
        int age = 180;
        if (!(age > 20 && age <= 120)) {
            throw new AgeException("年龄不合法");
        }
    }
}
//自定义类
class AgeException extends RuntimeException{
    private String message;

    public AgeException(String message) {
        super(message);
        this.message = message;
    }

    @Override
    public String getMessage() {
        return this.message;
    }
}

throws和throw区别

练习题:说出下面程序可能出现的异常

java 复制代码
public static void main(String[] args) {
    if(args[4].equals("john")){
        System.out.println("AA");
    }else{
        System.out.println("BB");
    }
    Object o= args[2];
    Integer i= (Integer)o;
}

1.如果args为空,数组越界异常、空指针异常。

2.Integer i = (Integet)o会出现类型转换异常。

相关推荐
淡海水1 小时前
05-03-源码-Boehm GC分配与碎片管理
开发语言·算法·unity·c#·游戏引擎·gc
努力实干1 小时前
C++ 面向对象与工程架构
开发语言·c++·架构
十年Java程序媛1 小时前
JVM 基础与 GC 垃圾回收详解|可达性分析、分代模型、垃圾收集器、STW
java·开发语言·jvm·spring boot·后端·面试
估值探索者1 小时前
【Python量化选股流水线 #03】候选股怎么排序?动量+低波+流动性三因子打分取 Top-N
开发语言·python·接口·api接口·股票数据接口·数据接口·股票数据api接口
骑士雄师1 小时前
supervisor 中关于上下文多轮信息维护常用到的python语法
开发语言·python
曹牧1 小时前
C#:字符串占位符与值数量不一致
开发语言·数据库·c#
2301_32241428041 小时前
基于SSM框架的在线考试管理系统的设计与实现14750-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·redis·mysql·tomcat·maven·mybatis·ssm
clz13145211 小时前
RuoYi-Cloud 新建微服务并实现服务间调用(OpenFeign 实战)
java·ruoyi
迅猛龙办公室1 小时前
Python函数实现反转一个只有3位数的整数
开发语言·前端·python