JavaSE进阶七 异常

1,异常机制

  • 什么是异常,java提供异常处理机制有什么用?

    • 以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常。
    • java语言是很完善的语言,提供了异常的处理方式,程序执行过程中出现了不正常情况,
      java把该异常信息打印输出到控制台,供程序员参考;程序员看到异常信息之后,可以
      对程序进行修改,让程序更加的健壮。
  • 程序执行控制台出现了:

      Exception in thread "main" java.lang.ArithmeticException:
      这个信息被我们为:异常信息,这个信息是JVM打印的。
    
  • 总结:

    • 什么是异常,程序执行过程中的不正常情况。
    • 异常的作用:增强程序的健壮性。
代码示例
public class ExceptionTest01 {
    public static void main(String[] args) {
        // System.out.println(10/0);// 出现异常  Exception in thread "main" java.lang.ArithmeticException:

        // 通过"异常类"实例化异常对象
        NumberFormatException numberFormatException = new NumberFormatException("数字格式化异常!");
        System.out.println(numberFormatException);
        NullPointerException nullPointerException = new NullPointerException("空指针异常!");
        System.out.println(nullPointerException);
    }

}

2,异常的存在形式

  • 异常在java中以类的形式存在,每一个异常类都可以创建异常对象。
  • 类是模板,对象是实际存在的个体。
  • Object下有个Throwable(可抛出的),Throwable有两个分支:
    • Error(不可处理,直接退出jvm)
    • Exception(可处理的):
      • Exception的直接子类:编译时异常
        • 要求程序员编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。
        • 受检异常:CheckedException
        • 受控异常:
      • RunTimeException:运行时异常。
        • 在编写程序阶段程序员可以预先处理,也可以不管,都可以。
        • 未受检异常:UnCheckedException
        • 未受控异常:
  • 编译时异常和运行时异常,都是发生在运行阶段,编译阶段异常是不会发生的。因为只要程序运行阶段才可以new对象,异常的发生就是new异常对象。
  • 编译时异常和运行时异常的区别:
    • 编译时异常一般发生的概率比较高
    • 运行时异常一般发生的概率比较低

3,异常的处理

  • Java语言中对异常的处理包括两种方式:
    • 1,在方法声明的位置上,使用throws关键字,抛给上一级。
    • 2,使用try..catch语句进行异常的捕捉。
  • 注意:java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续上抛,抛给了调用者JVM,JVM知道了这个
    异常发生,只有一个处理结果:终止java程序的执行。

3.1 分析异常案例

public class ExceptionTest02 {
    public static void main(String[] args)  {
        /*
            System.out.println(100/0);
            程序执行发生了ArithmeticException异常,底层new了一个ArithmeticException异常对象,
            然后抛出了,由于是main方法调用了"100/0",所以这异常ArithmeticException抛给了main
            方法,main方法没有处理,将这个异常抛给了JVM,JVM终止了程序的执行。

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

        // 调用doSome方法
        // 因为doSome方法声明位置上有throws ClassNotFoundException
        // 所以我们在调用doSome方法时候必须对这种异常进行预先处理,如果不处理编译器会报错
        // 编译报错信息: java: 未报告的异常错误java.lang.ClassNotFoundException; 必须对其进行捕获或声明以便抛出
       
        // doSome();

    }

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

3.2 处理异常

public class ExceptionTest03 {

    // 第一种处理方式,在声明方法的位置上继续使用throws,异常继续上抛,抛给调用者。
//    public static void main(String[] args) throws ClassNotFoundException {
//        doSome();
//    }

    public static void main(String[] args) {
        // 第二种处理方式:try..catch进行捕捉
        try {
            doSome();
        } catch (ClassNotFoundException e) {
            // catch捕捉异常之后,处理异常
            e.printStackTrace();
        }
    }

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

3.3 异常对象的两个重要方法:

  • 获取异常简单的描述信息

      String msg = exception.getMessage();
    
  • 打印异常追踪的堆栈信息:

      exception.printStackTrace();
    
代码示例
public class ExceptionTest05 {
    public static void main(String[] args) {
        NullPointerException e = new NullPointerException("空指针异常");
        String msg = e.getMessage();
        System.out.println(msg);// 空指针异常
        //  打印异常追踪的堆栈信息
        e.printStackTrace(); // java.lang.NullPointerException: 空指针异常

        System.out.println("hello world"); // e.printStackTrace();  不影响hello world的输出
    }
}

3.4 处理异常深入了解try..catch

  • catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常的父类型。
  • catch可以写多个,建议catch的时候,精确的一个一个的处理。这样有利于程序的调试。
  • catch写多个的时候,从上到下,必须遵守从小到大。
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

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

        try {
            FileInputStream fis = new FileInputStream("");
            fis.read();
            //
            System.out.println(100/0); // 这个异常是运行时异常,编写程序时可以处理,也可以不处理。
        }catch (FileNotFoundException e){ // 所有异常都走catch分支
            System.out.println("文件不存在");
        }catch (ArithmeticException | NullPointerException e){ // jdk8之后新特性 可以用|的形式编写
            System.out.println("读取文件失败");
        }catch (IOException e){

        }
    }
}

3.5 关于try..catch中finally子句

  • 在finally子句中的代码是最好执行的,并且一定会执行,即使try语句块中的代码出现了异常。
    • finally子句必须和try一起出现,不能单独编写。
  • finally语句通常使用在哪些情况下呢?
    • 通常在finally语句块中完成资源的释放/关闭,因为try语句块中的代码出现异常,finally中的代码也会正常执行。
代码示例
public class ExceptionTest06 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            // 创建输入流对象
            fis = new FileInputStream("");
            String s = null;
            s.toString(); // 这里会出现空指针异常

            // 流使用完需要关闭,因为流是占用资源的
            // 即使程序出现了异常,流也必须关闭!
            // 放在这里有可能关闭不了。
            fis.close();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }catch (NullPointerException e){
            e.printStackTrace();
        }finally {
            // 流的关闭放在这里比较保险,finally中的代码一定会执行的。
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3.6 finally面试题

3.6.1 final、finally、finalize有什么区别
  •   final是一个关键字,表示最终的不变的
      finally也是一个关键字,和try联合使用,使用在异常处理机制中,在finally语句块中的代码一定会执行。
      finalize是标识符,是Object类中的一个方法,作为方法名出现。
    
3.6.2 分析下面程序的运行结果
public class ExceptionTest08 {
    public static void main(String[] args) {
        int result = m();
        System.out.println(result);// 100
    }
    /*
        java语法规则
            方法体中的代码必须遵循自上而下顺序依次执行,
            return语句一旦执行,整个方法必须结束。
     */

    public static int m(){
        int i = 100;
        try {
            // 这行代码出现在int i = 100的下面,所以最终结果必须是返回100
            // return语句还必须保证是最后执行的,一旦执行整个方法结束。
            return i;
        }finally {
            i++;
        }
    }
}

上篇:JavaSE进阶六 通用类
下篇:JavaSE进阶八 集合一 Collection接口

你可能感兴趣的:(JavaSE进阶七 异常)