【java异常Exception】

本文详细介绍了Java中的异常处理机制,包括编译时异常和运行时异常的区别,异常的处理方式如throws和try-catch,异常处理的选择,常用异常方法的使用,finally块的作用,自定义异常的创建,以及异常与方法覆盖的规则。内容涵盖了异常的生命周期,资源释放,以及如何优雅地处理异常。
摘要由CSDN通过智能技术生成

异常

概述

  • 所有的 Exception 都是在运行时发生,因为异常的产生是需要new 对象的;
  • 不建议在main方法上使用throws,如果该异常真正发生就会抛给JVM,JVM会终止线程。

1. 编译时异常&&运行时异常

编译时异常

  • 编译时异常是指 在编写程序的时候,需要预先对这种异常进行处理,不处理的话编译器报错;
  • 编译时异常一般发生的概率较高;
  • 编译时异常又称为受检异常(受控异常 Checked Exception)。

运行时异常

  • RuntimeException 及子类均属于运行时异常,在编写程序阶段可以选择处理也可以选择不处理;

  • 运行时异常一般发生的概率比较低;

  • 运行时异常又被称为非受检异常(非受控异常 UnChecked Exception))。

types-of-exceptions-in-java

2. 异常的处理

2.1 throws

  • 在方法声明的位置,使用throws关键字抛给上一级,谁调用就抛给谁,同时调用者也需要对该异常继续处理,同样也有两种处理方式;
  • Java中异常发生之后如果一直上抛,最终会抛给nain方法,main方法继续向上抛,抛给了调用者JVM,JVM感知到该异常发生,终止程序;
  • throws 抛出 受检异常和非受检异常有区别。

throws 受检异常 && 非受检异常

  • 调用doSome()方法如果不对ClassNotFoundException异常进行处理,编译器会提示 Unhandled exception: java.lang.ClassNotFoundException,因为ClassNotFoundException 属于受检异常,在编写代码阶段必须进行处理;
  • 调用doAny时,不需要对ArithmeticException异常进行处理,因为ArithmeticException 属于非受检异常,在编写代码阶段不需要进行处理。
public class ExceptionTest02 {
    public static void main(String[] args) throws ClassNotFoundException {

        //调用doSome时,需要对ClassNotFoundException异常进行处理,因为ClassNotFoundException 属于受检异常
        doSome();
        //调用doAny时,不需要对ArithmeticException异常进行处理,因为ArithmeticException 属于非受检异常
        doAny();
    }


    /**
     * 1.以下代码表示doSome()方法在执行过程中,可能会出现ClassNotFoundException异常
     * 2.ClassNotFoundException属于编译时异常,在代码编写的时候需要处理
     * @throws ClassNotFoundException
     */
    static void doSome() throws ClassNotFoundException{

    }
    static void doAny() throws ArithmeticException{

    }
}

2.2 try catch

概述

  • catch(Exception e)括号内的e可以是该方法抛出的特定异常 ,也可以是其父类;
  • 多个catch块的情况下,捕捉到一个异常即停止;
  • 多个catch块捕捉异常的顺序:先子类后父类。

测试

  • catch()捕获的异常类型分别为
    • FileNotFoundException
    • FileNotFoundException的父类IOException
    • 抛出FileNotFoundException的父类IOException的父类Exception
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest05 {

    /**
     * 1.抛出 FileNotFoundException
     * catch (FileNotFoundException e) {
     *
     *  }
     * @param args
     */
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("dd");
        } catch (FileNotFoundException e) {
            System.out.println("exception");
        }
        System.out.println("2022-04-24");
    }

    /**
     * 2.抛出FileNotFoundException的父类IOException
     * catch (IOException e) {
     *
     * }
     * @param args
     */
    
/*    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("dd");
        } catch (IOException e) {
            System.out.println("exception");
        }
        System.out.println("2022-04-24");
    }*/
    /**
     * 3.抛出FileNotFoundException的父类IOException的父类Exception
     *catch (Exception e) {
     *
     *  }
     * @param args
     */
/*    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("dd");
        } catch (Exception e) {
            System.out.println("exception");
        }
        System.out.println("2022-04-24");
    }*/
}
/*
以上输出均为
exception
2022-04-24
*/
  • 多个catch块的情况,捕捉到一个异常即停止。
public class ExceptionTest06 {
    public static void main(String[] args) {
        FileInputStream dd = null;
        try {
            dd = new FileInputStream("dd");
            dd.read();
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在");
        }catch (IOException e){
            System.out.println("文件读取错误");
        }
        System.out.println("main over");
    }
}
//文件不存在
//main over
  • 多个catch块捕捉异常的顺序:先子类后父类。

    在ExceptionTest06.java中先捕获IOException再捕获其子类FileNotFoundException,会提示

    Exception ‘java.io.FileNotFoundException’ has already been caught

public class ExceptionTest06 {
    public static void main(String[] args) {
        FileInputStream dd = null;
        try {
            dd = new FileInputStream("dd");
            dd.read();
        } catch (IOException e){
            System.out.println("文件读取错误");
        }catch (FileNotFoundException e) {
            System.out.println("文件不存在");
        }
        System.out.println("main over");
    }
}
// 提示 Exception 'java.io.FileNotFoundException' has already been caught

2.3 throws和try catch的选择

  • 如果希望调用者来处理或者希望调用者感知到异常,选择 throws;
  • 其他选择try catch。

3. 异常的常用方法

概述

  • String message = e.getMessage(); 获取异常简单的描述信息;

  • e.printStackTrace() 异步执行,打印异常的追踪堆栈信息

    **【注意】**ExceptionTest07.java中"main over"的输出和nullPointerException.printStackTrace() 的输出顺序不确定,因为e.printStackTrace()是异步执行。

测试

public class ExceptionTest07 {
    public static void main(String[] args) {
        NullPointerException nullPointerException = new NullPointerException("空指针异常-2022-04-24");
        String message = nullPointerException.getMessage();
        System.out.println(message);
        // 异步输出
        nullPointerException.printStackTrace();
        System.out.println("main over");
    }
}
/*
空指针异常-2022-04-24
main over
java.lang.NullPointerException: 空指针异常-2022-04-24
	at exceptionTest.ExceptionTest07.main(ExceptionTest07.java:5)
*/

4. finally

概述

  • finally语句块通常用于完成资源的释放和关闭;
  • finally和return连用时,会在函数返回之前调用finally中的语句;
  • finally语句一定执行 (除非在执行finally语句之前退出jvm)。

测试

  • ExceptionTest08.java中,try中的s.toString()会造成空指针异常,导致下面的fis.close()无法执行,若没有finally语句块,则会导致FileInputStream未关闭;
  • 使用finally语句块用来确保资源的释放和关闭。
public class ExceptionTest08 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
           fis = new FileInputStream("D:\\test.txt");
           System.out.println("读取结束后请关闭流");
           String s = null;
           s.toString();
           // 以上代码会造成空指针异常,以下代码不会执行,流还未关闭
            System.out.println("尝试关闭流");
            fis.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
           e.printStackTrace();
        } catch (NullPointerException e){
            System.out.println("捕捉空指针异常");
        }finally {
            if (fis != null) {
                try {
                    fis.close();
                    System.out.println("关闭fis流");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("main over");
    }
}
//读取结束后请关闭流
//捕捉空指针异常
//关闭fis流
//main over
  • ExceptionTest09.java中,finally和return连用,会在return返回之前执行finally中的语句。
public class ExceptionTest09 {
    public static void main(String[] args) {

        try {
            System.out.println("try...");
            return;
        }finally {
            System.out.println("finally...");
        }
        //以下代码会提示 Unreachable statement
        System.out.println("xx");
    }
}
//try...
//finally...
  • finally语句不一定执行,退出jvm之后,finally语句不执行。
public class ExceptionTest10 {
    public static void main(String[] args) {
        try {
            System.out.println("try...");
            //System.exit(0)
            System.exit(0);
        }finally {
            System.out.println("finally...");
        }
    }
}
//try...
  • 【特殊情况】ExceptionTest11.java 程序执行结果为100
    • 方法体中的代码必须遵循自上而下顺序依次逐行执行
    • return语句一且执行,整个方法必须结束
public class ExceptionTest11 {
    public static void main(String[] args) {
        System.out.println(m());  //【输出】100
    }

    private static int m() {
        int i = 100;
        try {
            return i;
        }
        finally {
            i++;
        }
    }
}

5. finally&final&finalize

5.1 finally

  • 和try连用,finally语句一定执行 (除非在执行finally语句之前退出jvm);

  • finally语句块通常用于完成资源的释放和关闭;

  • finally和return连用时,会在函数返回之前调用finally中的语句。

5.2 final

  • final修饰的类无法被继承;
  • final修饰的方法无法被覆盖;
  • final修饰局部变量只能赋一次值,一旦赋值则不能再重新赋值;
  • final修饰的引用一旦指向某个对象,则不能再重新指向其他对象,但该引用指向的对象的内部数据是可以修改的;
  • final修饰的实例变量必须手动初始化,不能采用系统默认值。
public class Stu {
    String name;
    final String country;
}
// 报错提示 Variable 'country' might not have been initialized
  • final修饰的实例变量一般和static联合使用,称为常量

5.3 finalize()

概念

  • 是object类中的一个方法名,该方法由垃圾回收器gc调用;

  • finalize()是protected修饰的,如下

 protected void finalize() throws Throwable { }
  • 该方法只有一个方法体且里面没有代码;

  • 不需要程序员手动调用,JVM垃圾回收器负责调用该方法;

  • finalize()方法的执行时机

    当一个java对象即将被垃圾回收期回收的时候,垃圾回收器负责调用finalize()方法;

  • 垃圾销毁的时机,如果希望在对象销毁的时候执行一段代码的话,这段代码写到finalize方法当中;

  • 不像equals方法重写之后需要写代码调用,finalize()方法只需要重写,重写完自动会有程序来调用。

测试

  • 创建100000个对象
public class FinalizeTest {
    public static void main(String[] args) {
        for (int i = 0; i < 100000; i++){
            Person person = new Person();
            person = null;
        }
    }
}

class Person {
    String name;

    @Override
    protected void finalize() throws Throwable {
        System.out.println(this + "即将被销毁!");
    }
}
/*
objectTest.Person@1bb3fcd即将被销毁!
objectTest.Person@17cc18a即将被销毁!
objectTest.Person@14354bd即将被销毁!
objectTest.Person@41cbb1即将被销毁!
objectTest.Person@dcc0e3即将被销毁!
*/
  • 测试System.gc(); ,建议启动垃圾回收器。
public class FinalizeTest {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++){
            Person person = new Person();
            person = null;
            System.gc();
        }
    }
}

class Person {
    String name;

    @Override
    protected void finalize() throws Throwable {
        System.out.println(this + "即将被销毁!");
    }
}
//objectTest.Person@1bb3fcd即将被销毁!
//objectTest.Person@17cc18a即将被销毁!

6. 自定义异常类

步骤

  • 编写类继承Exception或者RuntimeException;
    • 继承Exception 是编译时异常
    • 继承RuntimeException是运行时异常
  • 提供两个构造方法,无参 + 带有String参数的。
public class ExceptionTest12 {
    public static void main(String[] args) {
        MyException e = new MyException("自定义异常");
        e.printStackTrace();
        System.out.println(e.getMessage());
    }
/*    自定义异常
    exceptionTest.MyException: 自定义异常
    at exceptionTest.ExceptionTest12.main(ExceptionTest12.java:5)
*/
}

7. 异常和方法覆盖

  • 重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,可以更少。
public class ExceptionTest13 {
   
}
class Animal{
    public void doSome(){
    }
    public void doOther() throws Exception{

    }
}
class Cat extends Animal{
    /**
     * 【提示】
     * 'doSome()' in 'exceptionTest.Cat' 【clashes】 with 'doSome()' in 'exceptionTest.Animal';
     *  overridden method does not throw 'java.lang.Exception
     * @throws Exception
     */
    public void doSome() throws Exception{

    }

    /**
     * 不抛出异常ok
     */
/*    public void doOther(){

    }*/

    /**
     * 抛出更小的异常ok
     * @throws NullPointerException
     */
    public void doOther() throws NullPointerException {

    }
}

8. 关于异常产生后后续代码是否继续执行

ExceptionTest01.java中 “2022-04-24” 不会输出

  • ArithmeticException继承RuntimeException 属于运行时异常,在编写阶段不需要处理异常。
public class ExceptionTest01 {
    public static void main(String[] args) {
        System.out.println(100/0);
        /*
        程序执行发生了ArithmeticException异常,底层new了一个ArithmeticException异常对象
        由于该方法是main方法调用,所以该异常抛给了main方法,main方法没有处理,将该异常自动抛给了JVM
        JVM终止程序的运行
        */
        System.out.println("2022-04-24");
    }
}
public class ArithmeticException extends RuntimeException {
    
}

try && throws

  • try语句块中某一行出现异常,之后的代码不执行;
  • 采用throws方式时,只要异常没有被捕捉,此方法的后续代码不会执行,比如m1 over和m2 over均未执行。
public class ExceptionTest04 {
    public static void main(String[] args) {
        System.out.println("main start");
        try {
            m1();
            //try语句块中某一行出现异常,之后的代码不执行
            System.out.println("异常之后代码不执行");
        } catch (FileNotFoundException e) {
            System.out.println("exception");
        }
        System.out.println("end");

    }

    private static void m1() throws FileNotFoundException {
        System.out.println("m1 start");
        m2();
        //只要异常没有捕捉,采用throws方式 此方法的后续代码不会执行
        System.out.println("m1 over");

    }

    private static void m2() throws FileNotFoundException {
        new FileInputStream("D:\\source");
        //出异常之后的代码不执行
        System.out.println("m2 over");
    }
}
/*
m1 start
exception 
end
*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值