检查型异常的问题

本文深入解析Java中的检查型异常与非检查型异常,通过实例演示如何使用反射获取对象的Field值,以及在不同访问控制下可能抛出的异常。探讨异常处理对代码结构的影响。

Java的异常分为两种异常,一种是检查型异常(checked exception),如IOException等。另一种是非检查型异常(unchecked exception),也叫运行时异常,如IllegalArgumentException等。

检查型异常和非检查型异常的区别在于,当一个方法想要抛出非检查型异常时,可以不在方法头抛出;而如果抛出检查型异常,则必须在方法头进行声明。当一个方法调用另一个抛出了非检查型异常的时候,可以不捕获那个方法抛出的异常;而当调用的是抛出检查型异常的方法,则必须捕获这个异常,或是在自身方法头也抛出这个异常。

关于检查型异常的问题,也是编程界著名的公案。有的专家认为检查型异常是不可或缺的,可以对异常进行很好的编程文档化。也有的专家认为检查型异常是画蛇添足,把异常处理复杂化了。比如跟Java很类似的C#中,就是没有检查型异常的。

最近看一些代码,里面调用了JDK中反射获取对象的Field值的方法:

	/**
     * @exception IllegalAccessException    if this {@code Field} object
     *              is enforcing Java language access control and the underlying
     *              field is inaccessible.
     * @exception IllegalArgumentException  if the specified object is not an
     *              instance of the class or interface declaring the underlying
     *              field (or a subclass or implementor thereof).
     * @exception NullPointerException      if the specified object is null
     *              and the field is an instance field.
     * @exception ExceptionInInitializerError if the initialization provoked
     *              by this method fails.
     */
    @CallerSensitive
    public Object get(Object obj)
        throws IllegalArgumentException, IllegalAccessException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        return getFieldAccessor(obj).get(obj);
    }

多余的注释我删掉了。看这个方法抛出了两个异常中的其中一个IllegalAccessException,这是一个检查型异常。抛出它的时机,这个方法注释写的很清楚:
如果这个field是强制进行Java语言访问控制,并且实际定义的field是不可访问的,那么就会抛出这个异常。

Java语言访问控制,简单理解就是访问控制符,主要有以下几种:
public: 可被所有类访问
protected: 可被同一个包或子类访问
private: 只能自己访问
无控制符: 被同一个包的类访问

所以我定义了如下的一个类:

public class Model {
    private int i;

    public Model(int i) {
        this.i = i;
    }
}

以及:

public class ExceptionTest {
    public static void main(String[] args) {
        Model model = new Model(1);
        Field[] fields = model.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().equals("i")) {![在这里插入图片描述](https://img-blog.csdnimg.cn/20190615142718710.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RicWIwMDc=,size_16,color_FFFFFF,t_70)
                try {
                    int i = (int) field.get(model);
                    System.out.println(i);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这个类是获取i这个属性,并打印出来。
在这里插入图片描述
可以看出,因为i是private的,所以在另一个类里没有通过反射获取它的权限,因此捕获了被抛出的IllegalAccessException。

如果加上一行代码,变成下面这样:

public class ExceptionTest {
    public static void main(String[] args) {
        Model model = new Model(1);
        Field[] fields = model.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().equals("i")) {
                field.setAccessible(true);
                try {
                    int i = (int) field.get(model);
                    System.out.println(i);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出
在这里插入图片描述
因为调用setAccessible方法,关闭了强制的Java语言访问控制,所以能获取到i的值。

这里我要强调的是,field这个get方法的注释里明确说了,只有当没有访问这个字段的权限的时候,才会抛出IllegalAccessException。因此,当我没有调用setAccessible这个方法时,我是预期到可能会捕获这个异常的。但是当我调用了这个方法的时候,我可以确定不会抛出这个异常,那我try catch这个异常,或是再抛出这个异常有什么意义呢?

因此我这里想说的是,检查型异常某种程度上影响了代码的结构,以及开发者对异常的灵活处理,假如我们定义某个接口的方法,声明了会抛出某个异常,并且规定好了在什么情况下会抛出什么异常,那么应该由调用者来决定是否捕获异常,或者是在哪一层捕获异常。可能开发者能根据它传入参数的情况,或是代码运行的环境,或是当前代码的上下文,就能确定是否会抛出这个异常。那这种时候,就没必要捕获异常。或是在调用方法的更上层捕获异常。如果是非检查型异常,那就可以实现这个效果。但如果是检查型异常,就必须捕获,或是再次抛出这个异常了。

检查型异常和非检查型异常的争论没有定论,限于水平有限,就不展开讨论了,只是把工作中遇到的一些问题和思考记录下来。

<think>好的,用户想了解Java中的检查异常,也就是Checked Exception的定义、特点和使用场景。首先,我需要回顾一下Java的异常体系结构。根据引用[1]和[3],Java的异常分为Error、Exception,而Exception又分为检查和非检查检查异常继承自Exception,而非检查包括RuntimeException及其子类。 用户需要明确的定义,所以我得先给出检查异常的基本定义。根据引用[1],检查异常是那些继承自Exception但不是RuntimeException的异常类,比如IOException、SQLException。这部分需要准确,不能混淆。 接下来是特点。根据引用[1]和[3],检查异常的特点包括编译时检查、强制处理、可恢复性。需要解释为什么编译器要检查这些异常,比如因为它们通常是可以被合理处理的,比如文件未找到,这时候程序可能需要提示用户或者进行其他操作。 使用场景方面,引用[1]和[3]提到IO操作、数据库访问、网络通信等外部依赖的情况。需要举例子说明,比如读取文件时可能出现的FileNotFoundException,这时候必须处理或者声明抛出。 用户可能还会疑惑为什么要设计检查异常,这时候可以引用引用[3]中的内容,说明这类异常的发生难以避免,编译器检查确保开发者处理,提高程序的健壮性。同时,也要提到一些争议,比如过多的检查异常可能导致代码冗余,这时候可以提到Java 8的改进,比如try-with-resources,引用中的例子可能没有提到,但根据常识可以补充。 需要注意区分检查和非检查异常的区别,引用[2]和[4]指出非检查异常不需要强制处理,通常是程序错误,比如空指针或者数组越界。这可能帮助用户更清晰理解两者的不同。 另外,要确保回答结构清晰,分点说明,比如定义、特点、使用场景分开。同时,用户提供的引用中有提到异常处理机制,比如throws关键字和try-catch块,这部分需要结合到回答中,说明如何处理检查异常,比如必须捕获或者声明抛出。 最后,生成的相关问题需要覆盖常见疑问,比如与非检查的区别、处理方式、设计目的等,这样用户如果有后续问题可以参考。 现在需要检查引用是否正确,比如引用[1]提到检查异常继承自Exception,而RuntimeException是继承自Exception的非检查异常,这点要准确。同时,引用[3]提到checked异常如IO和SQL异常,而引用[4]补充了运行时异常的例子,这些都需要正确对应。 确认所有术语使用正确,比如“检查”和“非检查”是否有其他称呼,比如“受检查”和“非受检查”,但用户的问题中使用的是“检查”,所以保持一致。</think>### Java检查异常(Checked Exception)详解 #### 一、定义 **检查异常(Checked Exception)** 是Java异常体系中继承自 $Exception$ 类的子类(但不包括 $RuntimeException$ 及其子类)[^1]。这类异常在**编译时被强制检查**,开发者必须显式处理,否则程序无法通过编译。 常见示例包括: - $IOException$(文件操作异常) - $SQLException$(数据库访问异常) - $ClassNotFoundException$(类未找到异常) #### 二、特点 1. **编译时检查** 编译器会验证代码是否对检查异常进行了处理(通过 `try-catch` 捕获或 `throws` 声明抛出)[^1][^3]。 ```java // 必须处理FileNotFoundException public void readFile() throws FileNotFoundException { FileReader file = new FileReader("test.txt"); } ``` 2. **强制处理机制** 开发者需明确异常处理逻辑,确保程序在遇到外部错误时能合理响应[^3]。例如,文件读取失败时提示用户重试或终止操作。 3. **可恢复性场景** 检查异常通常表示程序可恢复的外部问题,如网络中断后重连、文件路径错误后重新输入等[^1]。 #### 三、使用场景 1. **外部资源操作** - 文件读写($IOException$) - 数据库访问($SQLException$) - 网络通信($SocketException$) 2. **依赖环境问题** - 类未找到($ClassNotFoundException$) - 反射调用失败($NoSuchMethodException$) 3. **业务逻辑校验** 若需强制调用方处理特定错误(如参数格式非法),可自定义检查异常: ```java public class InvalidInputException extends Exception { public InvalidInputException(String message) { super(message); } } ``` #### 四、与非检查异常的区别 | **特性** | **检查异常** | **非检查异常** | |------------------|-----------------------------------|-------------------------------------| | 继承关系 | 继承自 $Exception$(非运行时) | 继承自 $RuntimeException$ 或 $Error$ | | 编译时检查 | 是[^1][^3] | 否[^2][^4] | | 典示例 | $IOException$, $SQLException$ | $NullPointerException$, $ArrayIndexOutOfBoundsException$ | | 处理必要性 | 必须显式处理 | 可选择不处理 | #### 五、争议与改进 - **争议点**:过度使用检查异常可能导致代码冗余(如多层 `try-catch`)[^3]。 - **Java 8+改进**:通过 `try-with-resources` 简化资源管理类异常处理: ```java try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { // 自动关闭资源,减少finally块 } catch (IOException e) { e.printStackTrace(); } ``` --- 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值