JavaSE--异常处理机制

发布时间 2023-08-15 21:58:43作者: 洛小依ovo

一、异常exception

1、什么是异常,以及Java提供的异常处理机制

  程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常

  java语言提供了异常的处理方式,如果程序执行过程中出现了不正常情况,

  java把该异常信息打印输出到控制台,供程序员参考。程序员看到异常信息之后,可以对程序进行修改,让程序更加的健壮

2、异常以类和对象的形式存在

  异常在Java中以类的形式存在,每一个异常类都可以创建异常对象

// 通过“异常类”实例化“异常对象”
NumberFormatException nfe = new NumberFormatException("数字格式化异常!");
// java.lang.NumberFormatException: 数字格式化异常!
System.out.println(nfe);

// 通过“异常类”创建“异常对象”
NullPointerException npe = new NullPointerException("空指针异常发生了!");
//java.lang.NullPointerException: 空指针异常发生了!
System.out.println(npe);

3、遇到异常,Java会自己new对象

 /*
  程序在这里除以0,
  发生了ArithmeticException异常,
  底层new了一个ArithmeticException异常对象,
  然后抛出了,由于是main方法调用了100 / 0,
  所以这个异常ArithmeticException抛给了main方法,
  main方法没有处理,将这个异常自动抛给了JVM。VM最终终止程序的执行。

        ArithmeticException 继承 RuntimeException,属于运行时异常。
        在编写程序阶段不需要对这种异常进行预先的处理。
*/
System.out.println(100 / 0);

// 这里的HelloWorld没有输出,没有执行。
System.out.println("Hello World!");

 

二、异常处理机制

1、异常的处理结构

Throwable继承自Object超类
// Throwable(可抛出的) 不管是错误还是异常都是可以抛出的
Error和Exception继承自Throwable
// Error是错误类,只要有错误发生,Java程序只有一个结果,就是终止程序执行,退出jvm
// 错误是不能处理的

// Exception是异常类
Exception分为:Exception的直接子类、RuntimeException

编译时异常
所有Exception的直接子类,都叫做编译时异常。编译时异常不是在编译阶段发生的
编译时异常是表示必须在编写程序的时候预先对这种异常进行处理,如果不处理编译器报错。

运行时异常
所有的RuntimeException及子类都属于运行时异常。运行时异常在编写程序阶段,你可以选择处理,也可以不处理。

 2、编译时异常和运行时异常的区别

编译时异常发生概率高,编译时异常又被称为受检异常或者受控异常

运行时异常发生概率低,运行时异常又被称为未受检异常或者未受控异常

注意::所有异常都是发生在运行阶段的

3、异常处理的两种方式

  1)第一种方式:

    在方法声明位置上,使用throws关键字看,抛给上一级(异常的上抛)

    谁调用我就抛给谁

    注意:一般不建议抛给main方法,main方法中最好使用try catch

public class ExceptionTest04 {
    public static void main(String[] args) {
        // main方法中调用doSome()方法
        // 因为doSome()方法声明位置上有:throws ClassNotFoundException
        // 我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
        // 如果不处理,编译器就报错
        //编译器报错信息: Unhandled exception: java.lang.ClassNotFoundException
        doSome();// 编译时报错
    }

    /**
     * doSome方法在方法声明的位置上使用了:throws ClassNotFoundException
     * 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常。
     * 叫做类没找到异常。这个异常直接父类是:Exception,所以ClassNotFoundException属于编译时异常。
     * @throws ClassNotFoundException
     */
    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome!!!!");
    }

}
// 第一种:使用throw关键字
public class ExceptionTest04 {
    // 抛给调用者
    public static void main(String[] args) throws ClassNotFoundException{
        doSome();
    }
    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome!!!!");
    }

}

注意注意:throws抛异常时可以抛出该异常,也可以抛出该异常的父类异常,也可以抛出所有异常

// 抛FileNotFoundException的父对象IOException,这样是可以的。因为IOException包括FileNotFoundException
private static void m2() throws IOException {

// Exception包括所有的异常
private static void m2() throws Exception{
    
// throws后面也可以写多个异常,可以使用逗号隔开。
private static void m2() throws ClassCastException, FileNotFoundException{

  1)第二种方式:

    使用try... catch语句进行异常的捕捉(异常的捕捉)

注意:Java中 异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,JVM知道这个异常发生,只有一个结果:终止java程序的执行。

// 第二种try...catch
public static void main(String[] args) {
    try {
        // 尝试
        doSome();
    } catch (ClassNotFoundException e) {// e是为这个异常取的名字
        // catch是捕捉到异常之后走的分支
        System.out.println("类没有发现异常");
        e.printStackTrace();
    }
}

    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome!!!!");
    }
}

注意注意:只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。另外需要注意,try语句块中的某一行出现异常,该行后面的代码不会执行

4、try...catch的深入了解

  1)catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型,也可以抛出Exception

  2)catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试

  3)catch写多个的时候,从上到下,必须遵守从小到大

public class ExceptionTest07 {

    public static void main(String[] args) {
        try {
            //创建输入流
            FileInputStream fis = new FileInputStream("D:document\\风格.pdf");
            //读文件
            fis.read();
        } catch(FileNotFoundException e) {
            System.out.println("文件不存在!");
        } catch(IOException e){
            System.out.println("读文件报错了!");
        }
 
    }
}
 // JDK8的新特性
// 可以 或 异常,这个异常或者那个异常
try {
    //创建输入流
    FileInputStream fis = new FileInputStream("D:\\curse\\02-JavaSE\\document\\JavaSE进阶讲义\\JavaSE进阶-01-面向对象.pdf");
    // 进行数学运算
    System.out.println(100 / 0);
    // 这里这些异常可以 或
} catch(FileNotFoundException | ArithmeticException | NullPointerException e) {
    System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
}

5、上抛和捕捉怎么选择

  如果希望调用者来处理,就使用上抛。其他情况使用捕捉

6、异常对象常用方法

获取异常简单的描述信息:
    String msg = exception.getMessage();

打印异常追踪的堆栈信息:
    exception.printStackTrace();
// 这里只是为了测试getMessage()方法和printStackTrace()方法。
// 这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。
NullPointerException e = new NullPointerException("空指针异常");

// 获取异常简单描述信息:这个信息实际上就是构造方法上面String参数。
String msg = e.getMessage(); //空指针异常
System.out.println(msg);

// 打印异常堆栈信息
// java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。
e.printStackTrace();

7、try catch中finally关键字

  1)

    在finally自居中的代码时最后执行的,并且一定会执行的,即使try语句块中的代码出现异常

    必须和try catch一起使用

  2)finally语句通常使用在哪些情况下

    通常在finally语句块中完成资源的释放/关闭。

    因为finally中的代码比较有保障。即使try语句块中的代码出现异常,finally中代码也会正常执行。

  3)退出jvm之后finally就不执行了

System.exit(0);

 

public class ExceptionTest10 {
    public static void main(String[] args) {
        FileInputStream fis = null; // 声明位置放到try外面。这样在finally中才能用。
        try {
            // 创建输入流对象
            fis = new FileInputStream("D:\\JavaSE进阶-01-面向对象.pdf");
            // 开始读文件....

            String s = null;
            // 这里一定会出现空指针异常!
            s.toString();
            System.out.println("hello world!");

            // 流使用完需要关闭,因为流是占用资源的。
            // 即使以上程序出现异常,流也必须要关闭!
            // 放在这里有可能流关不了。
            //fis.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch(IOException e){
            e.printStackTrace();
        } catch(NullPointerException e) {
            e.printStackTrace();
        } finally {
            System.out.println("hello 浩克!");
            // 流的关闭放在这里比较保险。
            // finally中的代码是一定会执行的。
            // 即使try中出现了异常!
            if (fis ! = null) { // 注意注意::避免空指针异常
                try {
                    // close()方法有异常,采用捕捉的方式。
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        System.out.println("hello kitty!");

    }
}

 

三、自定义异常

1、为什么自定义异常

  SUN提供的JDK内置的异常肯定是不够的用的

  在实际的开发中,有很多业务,这些业务出现异常之后,JDK中都是没有的和业务挂钩的。那么异常类我们可以自己定义

2、怎么定义异常

  第一步:编写一个类继承Exception或者RuntimeException

  第二步:提供两个构造方法,一个无参数的,一个带有String参数的

// 就是这样的结构,这样写就行,根据sun公司的源代码来这样写
public class MyException extends Exception{ // 编译时异常
    public MyException(){

    }
    public MyException(String s){
        super(s);
    }
}
/*
public class MyException extends RuntimeException{ // 运行时异常

}
 */

 

public class ExceptionTest15 {
    public static void main(String[] args) {

        // 创建异常对象(只new了异常对象,并没有手动抛出)
        MyException e = new MyException("用户名不能为空!");

        // 打印异常堆栈信息
        e.printStackTrace();

        // 获取异常简单描述信息
        String msg = e.getMessage();
        System.out.println(msg);
    }
}

3、怎么使用自定义异常

  trow手动抛出异常

// 先定义一个异常
/**
 * 栈操作异常:自定义异常
 */
public class MyStackOperationException extends Exception{ // 编译时异常

    public MyStackOperationException(){

    }

    public MyStackOperationException(String s){
        super(s);
    }

}
// 一个模拟栈的类
// 其中的一个方法
 /**
     * 压栈的方法
     * @param obj 被压入的元素
     */
    public void push(Object obj) throws MyStackOperationException {
        if(index >= elements.length - 1){
            /*  栈满了 这也算是一个异常,那我们就可以自定一个异常类来这个让我们使用
                以前的写法
                System.out.println("压栈失败,栈已满");
                return;
            */
            // 创建异常对象
            //MyStackOperationException e = new MyStackOperationException("压栈失败,栈已满!");
            // 手动将异常抛出去!
            //throw e; //这里捕捉没有意义,自己new一个异常,自己捉,没有意义。栈已满这个信息你需要传递出去。

            // 合并
            throw new MyStackOperationException("压栈失败,栈已满!");
        }
        index++;
        elements[index] = obj;
        System.out.println("压栈" + obj + "元素成功,栈帧指向" + index);
    }