java基础——异常

113 阅读5分钟

 一、    异常处理的任务

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引起的 ,可以根据行号去追溯异常

捕获.PNG

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();
                }

            }
        }
    }
}

捕获.PNG

这里需要特别说明一下:为什么会空指针呢? 原来,Throwable e = re.getCause();的源码可以看出,这个三元运算符,可以看到源码是判断 cause 和自身是否相等,如果相等则返回 null,如果不等则返回 cause。

如下图,debug就能看到cause和this相等,即返回了null,也就有了NullPointerException。 捕获.PNG