一、 异常处理的任务:
Java的基本理念是“结构不佳的代码不能运行”。
将控制权从错误产生的地方转移给能够处理这种情况的错误处理。所有的异常都有一个共同的祖先 Throwable(可抛出)。
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
二、 非受查异常
派生于Error类或者RuntimeException类的所有异常称为非受查异常,
RuntimeException的异常包括以下情况(自动抛出异常):
1、错误的类型转换(NumberFormatException)
2、数组访问越界 (ArrayIndexOutOfBoundsException)
3、访问null指针(java.lang.NullPointerException)
三、其他异常称为受查异常。
编译器将检查是否为所有的受查异常提供了异常处理。
总之,一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。如果方法没有声明所有可能发生的受查异常,编译器可能会发出一个错误消息。
1、 什么时候需要在方法中用throws子句声明异常,什么异常必须使用throws子句声明,以下4种情况应该抛出异常:
A、调用一个抛出受查异常的方法
eg:public FileInputStream(String name)throws FileNotFoundException
B、程序运行过程中发现错误,并且利用throw语句抛出一个受查异常
C、程序出现错误,例如非受查异常a[-1] = 0会抛出:数组访问越界 (ArrayIndexOutOfBoundsException)
D、Java虚拟机和运行时库出现的内部错误
出现前两种情况时,必须告诉方法调用者有可能抛出异常(如果处理器没有捕获这个异常,当前执行的线程就会结束)
2、 抛出异常
throws new EOFException();
或者
EOFException e = new EOFException();
throw e;
抛出异常步骤:
A、 找到一个已经存在的异常类(自定义一个派生于Exception或Exception子类的类[一般含有一个无参构造,一个有参构造])
B、 创建这个类的一个对象
C、 将对象抛出
注意:编译器严格执行throws说明符,如果调用了一个抛出受查异常的方法,就必须对它进行处理,或者继续传递。(通常,应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常继续进行传递),如果想要传递一个异常,就必须在方法的首部添加一个throws的说明符,以便告知调用者这个方法可能或抛出异常。
3、 Throws和throw的异同
A、throws是方法可能抛出异常的声明,只是一种异常出现的可能性,并不一定会发生这些异常,throw则是抛出了异常,执行throw(动作)则一定抛出了某种异常
B、throws出现在方法函数头,而throw出现在函数体
C、二者均不会由函数处理异常,真正的处理异常由函数的上层调用处理
4、 捕获异常
try {
int a = 5;
int b = 0;
String s = null;
System.out.println(a/b);
System.out.println(s.trim());
}catch (ArithmeticException arithmeticException) {
System.out.println("除0错,捕获第一个异常");
}catch (Exception e) {
System.out.println("该对象为空,如果第一个异常已经被捕获了,就轮不到我了");
}finally {
System.out.println("我是finally,我一定会执行");
}
注意:
A、Try语句块不可以独立存在,必须与catch或者finally块同存
B、catch块跟在try语句后面,它可以是一个或多个
C、catch块有一个参数,该参数是某种异常类的对象
D、多重catch语句中,异常类型必须子类在前父类在后可以加一个catch(Exception)来处理可能会被遗漏的异常
E、尽量添加finally语句块去释放占用的资源
5、 异常链
/**
* 异常链
* @author HASEE
*/
public class ExceptionLinkTest {
public void test1() throws SbException {
throw new SbException("上课要认真");
}
public void test2() {
try {
test1();
} catch (SbException e) {
RuntimeException runtimeException = new RuntimeException("不好好上课是愚蠢的,要好好努力啊");
runtimeException.initCause(e);
throw runtimeException;
}
}
public void test3() {
try {
test1();
} catch (SbException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
ExceptionLinkTest linkTest = new ExceptionLinkTest();
System.out.println("----调用test2---");
try {
linkTest.test2();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
System.out.println("----调用test3---");
try {
linkTest.test3();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
此时,运行期异常是由SBException引起的 ,可以根据行号去追溯异常
6、 把“被检查异常”转换为“不检查异常”
/**
* @author HASEE
* 被检查异常转换为不检查异常
*/
public class WrapCheckedException {
public void throwRuntimeException(int type) {
try {
switch (type) {
case 0 : throw new FileNotFoundException();
case 1 : throw new IOException();
case 2 : throw new RuntimeException("Where am i?");
default: return;
}
} catch (IOException e) {
//Adapt to unchecked;
throw new RuntimeException(e);
}
}
}
public class SomeOtherException extends Exception{
}
/**
* 转换过程
*/
public class TrunOffChecking {
public static void main(String[] args) {
WrapCheckedException wrapCheckedException = new WrapCheckedException();
wrapCheckedException.throwRuntimeException(3);
for (int i = 0; i < 4; i++) {
try {
if(i<3) {
wrapCheckedException.throwRuntimeException(i);
}else {
throw new SomeOtherException();
}
} catch (SomeOtherException e) {
System.out.println("SomeOtherException: " + e);
}catch (RuntimeException re) {
try {
//如果它的”cause“存在则返回这个异常的”cause“,否则返回空(”cause“是”throwable 发生的原因“)这个方法返回了由其他几个以
// Throwable 类型参数的构造方法,或者”initCause(Throwable)“方法设置的”cause“。虽然通常不需要重写此方法
// ,但子类可以重写此方法以通过其他方式返回原因集。这适用于将异常原因加到”Throwable“所形成的的“遗留链式抛出”。
// 注意,所有调用 getCause 方法来确定抛出的原因的 PrintStackTrace 方法不需要重写。
Throwable e = re.getCause();
throw e;
} catch (FileNotFoundException e) {
System.out.println("FileNotFoundException: " + e);
} catch (IOException e) {
System.out.println("IOException: " + e);
} catch (Throwable e) {
System.out.println("Throwable: " + e);
e.printStackTrace();
}
}
}
}
}
这里需要特别说明一下:为什么会空指针呢? 原来,Throwable e = re.getCause();的源码可以看出,这个三元运算符,可以看到源码是判断 cause 和自身是否相等,如果相等则返回 null,如果不等则返回 cause。
如下图,debug就能看到cause和this相等,即返回了null,也就有了NullPointerException。