目 录CONTENT

文章目录

从0开始学Java——异常(10)

Eric
2022-01-23 / 0 评论 / 0 点赞 / 163 阅读 / 3,730 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-12-12,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1 概述

  • 在使用计算机语言进行项目开发的过程中,即使程序员将代码写得尽善尽美,在系统的运行过程中依然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式读取文件是否存在网络是否保持畅通等。

  • 异常:在Java语言中,将程序执行中发生的不正常的情况称为“异常”

注意:开发过程中的语法错误和逻辑错误不是异常。

  • 对于异常,一般有两种解决方法:

    • ① 遇到错误就终止程序的执行。
    • ② 由程序员在编写程序的时候,就考虑到错误的检测、错误消息的提示、以及错误的处理。
  • Java将不同的异常通过不同的对象标识,一般发生某种异常,就创建该异常类型的对象,并抛出;然后程序员就可以捕获到这个异常对象,并处理;如果没有捕获到这个异常对象,那么这个异常对象将会导致程序的终止。

2 异常体系

  • 异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Errorjava.lang.Exception,平常我们所说的异常一般都是指java.lang.Exception

img

  • Throwable体系:

    • Error:严重错误,无法处理的错误,只能事先避免
      • 例如:StackOverflowError、OOM(OutOfMemoryError)、内存泄漏(Memory Leak)。
      • 内存溢出(OOM):是指应用程序中存在无法回收的内存或者使用的内存过多,最终是的程序运行用到的内存大于要提供的最大内存。
      • 内存泄露(Memory Leak):是指程序中已经动态分配的堆内存由于某种原因没有来得及释放或无法释放,造成一系统内存的浪费,导致程序运行速度减慢甚至导致系统崩溃等严重后果。
    • Exception:表示异常,其它因编程错误或偶然的外在因素导致的一般性问题,程序员可以通过代码的方式纠正,使得程序继续运行。
      • 例如:NullPointerExceptionFileNotFoundException等。
  • Throwable中的常用方法:

    • 打印异常的详细信息:
public void printStackTrace()
    • 获取发生异常的原因:
public String getMessage()

3 异常分类

  • 我们平常所说的异常就是指Exception,因为这类异常一旦出现,我们就需要对代码进行更正,修复程序。

  • 异常的分类:

    • ① 编译时期异常(checked异常):
      • 例如:FileNotFoundException
      • 是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一 般性异常。
      • 编译器要求Java程序必须捕获或声明所有编译时异常。
      • 对于这类异常,如果程序出处理,可能会带来意向不到的结果。
    • ② 运行时期异常(runtime异常):
      • java.lang.RuntimeException类及它的子 类都是运行时异常。
      • 是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,程序员应该积极避免其出现的异常。
      • 对于这类异常,可以不做处理,因为这类异常很普遍,如果全处理可能会对程序的可读性和运行效率产生影响。

注意:也有人将异常分为非runtime异常runtime异常

4 异常的抛出机制

  • Java提供的是异常处理的抓抛模型

  • 异常抛出机制:

img

  • 为了保证程序的正常执行,代码必须对可能出现的异常进行处理。

  • 示例:

package top.open1024.demo1;

/**
 * 发生异常的时候,如果没有进行处理,会一层一层的向上抛出,最终交给JVM处理,JVM会终止程序,输出对应的信息。
 * 
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 14:44
 */
public class TestException {
    public static void main(String[] args) {
        System.out.println("main方法");
        show();
    }

    public static void show() {
        System.out.println("show方法");
        int[] arr = {1, 2};
        System.out.println(arr[2]);
        System.out.println("show方法调用结束");
    }
}

img

5 异常的处理

5.1 异常处理的方式

  • ① try..catch..finally。

  • ② throw和throws。

5.2 异常throw

  • Java程序的执行过程中如果出现了异常,会产生一个异常类对象,该异常对象将被提交给JVM,这个过程称为抛出异常

  • 异常对象的生成:

    • ① 由JVM自动生成:程序运行结果中,JVM检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应的异常类的实例对象并抛出(自动抛出)。
    • ② 由开发人员手动抛出:由开发人员通过throw new Exception对象throw new Exception的子类对象手动抛出异常。
  • 手动抛出异常的场景:在定义方法的时候,方法需要接收参数,当调用方法使用接收到的参数的时候,需要先对参数数据进行合法性的校验,数据如果不合法,就应该告诉调用者,这时我们可以通过抛出异常的方式来告诉调用者。

  • 在Java中,提供了一个throw的关键字,它用来抛出一个指定的异常的对象。

  • 手动抛出异常的步骤:

    • ① 创建一个异常对象。
    • ② 通过throw new 异常对象,将异常告诉给调用者。

注意:

  • throw关键字必须用在方法内,它用来抛出一个异常对象,将这个异常传递给方法的调用者,并结束当前方法的执行。

  • 方法的调用者要么进行异常捕获处理,要么使用throws声明处理,当然也可以通过throw关键字继续抛出异常。

  • 语法:

thorw new 异常类名(实参);
  • 示例:
package top.open1024.demo2;

/**
 * 
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 14:44
 */
public class TestException {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};

        int element = getElement(arr, 2);
        System.out.println("element = " + element);

        /**
         Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 数组索引越界
            at top.open1024.demo2.TestException.getElement(TestException.java:27)
            at top.open1024.demo2.TestException.main(TestException.java:16)
         */
        int e = getElement(arr, 3); 
        System.out.println("e = " + e);

        System.out.println("main方法结束");
    }

    public static int getElement(int[] arr, int index) {
        if (null == arr) {
            throw new NullPointerException("要访问的arr数组不存在");
        }
        if (index < 0 || index > arr.length - 1) {
            throw new ArrayIndexOutOfBoundsException("数组索引越界");
        }
        return arr[index];
    }
}

5.3 声明异常throws

  • 声明异常:将异常标识出来,告诉调用者。如果方法内通过throw关键字编译时异常(非runtime异常),而没有捕获处理,那么必须通过throws进行声明,让调用者进行处理。

  • 关键字throws用在方法声明上,告诉调用者当前方法不处理异常,而是提醒该方法的调用者来处理异常。

  • 语法:

修饰符 返回值类型 方法名(形参列表) throws 异常类名1,异常类名2...{}
  • 示例:
package top.open1024.demo3;

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

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 15:20
 */
public class TestException {
    public static void main(String[] args) throws FileNotFoundException {
        readFile("java秘籍");
    }

    public static void readFile(String filePath) throws FileNotFoundException {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new FileNotFoundException(filePath + "文件不存在");
        }
    }
}

5.4 try...catch...finally

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

  • 如果一个异常回到main方法处,并且main方法也不处理,则程序终止运行。

  • 语法:

try{
......  //可能产生异常的代码 }
catch( ExceptionName1 e ){
......  //当产生ExceptionName1型异常时的处置措施 }
catch( ExceptionName2 e ){
......    //当产生ExceptionName2型异常时的处置措施 }
finally{
......     //无论是否发生异常,都无条件执行的语句 
}
  • 解释:

    • try:捕获异常的第一步是用try{}语句块选定捕获异常的范围,将可能出现异常的代码放到try语句块中。
    • catch(Exception e)
      • catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
      • 如果明确知道产生的是何种异常,可以用该异常类作为catch的参数,也可以用其父类作为catch的参数。
      • 多个catch语句块,子类型在上,父类型在下。
    • finally
      • 捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在程序流程到其它部分以后,能够对程序的状态做出统一的处理。
      • 不论在try代码块中是否发生了异常,catch语句中是否执行,catch语句中是否有异常,catch语句中是否有return,finally块中的语句都会被执行。

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

  • 示例:
package top.open1024.demo4;

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

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 16:36
 */
public class TestException {
    public static void main(String[] args) {
        readFile("不敲代码学会Java秘籍.txt");
        System.out.println("继续学习吧...");
    }

    public static void readFile(String filePath) {
        File file = new File(filePath);
        try {
            if (!file.exists()) {
                throw new FileNotFoundException(filePath + "文件不存在");
            }
            if (!file.isFile()) {
                throw new IllegalAccessException(filePath + "非法访问");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("这里的代码一定会执行");
        }

    }
}

5.5 不捕获异常的情况

  • ① 如果抛出的异常都是Exception或其子类型的运行时异常,这些运行时异常的特点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过,但是运行的时候发生异常会使得程序终止运行。

  • ② 如果抛出的异常都是IOException等类型的非运行异常,则必须捕获,否则编译错误。换言之,我们必须处理编译时异常,将这类类型的异常捕获,转换为运行时异常。

6 异常的注意事项

  • ① 多个异常使用捕获如何处理。

    • 多个异常分别处理。
    • 多个异常一次捕获,多次处理(推荐)。
    • 多个异常一次捕获一次处理。
try{
     编写可能会出现异常的代码
}catch(异常类型A  e){  当try中出现A类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch(异常类型B  e){  当try中出现B类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}

注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。

  • ② 运行时异常被抛出可以不处理,即不捕获也不声明抛出。

  • ③ 在程序没有发生异常的时候,try语句块中有return语句,也有finally语句,先执行finally语句,再执行return语句。

  • ④ 在程序发生异常的时候,catch语句块中有return语句,也有finally语句,先执行finally语句,再执行return语句。

  • ⑤ 如果finally中有return语句,永远返回finally中的return结果(避免该情况)。

  • ⑥ 如果父类方法抛出了多个异常,子类重写父类方法的时候,抛出的和父类方法相同的异常或者是父类方法抛出异常的子类或者不抛出异常(针对编译时异常而言)。

  • ⑦ 如果父类方法没有抛出异常,则子类重写父类方法的时候也不可以抛出异常,此时,如果子类方法中产生了编译时异常,则只能捕获,而不能声明抛出。

7 自定义异常

7.1 概述

  • Java中的不同异常类,分别表示某一种具体的异常情况,但是在开发中总会有些异常情况是Java中预先没有定义好的,此时我们需要根据自己业务的异常情况来定义异常类,比如:年龄负数问题,考试成绩负数问题等等。

  • 自定义异常类:

    • ① 自定义编译时异常类:自定义类并继承于java.lang.Exception
    • ② 自定义运行时异常类:自定义类并继承于java.lang.RuntimeException(实际开发中,这种用的很多)。

7.2 应用示例

  • 示例:
package top.open1024.demo5;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 21:58
 */
public class SexException extends RuntimeException {

    public SexException() {}

    public SexException(String message) {
        super(message);
    }

}
package top.open1024.demo5;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 22:03
 */
public class AgeException extends RuntimeException {
    public AgeException() {}

    public AgeException(String message) {
        super(message);
    }
}
package top.open1024.demo5;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 21:56
 */
public class Person {
    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private int age;

    /**
     * 性别
     */
    private String gender;

    public Person() {}

    public Person(String name, int age, String gender) {
        this.name = name;
        if (age < 0 || age > 150) {
            throw new AgeException("age必须在0~150之间");
        }
        this.age = age;
        if (!"男".equals(gender) || !"女".equals(gender)) {
            throw new SexException("性别必须是男或女");
        }
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + this.name + '\'' + ", age=" + this.age + ", gender='" + this.gender + '\'' + '}';
    }
}
package top.open1024.demo5;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-14 21:58
 */
public class PersonTest {
    public static void main(String[] args) {
        Person person = new Person("张三", 180, "呵呵");

        System.out.println("person = " + person);

    }
}
0

评论区