异常类Throwable

发布 | 2024-08-08 | JAVA




Throwable结构

  • Error:严重错误Error,无法人为处理的错误。

    • 例如: StackOverflowErrorOutOfMemoryError
  • Exception:表示异常,其它因编程错误或偶然的外在因素导致的一般性问题。

    • 例如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界

Throwable中的常用方法:

  • public void printStackTrace():打印异常的详细信息。

    包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

  • public String getMessage():获取发生异常的原因。

    提示给用户的时候,就提示错误原因。

出现异常,不要紧张,把异常的简单类名,拷贝到API中去查。

异常分类

由于Error情况发生是我们无法处理的,一般因为是内存不足或程序存在严重逻辑问题,只能通过扩大内存或重新修改代码解决。

平常所遇到的大多数是Exception类型异常,通常分两大类:

非受查异常
运行期异常(unchecked Exception):这类异常的发生多数是因为程序员编写的代码逻辑不够严谨造成的(如数组脚标越界异常),可以选择进行处理或不处理,最好是通过修正、优化代码避免异常的发生(或者使用异常处理简化复杂的逻辑判断代码)。
受查异常
编译期异常(checked Exception):这类异常一般由程序之外的因素引起的(如程序读取的文件不存在、网络中断),而不是程序员写的代码逻辑有问题,所以程序员容易忽略对这类异常的处理,而恰恰这类异常又很常发生,所以Java要求针对这类可能发生的异常必须进行处理,否则编译无法通过。(只有java语言有需强制处理的异常)

常见的错误和异常演示示例

  1. VirtualMachineError

    最常见的就是:

    StackOverflowError:虚拟机栈内存不足,无法分配栈帧所需空间。

    OutOfMemoryError:没有足够的内存空间可以分配。

        @Test
        public void test01(){
            //StackOverflowError
            digui();
        }
        
        public void digui(){
            digui();
        }
        @Test
        public void test02(){
            //OutOfMemoryError
            //方式一:
            int[] arr = new int[Integer.MAX_VALUE];
        }
        @Test
        public void test03(){
            //OutOfMemoryError
            //方式二:
            StringBuilder s = new StringBuilder();
            while(true){
                s.append("atguigu");
            }
        }
  2. 运行时异常

        @Test
        public void test01(){
            //NullPointerException
            int[] arr=null;
            System.out.println(arr.length);
        }
        
        @Test
        public void test02(){
            //ClassCastException
            Person p = new Man();
            Woman w = (Woman) p;
        }
        
        @Test
        public void test03(){
            //ArrayIndexOutOfBoundsException
            int[] arr = new int[5];
            for (int i = 1; i <= 5; i++) {
                System.out.println(arr[i]);
            }
        }
        
        @Test
        public void test04(){
            //InputMismatchException
            Scanner input = new Scanner(System.in);
            System.out.print("请输入一个整数:");
            int num = input.nextInt();
        }
        
        @Test
        public void test05(){
            int a = 1;
            int b = 0;
            //ArithmeticException
            System.out.println(a/b);
        }
  3. 编译时异常

        @Test
        public void test06() throws InterruptedException{
            Thread.sleep(1000);//休眠1秒
        }
        
        @Test
        public void test07() throws FileNotFoundException{
            FileInputStream fis = new FileInputStream("Java学习秘籍.txt");
        }
        
        @Test
        public void test08() throws SQLException{
            Connection conn = DriverManager.getConnection("....");
        }

9.4 异常的生成与抛出机制throw

执行过程中如出现异常,会生成一个异常类对象,然后该异常对象会被提交给Java运行时系统,这个过程称为抛出(throw)异常。

异常对象的生成与抛出有两种方式:

  1. 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,就会在后台自动创建一个对应异常类的实例对象并自动抛出

    我们通过示例分析下一次产生的过程:

    运行以下程序会产生一个数组索引越界异常ArrayIndexOfBoundsException

    // 工具类
    public class ArrayTools {
        // 对给定的数组通过给定的角标获取元素。
        public static int getElement(int[] arr, int index) {
            int element = arr[index];
            return element;
        }
    }
    // 测试类
    public class ExceptionDemo {
        public static void main(String[] args) {
            int[] arr = { 34, 5, 67 };
            intnum = ArrayTools.getElement(arr, 4)
            System.out.println("num=" + num);
            System.out.println("over");
        }
    }

    由此看出,异常对象被JVM创建后,在产生异常的方法中会自动抛出,抛给方法的调用者,抛给main方法,最后抛给虚拟机,虚拟机打印异常信息后终止程序。

  1. 由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,手动创建的异常对象需要手动抛出,才会对程序产生影响。

    在Java中,使用关throw关键字手动抛出一个异常对象,throw用在方法内,将这个异常对象传递到方法调用者处,同时结束当前方法的执行。

    使用格式:

    throw new 异常类名(参数);

    例如:

    throw new NullPointerException("要访问的arr数组不存在");
    
    throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");

    throw的使用示例:

    public class ThrowDemo {
        public static void main(String[] args) {
            //创建一个数组 
            int[] arr = {2,4,52,2};
            //根据索引找对应的元素 
            int index = 4;
            int element = getElement(arr, index);
    
            System.out.println(element);
            System.out.println("over");
        }
        /*
         * 根据 索引找到数组中对应的元素
         */
        public static int getElement(int[] arr,int index){ 
            if(arr == null){
                /*
                 判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
                 这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 
                  */
                throw new NullPointerException("要访问的arr数组不存在");
            }
               //判断  索引是否越界
            if(index<0 || index>arr.length-1){
                 /*
                 判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
                 这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 
                  */
                 throw new ArrayIndexOutOfBoundsException("哥们,角标越界了~~~");
            }
            int element = arr[index];
            return element;
        }
    }

    注意:如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。

    那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续将问题声明出去,使用throws声明处理。

异常的处理机制

如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常。

捕获异常try…catch

  • 捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。

    捕获异常语法如下:

    try{
         编写可能会出现异常的代码
    }catch(异常类型1  e){
         处理异常的代码
         //记录日志/打印异常信息/继续抛出异常
    }catch(异常类型2  e){
         处理异常的代码
         //记录日志/打印异常信息/继续抛出异常
    }
    ....

    try:捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。建议:此范围尽量小。

    catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常。

    • 可以有多个catch块,按顺序匹配。
    • 如果多个异常类型有包含关系,那么小上大下

    演示示例:

    public class TestException {
        public static void main(String[] args)  {
            try {
                readFile("不敲代码学会Java秘籍.txt");
            } catch (FileNotFoundException e) {
    //            e.printStackTrace();
    //            System.out.println("好好敲代码,不要老是想获得什么秘籍");
                System.out.println(e.getMessage());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } 
            
            System.out.println("继续学习吧...");
        }
        
        // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
        public static void readFile(String filePath) throws FileNotFoundException, IllegalAccessException{
            File file = new File(filePath);
            if(!file.exists()){
                throw new FileNotFoundException(filePath+"文件不存在");
            }
            if(!file.isFile()){
                throw new IllegalAccessException(filePath + "不是文件,无法直接读取");
            }
            //...
        }
        
    }
  • 获取异常信息:

    捕获到了异常对象,就可以获取异常对象中封装的异常信息,Throwable类中定义了一些方法用于获取异常对象中的信息:

    • public String getMessage():获取异常的描述信息。
    • public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。这些信息包含了异常的类型,异常信息,还包括异常出现的位置,在开发和调试阶段,建议使用printStackTrace。

finally块

finally:在finally代码块中存放的代码都是一定会被执行的。由于异常会引发程序跳转,导致后面有些语句执行不到,如果一定要执行这些语句就可以使用finally,finally常用于释放系统资源。

比如:当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),无论异常有没有发生,我们都要在使用完之后关闭已打开的资源,避免系统资源的浪费。

finally的语法:

 try{
     
 }catch(...){
     
 }finally{
     无论try中是否发生异常,也无论catch是否捕获异常,也不管try和catch中是否有return语句,都一定会执行
 }
 
 或
  try{
     
 }finally{
     无论try中是否发生异常,也不管try中是否有return语句,都一定会执行。
 } 

注意:finally不能单独使用。

当只有在try或者catch中调用退出JVM的相关方法,例如System.exit(0),此时finally才不会执行,否则finally永远会执行。

finally代码IO流读取文件示例如下:(暂)

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

public class TestException {
    public static void main(String[] args)  {
        readFile("不敲代码学会Java秘籍.txt");
        System.out.println("继续学习吧...");
    }
    
    // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
    public static void readFile(String filePath) {
        File file = new File(filePath);
        FileInputStream fis = null;
        try {
            
            if(!file.exists()){
                throw new FileNotFoundException(filePath+"文件不存在");
            }
            if(!file.isFile()){
                throw new IllegalAccessException(filePath + "不是文件,无法直接读取");
            }
            fis = new FileInputStream(file);
            //...
        } catch (Exception e) {
            //抓取到的是编译期异常  抛出去的是运行期 
            throw new RuntimeException(e);
        }finally{
            System.out.println("无论如何,这里的代码一定会被执行");
            try {
                if(fis!=null){
                    fis.close();
                }
            } catch (IOException e) {
                //抓取到的是编译期异常  抛出去的是运行期 
                throw new RuntimeException(e);
            }
        }
        
    }
}

声明异常throws

throws:用在方法上,表明此方法可能会产生的异常类型。

如果在某方法内通过抛出了必须要处理的编译期异常,有两种选择:要么在当前方法进行捕获处理,要么通过throws在当前方法上进行声明,让方法的调用者去处理。

声明异常格式:

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }    

声明异常的代码演示:

import java.io.File;
import java.io.FileNotFoundException;

public class TestException {
    public static void main(String[] args) throws FileNotFoundException {
        readFile("不敲代码学会Java秘籍.txt");
    }
    
    // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
    public static void readFile(String filePath) throws FileNotFoundException{
        File file = new File(filePath);
        if(!file.exists()){
            throw new FileNotFoundException(filePath+"文件不存在");
        }
    }
    
}

throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。

import java.io.File;
import java.io.FileNotFoundException;

public class TestException {
    public static void main(String[] args) throws FileNotFoundException,IllegalAccessException {
        readFile("不敲代码学会Java秘籍.txt");
    }
    
    // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
    public static void readFile(String filePath) throws FileNotFoundException,IllegalAccessException{
        File file = new File(filePath);
        if(!file.exists()){
            throw new FileNotFoundException(filePath+"文件不存在");
        }
        if(!file.isFile()){
            throw new IllegalAccessException(filePath + "不是文件,无法直接读取");
        }
        //...
    }
    
}


附录


歌名:《死了都要try》

死了都要try

不抓住异常不痛快

BUG多深,只有这样,才不用重来

死了都要try

不catch够我不痛快

程序毁灭throw还在

把每天,当作程序来更改

一改一天,都累到泪水掉下来

不理会,老板是看好或看坏

只要有工资,来还贷

改,不是需求做的太坏

那是客户想要什么,自己都不明白

忍受现在,自己一生都还不完的贷

很多模块,不能完成,我还得改

死了都要try

不catch够我不痛快

程序毁灭throw还在




© 著作权归作者所有

本文由 趣代码Blog 创作,采用 知识共享署名4.0 国际许可协议进行许可,本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。

评论关闭