6 异常处理

Head First Java 和 AcWing Java课程做的总结6。

当在编写可能有异常的方法时,需要处理异常状况的程序。

执行期的大多数问题来自程序的错误,而这些错误应该在开发阶段解决掉,但是某些错误还是有可能在执行期出现,例如找不到文件、服务器出现故障。

6.1 ErrorException的区别

Error是程序无法处理的错误,比如OutOfMemoryErrorThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。此类异常是程序的致命异常,是无法捕获处理的。

Exception是程序本身可以处理的异常,这种异常分两大类运行时异常非运行时异常。 程序中应当尽可能去处理这些异常。

6.2 异常处理(exception-handing)机制

  • 是个简捷、轻量化的执行期间例外状况处理方式,它能够将处理错误状况的程序代码摆在一个容易阅读的位置。

  • 需要知道所调用的方式是由风险的,也就是方法可能产生异常。看该方法的声明是否有throws语句就知道了。

  • try-catch

    • 把有风险的程序代码包含在try-catch块中,编译器会知道所调用的方法有风险,并且也已经准备好处理它。

    • try{
          //把有风险的程序放在try中
      }catch(Exception ex){
          //处理异常的程序
      }
      
  • try-catch-finally

    • finally块是用来存放不管有没有异常都得执行的程序。

    • 如果trycatch块有return指令,finally还是会执行。

    • try{
          //把有风险的程序放在try中
      }catch(Exception ex){
          //处理异常的程序
      }finally{
          //都得做的事
      }
      
  • duck异常

    • 如果调用服务程序b不想处理异常,可以把它duck掉来避开。让调用程序b程序acatch该异常。没有用try-catch处理有风险的方法,则调用方b也成有风险的了。
    • 只需要让程序b声明成再throws此异常即可。
    • 方法抛出异常时,方法会从栈上立即被取出,而异常会再度丢给栈上的方法,也就是调用方。
    • duck只是在踢皮球,早晚得有人来处理这件事,若main()也duck掉异常,则Java虚拟机直接死机。
  • 异常处理规则:

    • catchfinally不能没有try;
    • try一定要有catchfinally,即不能只有try
    • trycatch之间不能有程序;
    • 只带有finallytry必须要声明异常。

6.3 异常对象

异常是一种Exception类型的对象。

  • throw: 在函数内抛出一个异常。
  • throws:在函数定义时抛出一些可能的异常。

程序代码会抓住异常,而抛出异常的是——声明有异常的方法,也就是该方法把异常抛出来。

方法可以抓住其他方法所抛出的异常。异常总是会丢给调用方。

举例:

//有风险、会抛出异常的程序代码
public void takeRisk() throws BadException{
    if(abandonAllHope){
        throw new BadException();
    }
}

//调用该方法的程序代码
public void crossFinger(){
    try{
        anObject.takeRisk();
    }catch(BadException ex){
        System.out.println("Aaargh");
        ex.printStackTrace();
    }
}

内置异常方法:

方法 说明
public String getMessage() 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了
public Throwable getCause() 返回一个 Throwable 对象代表异常原因。
public String toString() 返回此 Throwable 的简短描述。
public void printStackTrace() 将此 Throwable 及其回溯打印到标准错误流
public StackTraceElement [] getStackTrace() 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
public Throwable fillInStackTrace() 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。

6.4 异常分类

异常继承树:

不检查异常:

  • 上图灰色,都继承自java.lang.RuntimeException
  • RuntimeException被称为不检查异常,可以自己抛出来并抓住它们,但是没有这个必要,编译器也不管。
  • 大部分的RuntimeException都是因为程序逻辑的问题,而不是以无法预测或防止的方式出现的执行期失败状况。不检查异常一般是程序代码写的不够严谨而导致的问题,可以通过修改代码来规避。
  • try-catch是用来处理真正的异常。而不是程序的逻辑错误,该快要做的事恢复的尝试,或者至少列出错误信息。
  • 任何继承过RuntimeException的类都不会受编译器是否声明它会抛RuntimeException的检查,同样的,也不会关调用方是否认识到可能会在运行期间遇到异常。
  • 常见的运行时异常:空指针异常(NullPointerException)、除零异常(ArithmeticException)、数组越界异常(ArrayIndexOutOfBoundsException)等;

检查异常:

  • 上图白色,都继承自·java.lang.Exception
  • 所有不是RuntimeException派生的Exception都是检查型异常。
  • 编译器所关心得异常,程序必须要意识到这种异常可能的存在。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。
  • 检查性异常必须使用try catch或者throws等关键字进行处理,否则编译器会报错;
  • 常见的检查异常:输入输出异常(IOException)、文件不存在异常(FileNotFoundException)、SQL语句异常(SQLException)等。

总的来说:

  • 检查性异常: 不处理编译不能通过;

    非检查性异常:不处理编译可以通过,如果有抛出直接抛到控制台。

  • 运行时异常: 就是非检查性异常

    非运行时异常: 就是检查性异常

6.5 内置异常类

非检查性异常:

异常 描述
ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数”除以零”时,抛出此类的一个实例。
ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常。
ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
IllegalMonitorStateException 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
IllegalStateException 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IllegalThreadStateException 线程没有处于请求操作所要求的适当状态时抛出的异常。
IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NegativeArraySizeException 如果应用程序试图创建大小为负的数组,则抛出该异常。
NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常。
NumberFormatException 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
SecurityException 由安全管理器抛出的异常,指示存在安全侵犯。
StringIndexOutOfBoundsException 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
UnsupportedOperationException 当不支持请求的操作时,抛出该异常。

检查性异常:

异常 描述
ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常。
CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
IllegalAccessException 拒绝访问一个类的时候,抛出该异常。
InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
InterruptedException 一个线程被另一个线程中断,抛出该异常。
NoSuchFieldException 请求的变量不存在
NoSuchMethodException 请求的方法不存在

6.6 多重异常&异常多态

方法可以抛出多个异常,但该方法的声明必须要有含有全部可能的检查异常(若存在共同父类,则声明父类即可)。

处理多重异常:

  • 要注意catch出现的先后顺序;

  • public class Laundry{
        public void doLaundry() throws PantsException, LingerieException{
            
        }
    }
    
    public class Foo{
        public void go(){
            Laundry laundry = new Laundry();
            try{
                laundry.doLaundry();
            }catch(PantsException pex){
                
            }catch(LingerieException lex){
                
            }
        }
    }
    
  • 有多个catch块时,要从小排到大。即异常继承树中越底层,越在catch中先出现。大的放在上面会导致无法通过编译。

异常的多态性:

  • 异常也是对象,所以也能够以多态的方式来引用;

  • 这样不必明确的声明每个可能抛出的异常,可以只声明父类即可。

  • //以异常的父型来声明会抛出的异常
    public void doLaundry() throws ClothingException{
        //声明父类异常可让抛出任何子类异常
    }
    
    //以抛出异常父型来catch异常
    ry{
        laundry.doLaundry();
    }catch(ClothingException cex){
    
    }
    
  • 但是能够用父型来这么做,不代表就一个这么做

    • 全部异常都可以用catch(Exception ex)这样处理,但是这样完全搞不清哪里出错。

6.7 try-with-resources

JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。
try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。

import java.io.*;

public class Main {

    public static void main(String[] args) {
        String line;
        try (
                BufferedReader br = new BufferedReader(new FileReader("input.txt"));
                BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
        ) {
            while ((line = br.readLine()) != null) {
                System.out.println("Line => " + line);
                bw.write("copy: " + line + "\n");
            }
            bw.flush();
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        }
    }
}

posted @   杨大康  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示