上一篇地址:整理好了!2024年最常见 100 道 Java基础面试题(十七)-CSDN博客
三十五、运行时异常与受检查异常有什么区别?
在Java中,异常分为两大类:运行时异常和受检查异常,它们之间的主要区别体现在异常处理的强制性、使用场景以及它们继承的类上。
运行时异常(RuntimeException)
- 继承关系:运行时异常继承自
java.lang.RuntimeException
。 - 编译时检查:Java编译器不会强制方法或代码块捕获运行时异常或声明抛出它们。也就是说,运行时异常是可选捕获的。
- 编程错误:运行时异常通常反映了程序中的编程错误,如数组越界、空指针引用等。
- 恢复可能性:运行时异常通常被认为是不可恢复的错误,尽管在某些情况下,通过适当的错误处理逻辑,程序可能能够从这些异常中恢复。
- 示例:
NullPointerException
IllegalArgumentException
IndexOutOfBoundsException
受检查异常(Checked Exceptions)
- 继承关系:受检查异常继承自
java.lang.Exception
但不是RuntimeException
的子类。 - 编译时检查:Java编译器要求必须处理所有受检查异常。方法可以捕获这些异常或在方法签名中使用
throws
关键字声明它们,以便调用者处理。 - 外部错误:受检查异常通常表示可以从外部条件或用户操作中恢复的异常情况,如文件未找到、数据库连接失败等。
- 恢复可能性:受检查异常通常被认为是可以被程序捕获并适当处理的,以恢复程序的正常执行。
- 示例:
IOException
SQLException
GeneralSecurityException
主要区别
- 处理强制性:受检查异常必须被捕获或声明抛出,而运行时异常则没有这个要求。
- 继承类:受检查异常继承自
Exception
,运行时异常继承自RuntimeException
。 - 错误类型:受检查异常通常用于可预见的、可以恢复的错误,而运行时异常通常用于不可预见的编程错误。
- 使用场景:受检查异常适用于那些调用者可能需要知道并处理的情况,而运行时异常则适用于那些调用者通常不需要处理的情况。
示例
public class ExceptionExample {
public static void main(String[] args) {
try {
readFile(); // 可能抛出IOException
} catch (IOException e) {
e.printStackTrace();
}
try {
processArray(); // 可能抛出IndexOutOfBoundsException
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
public static void readFile() throws IOException {
// 模拟抛出受检查异常
throw new IOException("File not found");
}
public static void processArray() {
int[] array = new int[1];
// 模拟抛出运行时异常
array[2] = 5; // 这将抛出IndexOutOfBoundsException
}
}
在这个示例中,readFile
方法抛出了一个受检查异常IOException
,调用者必须捕获它或同样声明抛出。而processArray
方法抛出了一个运行时异常IndexOutOfBoundsException
,调用者可以选择捕获它,也可以不捕获。
总结
了解运行时异常和受检查异常的区别对于编写健壮且易于维护的Java程序非常重要。合理地使用异常处理可以提高程序的稳定性和可维护性。同时,应该避免滥用异常,例如,不应该使用异常来控制正常的程序流程。
三十六、什么时候会发生空指针异常?
在Java中,空指针异常是一个常见的运行时异常,属于不受检查异常(Unchecked Exceptions),它在尝试使用null
引用进行操作时抛出。
以下是一些会导致NullPointerException
的常见情况:
-
访问对象的成员变量或方法: 当尝试通过一个
null
引用访问对象的成员变量或调用其方法时,会抛出空指针异常。Object obj = null; System.out.println(obj.toString()); // 抛出NullPointerException
-
数组索引访问: 如果尝试访问一个数组的元素,而该数组为
null
,则会抛出空指针异常。String[] array = null; System.out.println(array[0]); // 抛出NullPointerException
-
调用超类的方法: 如果子类的实例的超类引用为
null
,调用超类的方法时会抛出空指针异常。class Base { void show() {} } class Derived extends Base { void test() { super.show(); // 如果super引用为null,将抛出NullPointerException } }
-
使用反射调用方法: 通过反射调用对象的方法时,如果该对象为
null
,也会抛出空指针异常。Method method = ...; Object obj = null; method.invoke(obj); // 抛出NullPointerException
-
访问类变量或静态方法: 如果尝试通过一个
null
类的实例引用访问类变量或静态方法,同样会抛出空指针异常。ClassWithStaticField cls = null; System.out.println(cls.SOME_STATIC_FIELD); // 抛出NullPointerException
-
使用字符串连接: 当使用
null
字符串与非null
字符串进行连接操作时,会抛出空指针异常。String str = null; String result = str + "some string"; // 抛出NullPointerException
-
使用
==
或!=
比较对象: 使用==
或!=
比较一个null
对象引用和非null
对象引用时,不会产生空指针异常,但如果比较涉及null
数组,则会。String str = null; boolean isEqual = str == "some string"; // 不抛出NullPointerException
-
方法返回
null
: 如果一个方法设计为返回对象,但实际返回了null
,并且调用者尝试在返回的对象上调用方法或访问变量,那么调用者处会抛出空指针异常。Object getObject() { return null; // 方法返回null } void useObject() { Object obj = getObject(); obj.toString(); // 抛出NullPointerException }
如何避免空指针异常
- 代码审查:在编写和审查代码时,注意检查可能的
null
引用使用。 - 输入验证:对所有可能为
null
的输入进行验证。 - 使用Java 8的Optional:使用
Optional
类来封装可能为null
的引用,以避免直接使用裸的null
引用。 - 断言和检查:在方法内部使用断言或显式检查来确保引用不为
null
。 - 日志记录:记录空指针异常的发生,以便于调试和分析原因。