体系

我们在使用java的时,经常面对和处理的是异常(Exception)很少处理错误。因为如果是错误级别的往往都是比较底层的非代码层面的问题。
但是这两个的区别,有必搞清楚。
这两个是一对难兄难弟,有问题的时候都会出现这两兄弟。

通过图片可以直观的看出它们的体系,这图点开看比较清楚:

exception和error体系图

异常 Exception

这个是最常遇见的问题,主要是由于编码原因异常的问题。
我们开发过程中常见的是运行时异常,就是字面意思,运行时才知道的异常,运行时,才会有可能抛出来的异常。
那相对的,就有非运行时的异常,就是不需要运行,也能知道是异常。

而异常当中,又有几个概念,这些概念性的东西,只是帮助分类和理解,使用场景可以说是经常遇见,分别是:

两种异常:

  • 运行时异常 RuntimeException
  • 异常 Exception

运行时异常 RuntimeException

只有运行时才会知道是否有异常,比如下面这段代码会不会抛常异?

1
2
3
4
5
6
public class Test {

public void test(int a, int b) {
int c = a / b;
}
}

会不会抛异常,用眼睛看很合理,没毛病,但是问题是你不知道ab分别会传什么值进来。
如果传:

a = 9;
b = 3;

没问题,但是如果传:

a = 1;
b = 0;

就抛异常了,上面那段代码不在运行时,你根本不知道会不会抛异常,所以只有运行时才会知道,就叫运行时异常。
运行时异常

处理RuntimeException的原则是:如果出现 RuntimeException,那么一定是程序员的错误。
例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。
其他(IOException等等)checked 异常一般是外部错误,例如试图从文件尾后读取数据、网络中断等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。

常见的运行时异常有:

  • NullPointerException: 空指针异常
  • IndexOutOfBoundsException: 下标越界异常
  • IllegalArgumentException: 传递非法参数异常
  • ClassCastException: 类型强制转换异常
  • NumberFormatException: String转换为指定的数字类型异常
  • ArithmeticException: 算术运算异常 如除数为0

异常 Exception

除了上面的 运行时异常 RuntimeException之外,其他的异常都是 Exception 的子类,都是检查时异常Checked Exception
这种异常程序无法恢愎,运行出现时会导至程序终止,如空指针。
IOException、SQLException,这些都是异常。
上面已经总结了。

错误 Error

代码运行中不是由代码引起的问题,是由外部资源异至的JVM错误,一般就归到错误里,通常由JVM处理问题,有的错误JVM也处理不了。
Error 是可以被捕获的,但是程序都已经出现了JVM都无法处理的错误,捕获的意思除了打印详情外,还让程序继续执行,比如 OOM 这种错误,还有必要执行吗,生产环境,这样做可能会造成经济损失,所以完全不建议捕获错误。

举个例子,手动制造内存溢出,使JVM产生错误后退出。

JVM 启动参数:

-Xms20M -Xmx20M -Xmn20M -XX:+HeapDumpOnOutOfMemoryError

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestOOM {

public static void main(String[] args) {
test1();
}

public static void test1() {
List<String> list = new ArrayList<String>();
try {
while (true) {
list.add(new String());
}
} catch (Exception exception) {
System.out.println("output...");
}
}
}

结果:
JVM 遇到错误后,直接就退出了,catch 里的语句没有打印。

运行错误

checked exception 和 unchecked exception

这两个就是字面上的意思:

  • 已检查异常 checked exception
  • 未检查异常 unchecked exception

捕获错误 不推荐

还是那句话,JVM都Error了,再让程序运行,没有意义,不能保证JVM能恢复正常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.liukai.check;

public class TestCatchError extends Error {
private static final long serialVersionUID = -351488225420878020L;

public TestCatchError() {
super();
}

public TestCatchError(String msg) {
super(msg);
}

public static void main(String[] args) {
try {
throw new TestCatchError("test catch error");
} catch (Throwable t) {
System.out.println("step in the catch ~");
t.printStackTrace();
}
}
}

捕获错误

checked exceptions

需要在代码中显式地在方法签名中加上throws语句,或用throws-catch语句处理,否则编译不通过。
比如在使用IO类时,JDK一定会要求加上throws-catch,因为这些方法JDK已经throws了,也就是 checked 了,不加不行。

unchecked exceptions

不需要在代码中显式地处理,事实上是不鼓励显式的处理,因为这样的代码是多余的。
我们写的大部分代码都是 unchecked ,因为在代码中不可能每一行、每一个代码块都是添加 try-catch,这样直接设计JDK全局加上就完了,还需要程序员去关心checked不checked,我们只需要在可能出现的地方加上try-catch就可以了,这种问题完全依靠代码健状性即可。

check和uncheck

总结

异常可以被补获,而错误不能,会异常JVM异常退出。
一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。
对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。