java基础中异常处理机制

一、首先体验程序中的异常?

1.什么是异常?

  • 程序在运行中发生了意外的情况,称为异常(Exception),程序一旦出现异常,后面的代码将无法执行,程序终止
  • 为了保证后面的代码正常执行,需要对异常进行处理
  • java中采用“类”去模拟异常
  • 类是可以创建对象的
    • 比如NullPointerException e = 0x1234;表示e是引用类型,e中保存的内存地址指向堆中的“对象”,这个对象一定是NullPointerException类型
    • 这个对象就表示真实存在的异常事件
    • NullPointerException是一类异常(不同的异常对应不同的类)

2.异常小案例

package 异常机制;

public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;

        int c = a/b;
        //第8行的代码出现了异常,“没有处理”,下面的代码不会执行,直接退出了JVM
        System.out.println("Hello World");
    }
}

  • 控制台输出结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at 异常机制.Test.main(Test.java:8)
  • 上面代码,可以看出,以上程序编译通过了,但是运行时出现了异常,表示发生某个异常事件
  • JVM向控制台输出如下的信息,本质是:程序执行过程中发生了算数异常这个事件(因为被除数不能为0),JVM为我们创建了一个ArithmeticException类型的对象。并且这个对象中包含了详细的异常信息,并且JVM将这个对象中的信息输出到控制台
  • 由于出现算数异常,又没有对其进行处理,所以Hello World不会输出
  • 如果不懂上面异常的本质,可以先学习理解一下类与对象的创建与使用]

3.异常机制的作用?

  • java语言为我们提供一种完善的异常处理机制
  • 程序发生异常事件之后,为我们输出详细的信息,程序员通过这个信息,可以对程序进行一些处理,使程序更加健壮

二、异常的层次结构

在这里插入图片描述

  • 上诉异常处理机制图示结构:
Object
    Throwable 
       Exception   
            编译时异常
            运行时异常
       Error
  • Exception
    • 编译时异常:
      • 所有Exception的直接子类都是“编译时异常”
      • 该异常是可预期,很有可能发生的,发生的机率比较高
      • 所有的编译时异常,要求程序员在编写程序阶段,必须对它进行处理,如果不处理的话,编译无法通过。处理异常有两种方式:捕捉和声明抛出。捕捉:try…catch…,声明抛出就是在方法声明的位置上使用throws关键字抛出异常
    • 运行时异常:
      • 所有RuntimeException的子类都是运行时异常
      • 该类异常不一定发生,发生的机率比较低,如果代码没有出现逻辑错误,是不会发生
      • 编译器不要求去处理,程序员在写代码时可以不处理
  • Error
    • java程序运行过程中如果出现了错误,属于JVM和系统发生的错误,程序员无法解决的问题。例如:StackOverFlowError (一旦线程栈的大小增长超过了允许的内存限制,就会抛出 java.lang.StackOverflowError 错误)

二、处理异常的两种方式

  • 使用throws 抛出异常
  • 使用try…catch处理异常

1.使用throws 抛出异常

1.1 首先看一下要求异常处理的代码

package 异常机制;

import java.io.FileInputStream;

public class Test02 {

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

    public static void m1() {
        m2();
    }

    private static void m2() {
        m3();
    }

    private static void m3() {
        new FileInputStream("D:/x.txt");
    }
}

  • 以上程序会发现FileInputStream在编译的时候会报红色波浪线

  • 看源码发现FileInputStream这个构造方法在声明的位置上使用了throws FileNotFoundException(向上抛)
    FileInputStream源码

  • FileNotFoundException继承Exception类,所以属于编译时异常,要求程序员必须对其进行异常处理(throws抛出或者try…caych)
    层次结构图

1.2 对上述代码使用throws抛出异常

在方法声明的位置上使用throws FileNotFoundException就可以编译通过了,或者写IOException,或者写Exception,写比FileNotFoundException更宽泛的异常就可以

package 异常机制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionTest02 {
    public static void main(String[] args) throws FileNotFoundException{
        m1();
        //使用throws处理异常不是真正处理异常而是推卸责任
        //谁调用的就会抛给谁
        //上面的m1方法如果出现了异常,因为采用的是上抛,给了JVM,JVM遇到这个异常就会退出JVM,下面的这个代码不会执行
        System.out.println("Hello World!");
    }

    public static void m1() throws FileNotFoundException{
        m2();
    }

    private static void m2() throws FileNotFoundException{
        m3();
    }

    private static void m3() throws FileNotFoundException {
        new FileInputStream("D:/x.txt");
    }
}

  • 上诉程序可以看出,从调用m3方法开始一直向上抛异常,直到m1方法出现了异常,并且采用的是上抛,再抛给main方法,最终抛给了JVM(java虚拟机),JVM遇到这个异常就会退出JVM,下面的"Hello World!"字符串字样就不会打印输出
  • 上述程序编译没有问题,打印输出信息如下
    编译信息如图
  • 可以看出在程序运行过程中发生了FileNotFoundException类型的异常
  • JVM为我们创建了一个FileNotFoundException类型的对象
  • 该对象中携带以下的信息
  • JVM负责将该对象的信息打印到控制台
  • 并且JVM停掉了程序的运行

如果对m1方法进行try…catch进行捕捉

try...catch捕捉

  • 打印输出:
    打印输出

  • 可以看出,如果对m1方法进行try…catch处理,捕捉到了这个异常并且处理了这个异常,那么"Hello World!"就会打印输出

1.3 总结

  • 使用throws处理异常不是真正处理异常而是推卸责任,如果想要处理该异常,使用try…catch进行捕捉,就不会抛给JVM而使程序异常退出

2.使用try…catch处理异常

2.1 语法

try{
    可能出现异常的代码;
}catch(异常类型1  变量){
    处理异常的代码;
}catch(异常类型2  变量){
    处理异常的代码;
}...
  • catch语句块可以写多个
  • 但是从上到下执行catch,必须从小类型异常到大类型异常进行捕捉
  • try…catch…中最多执行1个catch语句块。执行结束之后try…catch…就结束了

2.2 示例代码

  • read方法:尝试将字符读入指定的字符缓冲区,源码中看到在read构造方法中看到在声明的位置上使用了throws IOException
    read源码
package 异常机制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest03 {
    public static void main(String[] args) {
        try {
            //程序执行到此处发生了FileNotFoundException类型的异常
            //JVM会自动创建一个FileNotFoundException类型的对象,将该对象的内存地址赋值给catch语句块中的e变量
            FileInputStream fis = new FileInputStream("D:/a.txt");
            //上面的代码出现了异常,try语句块的代码不再继续执行,直接进入catch语句块中执行
            System.out.println("gggggggggggggggg");
            fis.read();
        } catch (FileNotFoundException e) { //e内存地址指向堆中的那个对象是“FileNotFoundException类型的”事件
            System.out.println("读取的文件不存在");
            //FileNotFoundException将Object中的toString方法重写
            System.out.println(e);  //java.io.FileNotFoundException: D:a.txt (系统找不到指定的文件。)
        } catch (IOException e) {
            System.out.println("其他IO异常");
        }
        System.out.println("aaa");
    }
}

  • 程序执行到读取特定文件的时候,发生了FileNotFoundException类型的异常
  • JVM会自动创建一个FileNotFoundException类型的对象,将该对象的内存地址赋值给catch语句块中的e变量
  • e内存地址指向堆中的那个对象是“FileNotFoundException类型的”事件
  • FileNotFoundException将Object中的toString方法重写
  • 控制台输出信息:
读取的文件不存在
java.io.FileNotFoundException: D:a.txt (系统找不到指定的文件。)
aaa

2.3 总结

  • try{} 不能单独存在,后面必须跟上 catch 或者 finally , catch可以有多个

三、关于printStackTrace和getMessage方法的应用

  • 如何取得异常对象的具体信息,常用的方法主要有两种
    • 取得异常的堆栈信息(比较适合于程序调式阶段),printStackTrace()
    • 取得异常描述信息:getMessage()

1.printStackTrace方法

package 异常机制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExceptionTest04 {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("D:a.txt");
        } catch (FileNotFoundException e) {
            //打印异常堆栈信息
            //一般情况下都会使用该方式去调式程序
            e.printStackTrace();

//            String message = e.getMessage();
//            System.out.println(message);    //D:a.txt (系统找不到指定的文件。)
        }
        System.out.println("abc");

    }
}

  • 控制台输出
java.io.FileNotFoundException: D:a.txt (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at 异常机制.ExceptionTest04.main(ExceptionTest04.java:9)
abc

2.getMessage方法

  • 将上面13行代码注释掉,15、16行代码打开,运行代码
  • 控制台输出
D:a.txt (系统找不到指定的文件。)
abc

3.总结

  • 从上述两个打印信息看出,printStackTrace打印异常信息比较详细,getMessage打印的异常信息较为简洁,printStackTrace打印的信息包含了getMessage打印出来的信息,所以一般使用printStackTrace来打印异常信息

四、finally语句块

  • finally语句块中可以直接和try语句块联用 try…finally…
  • try…catch…finally也可以
  • 在finally语句块中的代码是一定会执行的
  • 只要在执行finally语句块之前退出了JVM,则finally语句块就不会执行
  • 通常在程序中为了保证某资源一定会释放,所以一般在finally语句块中释放资源
package 异常机制;

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

        int i = m1();
        System.out.println(i);  //10
    }

    public static int m1(){
        int i = 10;
        try{
            return i;
        }finally {
            i++;
            System.out.println("m1的i="+i);  //m1的i=11
        }
    }
}

  • 输出结果
m1的i=11
10
  • return i;分开来可以写成int temp = i; return temp;,操作的是int i = 10;
  • finally语句块中的i++;操作的是int i = 10;
package 异常机制;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest07 {
    public static void main(String[] args) {
        //必须在外边声明
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("D:a.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if (fis!=null){
                try{
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

  • 流是个资源,为了保证资源一定会释放,用完我们要关闭
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章