目录
- 1.Exception 和 Error 有什么区别?
- 2.Throwable 类中常用方法有哪些?
- 3.Checked Exception 和 Unchecked Exception 有什么区别?
- 4.throw 和 throws 的区别是什么?
- 5.try-catch-finally 如何使用?
- 6.finally 块中的代码一定会执行吗?
- 7.异常使用有哪些需要注意的地方?
- 8.什么时候用 assert?如何使用 assert?
- 9.什么是内存泄漏和内存溢出?
1.Exception 和 Error 有什么区别?
(1)首先来看看 Java 中的异常层次结构图:
(2)在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类,Throwable 类有两个重要的子类:
- Exception:程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为以下两种异常:
- Checked Exception:受检查异常,必须处理;
- Unchecked Exception:不受检查异常,可以不处理;
- Error:Error 属于程序无法处理的错误 ,例如虚拟机内存溢出错误 (OutOfMemoryError)、类定义错误 (NoClassDefFoundError) 等 。这些异常发生时,Java 虚拟机一般会选择线程终止。
2.Throwable 类中常用方法有哪些?
(1)String getMessage()
:返回异常发生时的简要描述。
(2)String toString()
:返回异常发生时的详细信息。
(3)String getLocalizedMessage()
:返回异常对象的本地化信息。使用 Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage() 返回的结果相同。
(4)void printStackTrace()
:在控制台上打印 Throwable 对象封装的异常信息。
3.Checked Exception 和 Unchecked Exception 有什么区别?
(1)Checked Exception 即受检查异常,Java 代码在编译过程中,如果受检查异常没有被 catch 或者 throws 关键字处理的话,就无法通过编译。例如下面这段 I/O 操作的代码:
(2)Unchecked Exception 即不受检查异常,Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。RuntimeException 及其子类都统称为不受检查异常,常见的有:
- ArithmeticException(算术异常)
- ClassCastException (类转换异常)
- IllegalArgumentException (非法参数异常)
- NumberFormatException(字符串转换为数字格式异常,IllegalArgumentException 的子类)
- IndexOutOfBoundsException (下表越界异常)
- NullPointerException (空指针异常)
- SecurityException (安全异常)
- UnsupportedOperationException(不支持的操作错误,例如重复创建同一用户)
更多 RuntimeException 的子类可在 IDEA 中进行查看:
4.throw 和 throws 的区别是什么?
(1)throw
① 表示方法内抛出某种异常对象(只能是一个);
② 用于程序员自行产生并抛出异常;
③ 位于方法体内部,可以作为单独语句使用;
④ 如果异常对象是非 RuntimeException,则需要在方法申明时加上该异常的抛出,即需要加上 throws 语句或者在方法体内使用 try catch 处理该异常,否则编译报错;
⑤ 执行到 throw 语句则后面的语句块不再执行;
(2)throws
① 方法的定义上使用 throws 表示这个方法可能抛出某些异常(可以有多个);
② 用于声明在该方法内抛出了异常;
③ 必须跟在方法参数列表的后面,不能单独使用;
④ 需要由方法的调用者进行异常处理;
package test;
import java.io.IOException;
class Solution {
/**
* 测试 throws 关键字
* @throws NullPointerException
*/
public static void testThrows() throws NullPointerException {
Integer i = null;
System.out.println(i + 1);
}
/**
* 测试 throw 关键字抛出运行时异常
* @param i
*/
public static void testThrow(Integer i) {
if (i == null) {
//运行时异常不需要在方法上申明
throw new NullPointerException();
}
}
/**
* 测试 throw 关键字抛出非运行时异常,需要方法体需要加 throws 异常抛出申明
* @param filePath
*/
public static void testThrow(String filePath) throws IOException {
if (filePath == null) {
//非运行时异常,需要方法体需要加 throws 异常抛出申明
throw new IOException();
}
}
public static void main(String[] args) {
testThrows();
Integer i = null;
testThrow(i);
String filePath = null;
try {
testThrow(filePath);
} catch (IOException e) {
e.printStackTrace();
}
}
}
上述代码中三个方法对应的异常如下:
5.try-catch-finally 如何使用?
(1)try 块:用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。
(2)catch 块: 用于处理 try 捕获到的异常。
(3)finally 块 : 无论是否捕获或处理异常,finally 块里的语句都会被执行。但需要注意的是,当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。
示例代码如下:
public static void main(String[] args) {
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
} finally {
System.out.println("Finally");
}
}
输出结果如下:
Try to do something
Catch Exception -> RuntimeException
Finally
注意:不要在 finally 语句块中使用 return!当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。
6.finally 块中的代码一定会执行吗?
(1)finally 块中的代码不一定会执行!例如在执行到 finally 块之前 JVM 就被终止运行的话,finally 中的代码就不会被执行。
public static void main(String[] args) {
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
// 终止当前正在运行的 JVM
System.exit(1);
} finally {
System.out.println("Finally");
}
}
输出结果如下:
Try to do something
Catch Exception -> RuntimeException
(2)另外,在以下 2 种特殊情况下,finally 块的代码也不会被执行:程序所在的线程死亡、关闭 CPU。
7.异常使用有哪些需要注意的地方?
(1)不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。
(2)每次手动抛出异常,我们都需要手动 new 一个异常对象抛出。抛出的异常信息一定要有意义。
(3)建议抛出更加具体的异常,例如出现字符串转换为数字格式错误的时候,就应该抛出 NumberFormatException 而不是其父类IllegalArgumentException。
(4)使用日志打印异常之后就不要再抛出异常了(两者不要同时存在一段代码逻辑中)。
8.什么时候用 assert?如何使用 assert?
(1)assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,assertion 用于保证程序最基本、关键的正确性。assertion 检查通常在开发和测试时开启。
(2)为了提高性能,在软件发布后, assertion 检查通常是关闭的。在实现中,断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为 true;如果表达式计算为 false,那么系统会报告一个 AssertionError。
(3)断言用于调试目的:
assert(a > 0); // throws an AssertionError if a <= 0
(4)断言有以下两种形式:
assert Expr1;
assert Expr1 : Expr2;
Expr1 应该总是产生一个布尔值。 Expr2 可以是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息。这两种表达形式本质是一样的,不同在于第二种方式中,可以指定输出错误的信息。
(5)断言在默认情况下是禁用的。
① 要在编译时启用断言,需使用 source 1.4 标记:
javac -source 1.4 Test.java
② 要在运行时启用断言,可使用 -ea 或者 -enableassertions 标记。
③ 要在运行时选择禁用断言,可使用 -da 或者 -disableassertions 标记。
④ 要在系统类中启用断言,可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。
(6)可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法 的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以 在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。
9.什么是内存泄漏和内存溢出?
(1)内存泄漏 (Memory Leak):指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
(2)内存溢出 (Out Of Memory,简称 OOM):指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。常见的解决方案有:
① 修改JVM启动参数,直接增加内存。
② 检查错误日志,查看 “Out Of Memory” 错误前是否有其它异常或错误。
③ 对代码进行走查和分析,找出可能发生内存溢出的位置。
④ 使用内存查看工具动态查看内存使用情况。
(3)内存泄漏最终会导致内存溢出。