java异常与处理

1. Java的Error与Exception

1.1 总体架构

在这里插入图片描述

1.2 错误与异常的区别

Error:

  • Error是指在正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如jvm自身)处于非正常的不可恢复的状态。如OutOfMemoryErrorStackOverFlowError等。
  • Error类及其子类的实例,代表了JVM本身的错误,但是这些错误不能被程序员通过代码处理,除了退出运行外别无选择。
  • 如何可能的话,Error应该在系统级被捕捉,所以程序员应该关注Exception为父类的分支下的各种异常类。

Exception:

  • Exception以及他的子类,代表程序运行时发生的各种不期望发生的事件,是程序本身可以处理的异常
  • Exception可以被Java异常处理机制处理,是异常处理的核心。
  • Exception表示程序需要捕捉、需要处理的异常,是由于程序设计的不完善而出现的问题,程序必须处理的问题。
  • 异常分为检查型(checked)非检查型(unchecked)
    • 表示一个由程序员导致的错误,是否应该在应用程序级别被处理
    • checked异常强制要求程序员为这样的异常做预备处理工作,也就是必须进行try-catch处理,或通过throws语句进行抛出

总结: 异常和错误的区别是,异常是可以被处理的,而错误是没法处理的

unckecked exception:

  • 非检查型异常(unckecked exception): Error 和 RuntimeException以及他们的子类,是运行时发生的,无法预先捕捉处理的

    javac在编译时,不会提示和发现这样的异常,不要求程序处理这些异常。。

  • 如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理

  • 对于这些异常,我们应该修正代码,而不是去通过异常处理器处理。

  • 这样的异常发生的原因多半是代码逻辑有问题,如除0错误ArithmeticException,强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。

checked exception:

  • 检查型异常(ckecked exception): 除了ErrorRuntimeException的其它异常,一般是由程序的运行环境导致的

  • 因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如IOExceptionFileNotFoundExceptionEOFException、一些自定义异常等。

    javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws/throw)。

  • 在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明以抛出它,否则编译不会通过。

总结:

  1. 异常可以分为检查型异常和非检查型异常,或者分为:运行时异常和非运行时异常,Exception是所有检查异常的父类;
  2. 检查和非检查是对于javac来说的:
    检查型异常必须使用try, catch关键字进行显式的捕获处理,否则编译器会报错。
    非检查型异常通常是可以避免的代码逻辑错误,具体可以根据需要来判断是否需要捕获,并不会在编译期强制要求。
    在这里插入图片描述

1.3 错误与异常的联系

  • exceptionerror都是继承了throwable类,在java中只有throwable类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
  • exceptionerror体现了java平台设计者对不同异常情况的分类:
    (1)exception是程序运行中,可以预料的意外情况,并且应该被捕获并进行相应的处理。
    (2) error是指在正常情况下,不大可能出现的情况,绝大部分的error都会导致程序(比如jvm自身)处于非正常的、不可恢复的状态。既然是非正常情况,所以不便于、也不需要捕获

2. Java的Exception处理

2.1 异常处理的几个关键字

Java异常机制用到的几个关键字:trycatchfinallythrowthrows

  • try: try用来包裹一段可能会发生任何异常的代码,当try语句块内发生异常时,异常就被抛出。
  • catch: catch子句紧跟在try块后面,用来指定想要捕获的异常类型。
    (1)在代码中使用try-catch块进行异常处理:try是块的开始,catch是在try块的末尾处理异常。
    (2)可以一个try有多个catch块,try-catch块也可以嵌套
  • finally: finally语句块总是会被执行
    (1)可以确保一段代码不管发生什么异常状况都要被执行
    (2)主要用于回收在try块里打开的资源(如数据库连接网络连接磁盘文件)。
  • throw : throw语句用来明确地抛出一个异常。
  • throws: throws用在方法签名中,用于声明该方法可能抛出的异常。

2.2 异常处理两种方法

  • 通过try...catch...finally语句块来处理,其中finally语句块可选

    try{try块中放可能发生异常的代码。
         ② 如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。
         ③ 如果发生异常,则尝试去匹配catch块。
     
    }catch(SQLException SQLexception){
        ① 每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。
        ② catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
        ③ 如果try中没有发生异常,则所有的catch块将被忽略。
     
    }catch(Exception exception){
        //...
    }finally{finally块通常是可选的。
        ② 无论异常是否发生,异常是否被匹配处理,finally都会执行。
        ③ 一个try至少要有一个catch块,否则, 至少要有1finally块。但是finally不是用来处理异常的,finally不会捕获异常。
        ④ finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 
    }
    
  • 在具体位置不处理,而是通过throws/throw抛出到上层再进行处理。

    public static void test() throws MyException{
        try {
            int i = 10 / 0;
            System.out.println("i=" + i); 
        } catch (ArithmeticException e) {
            throw new MyException("This is MyException"); 
        }
    }
    

2.3 throw和throws关键字

二者的区别:

  • 写法上 : throw 在方法体内使用,throws 函数名后或者参数列表后、方法体前使用。
  • 意义 : throw 强调动作,而throws 表示一种倾向、可能但不一定实际发生
  • 作用的对象: throws 后面跟的是异常类,可以一个或多个,多个用逗号隔开。throw 后跟的是异常对象,或者异常对象的引用,只能有一个

使用的注意事项:

  • 如果你在方法中抛出了一个异常对象(throw),就必须在方法上声明一个异常的抛出(throws)。

    注意: throw的是检查型异常,必须通过throws声明;非检查型异常,无须throws声明

  • 如果一个方法调用了抛出异常的方法,那么调用者就必须处理抛出的这个异常(try…catch)。
  • 如果一个方法中抛出了异常,那么throw后面的代码不会再被执行。

    finally语句中的代码仍然会执行

  • 在一种情况下只能抛出一个异常,如果是多个异常的话,就取离它最近的那个异常。 —— 不是很理解 😂

2.4 如何获取异常信息

常用的异常信息获取方法:

  • String getMessage(): 用于获取异常的详细消息字符串。

  • void printStackTrace(): 输出Throwable对象的堆栈跟踪信息到控制台。

  • String getLocalizedMessage(): 提供此方法,以便子类可以覆盖它以向调用程序提供特定于语言环境的消息。 —— 从未使用过,其内部实际调用了getMessage()

  • String toString(): 方法返回String格式的Throwable信息,此信息包括Throwable的名字本地化信息( getLocalizedMessage()获取的信息)。

    public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
    }
    

2.5 用户自定义异常

  • 自定义异常类,需要继承Throwable类Exception类

  • 编写自己的异常类时需要记住下面的几点:
    ① 所有异常都必须是 Throwable 的子类。
    ② 如果希望写一个非检查型异常类,则需要继承 Exception 类。
    ③ 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

    /*
     * 自定义的异常类,格式如下,通过super关键字,将错误信息传递给Exception的构造函数中,之后再调用
     * toString()方法就可以打出自己想写的异常信息了
     */
    class MyException extends Exception
    {
        MyException(String ErrorMessage)
        {
            super(ErrorMessage);
        }
    }
    /*
     * 自定义异常:java自己的异常可以手动抛出也可以自动抛出,而自己定义的异常java虚拟机不认识
     * 所以,我们要通过throw关键字自己抛出异常,抛出异常之后我们有两种处理方式,第一种是throws声明抛出
     * 第二种是在下面直接try catch进行处理
     */
    public class Myyichang 
    {
        public static void main(String[] args) 
        {
           try
            {
                int c = chu(2, -1);
                System.out.println(c);
            }
            catch(MyException e)
            {
                System.out.println(e.toString());
            }
        }
        
        static int chu(int a,int b) throws MyException 
        {
            if(b <= 0) {
                throw new MyException("出现负数或者零了");
            }
            return a / b;
        }
    }
    

3. 热门面试问题

3.1 final、finally、finalize的区别

  • final用于声明变量、方法和类的,分别表示变量值不可变、方法不可重写、类不可以继承
  • finally异常处理中的一个关键字,finally{}语句块中的代码一定会被执行
  • finalize是Object类的一个方法,在垃圾回收的时候会调用被回收对象的此方法。

3.2 包含异常时,代码的执行逻辑

  • 有如下代码,try和finally语句块中均有return语句,finally中的return语句是否会执行,什么时候执行?
    String s = "hello";
    try {
        File file = new File("test.txt");
        return 0;
    } catch(Exception e) {
        e.printStackTrace();
    } finally {
        return 1;
    }
    

答案: 会执行,在方法返回调用者前执行,最后返回的是1。

特殊情况: 若在try里面有System.exit(0)退出JVM的情况下,finally块中的代码不会执行。

3.3 JDK 7中异常的新特性

JDK7中的两个新特性

  1. 一是,一个catch块中可以捕获多个异常,就像原来用多个catch块一样。
  2. 二是,自动化资源管理(ARM)也称为try-with-resource块
  3. 新特性的作用:都可以在处理异常时减少代码量,同时提高代码的可读性。

multi-catch块

  • 允许程序员在一个catch块中捕获多个异常

    catch(IOException | SQLException | NullPointerException ex){
    	logger.error(ex);
    	throw new MyException(ex.getMessage());
    }
    

try-with-resource块

  • 大多数情况下,我们使用finally块来关闭资源,有时我们忘记关闭它们并在资源耗尽时获得运行时异常。

  • 这些异常很难调试,我们可能需要查看我们使用该类资源的每个地方,以确保我们关闭它。

  • try-with-resources,允许程序员在try语句中创建一个资源并在try-catch块中使用它。

  • 当执行完try-catch块时,运行时环境会自动关闭这些资源。

  • 注意: 要求该资源实现了java.lang.AutoCloseable或其子类java.io.Closeable

  • 代码示例:

    try (MyResource mr = new MyResource()) {
    	System.out.println("MyResource created in try-with-resources");
    } catch (Exception e) {
    	e.printStackTrace();
    }
    

3.4 异常链

  • 常常会在捕获一个异常后抛出另外一个异常,并且希望把原始的异常信息保存下来,这被称为异常链

  • 异常链主要作用就是:保存异常信息,在抛出另外一个异常的同时不丢失原来的异常。

    static void g() throws ExceptionC {
       try {
           f();
       } catch (ExceptionB e) {
           ExceptionC c = new ExceptionC("exception a");
           //异常链,使用initCause()来设置cause
           c.initCause(e);
           //另外一种实现方式
           // throw new Exception("exception a", e);   
           throw c;
       }
    }
    
    public static void main(String[] args) {
           try {
               g();
           } catch (ExceptionC e) {
               e.printStackTrace();
           }
    }
    

3.5 你平时在项目中是怎样对异常进行处理的?

  • 尽量避免出现runtimeException:

    (1)对于可能出现空指针的代码,在使用对象之前一定要判断一下该对象是否为空
    (2)必要的时候对runtimeException也进行try-catch处理。

  • 异常处理要优雅: 进行try-catch处理的时候要在catch代码块中对异常信息进行记录,通过调用异常类的相关方法获取到异常的相关信息

    不仅要给用户良好的用户体验,也要能帮助程序员有效定位异常出现的位置及原因。

  • 使用特定异常以便于调试,比如如IOException,最好可以具体到时FileNotFoundException,还是EOFException
  • 在程序中尽早抛出异常(Fail-Fast);在程序后期捕获异常,让调用者处理异常
  • 资源回收: 使用Java 7 ARM功能确保资源已关闭或使用finally块正确关闭它们。
  • 使用自定义异常从应用程序API中抛出单一类型的异常。 —— 不是很理解 😂
  • 异常的定义要遵循命名约定,始终以Exception结束。

参考链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值