JavaSE-17 【异常】

第一章 什么是异常

1.1 异常的概念

异常:指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止

在Java中,异常本身就是一个类,产生异常就是创建一个异常对象并且抛出一个异常对象的过程

Java处理异常的方式是中断处理。

1.2 异常的体系

异常机制其实是帮助我们找到程序中的问题。

异常的根类是:java.lang.Throwable,其有两个子类,分别是

  • java.lang.error 错误
  • java.lang.exception 异常,平常所说的异常就是exception

JavaSE-17 【异常】_第1张图片

Throwable体系:

  • Error:错误,不需要处理
  • Exception: 需要进行处理的异常

JavaSE-17 【异常】_第2张图片

  • 异常处理的方式:
    • throws 抛出异常
    • try…catch 捕获异常

1.3 异常的分类

  • Exception分为两大类:
    • 运行时异常 [即程序运行时,发生的异常]。
    • 编译时异常 [即编程时编译器检查出的异常,Checked异常]。

所有的RuntimeException类及其子类的实例被称为Runtime异常;

不是RuntimeException类及其子类的异常实例则被称为Checked异常。

  • 案例
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo01_exception {
    /*
        Exception: 异常
            分为运行时异常和非运行时异常
            RuntimeException:运行期异常---编译期不检查,运行时出现问题,需要我们回来修改代码
            非RuntimeException:非运行期异常---编译器必须处理,否则程序编译不通过,无法运行
        Error:错误,不需要处理
     */
    //异常/错误案例
    public static void main(String[] args) throws ParseException {
        show1();//抛出异常
        show2();//捕获异常
        show3();//程序错误
    }

    private static void show3() throws ParseException {
        //异常1:编译其异常---抛出异常
        //格式化日期
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        //把字符串格式的日期转换成Date格式的日期
        Date date = format.parse("2022-12-12");
        System.out.println(date);
    }

    private static void show2() {
        //异常2:运行期异常---捕获异常
        int[] arr1 = {1, 2, 3};
        try {
            //可能会出现异常的代码
            System.out.println(arr1[3]);
        } catch (Exception e) {
            //异常的处理逻辑
            System.out.println(e);
        }
    }

    private static void show1() {
        //错误:内存溢出,出现错误:java.lang.OutOfMemoryError
        int[] arr2 = new int[1000 * 100000000];
        System.out.println(arr2);
    }
}
  • 抛出异常

JavaSE-17 【异常】_第3张图片

  • 捕获异常

JavaSE-17 【异常】_第4张图片

  • 程序错误

JavaSE-17 【异常】_第5张图片

1.4 异常产生的过程

JavaSE-17 【异常】_第6张图片

JavaSE-17 【异常】_第7张图片

JavaSE-17 【异常】_第8张图片

第二章 异常的处理

Java异常的处理包含五个关键字:try、catch、finally、throw、throws

2.1 抛出异常throw

throw关键字:

  • 作用:使用throw关键字在指定的方法中抛出指定的异常
  • 语法:throw new XXXException(“异常产生的原因”)

注意事项:

  • throw关键字必须写在方法的内部、
  • throw关键字后面new的对象必须是Exception或者是其子类对象
  • throw关键字抛出指定的异常对象,我们必须处理
    • throw关键字后面new的是RuntimeException或者RuntimeException对象的子类,可以不做处理,交予JVM处理,打印异常,中断程序
    • throw关键字后面new的是编译检查异常,那必须处理这个异常,方式:throws或者try…catch

案例:

public class Demo03_Throw {
    /*
        throw关键字的使用
        案例:获取指定数组下标处的元素
            1、定义方法
            2、参数:int[] arr, int index
            3、对传递给方法的参数进行校验合法性
            4、如果参数不合法,使用抛出异常的方式,告知调用者,参数传递错误
     */
    public static void main(String[] args) {
        //1、创建数组
        int[] arr1 = null;
        int[] arr2 = {1, 2, 3};
        //2、赋值调用方法
        //参数(arr1,1)
        int element1 = getELement(arr1, 1);
        System.out.println(element1);
        //参数(arr2,2)
        int element2 = getELement(arr2, 2);
        System.out.println(element2);
    }

    //定义获取数组元素的方法
    private static int getELement(int[] arr, int index) {
        //1、判断传递的参数arr是否为null
        if (arr == null) {
            //空指针异常属于运行期异常,交给JVM处理,我们不需要处理
            throw new NullPointerException("传递的数组的值是空");
        }
        //2、判断index是否在数组的范围内
        if (index < 0 || index > arr.length - 1) {
            //抛出数组下标越界异常
            throw new IndexOutOfBoundsException("传递的下标越界");
        }
        int ele = arr[index];
        //方法的返回值
        return ele;
    }
}

JavaSE-17 【异常】_第9张图片

JavaSE-17 【异常】_第10张图片

2.2 声明异常throws

throws关键字:

  • 作用:运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常
  • 语法格式:修饰符 返回值类型 方法名(参数类型 参数名) throws 异常类型{ … }
  • 注意:如果方法中throw抛出了编译时异常,而没有捕获处理,那么就必须通过throws声明,交给调用者处理

JavaSE-17 【异常】_第11张图片

案例:

import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo04_Throws {
    /*
        throws关键字
            概念:异常处理的第一种方式:交给别人处理
            作用:可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理,
                 自己不处理,别人处理,最终交给JVM处理---中断程序
            格式:在方法声明时使用
            注意事项:
                1、必须写在方法声明处
                2、throws关键字后面的异常必须是 Exception 或者是Exception的子类
                3、方法内部如果抛出多个异常对象,那么throws后边必须声明多个异常
                4、方发声明处可以使用Exception一个类,代替多个其子类异常
                5、throws抛出异常的两种处理方式
                    a、抛出的异常交给方法调用者处理,最终交给JVM处理
                    b、try...catch处理异常
     */
    //声明异常类型存在子父类关系,直接写父类即可
    //public static void main(String[] args) throws FileNotFoundException, IOException {
    //readFile("d:\\1.txt");
    //}
    public static void main(String[] args) throws IOException {
        readFile("d:\\1.txt");
    }

    /*
        定义方法:对传递的文件路径进行合法性判断
                如果路径不是d:\\1.txt,那么就抛出文件找不到异常,抛给方法的调用者
     */
    public static void readFile(String fileName) throws FileNotFoundException, IOException {
        //判断传入的文件名称是否是以.txt结尾的
        if (!fileName.endsWith(".txt")) {
            //抛出IO异常,编译检查型异常,需要throws抛出给方法的调用者或者try...catch捕获处理
            throw new IOException("文件后缀名错误");
        }
        //判断传入文件名称是否一致
        if (!fileName.equals("d:\\1.txt")) {
            //此异常是编译检查异常,需要处理异常,可以使用throws或者try...catch
            throw new FileNotFoundException("文件找不到");
        }
        //文件名称一致,上传成功
        System.out.println("路径正常,读取文件成功");
    }
}

JavaSE-17 【异常】_第12张图片

JavaSE-17 【异常】_第13张图片

2.3 捕获异常try…catch

如果异常出现,会立即终止程序,所以需要我们处理异常,

  • 1、该方法不处理,而是声明抛出,由该方法的调用者来处理 ( throws )

  • 2、在方法中使用try-catch的语句来处理异常

try-catch的方式就是用于捕获异常

  • 作用:在Java中针对性的对某些语句进行捕获,可以对出现的异常进行指定方式的处理

  • 语法:

  • try {
        编写可能出现异常的代码;
    }catch(Exception e){
        处理异常的代码;
        打印异常、继续抛出异常;
    }
    

JavaSE-17 【异常】_第14张图片

案例:

import java.io.IOException;

public class Demo05_TryCatch {
    /*
        try-catch:异常处理的第二种方式:捕获异常自行处理
        作用:捕获产生的异常并处理
        语法:
            try{
                可能出现异常的代码;
            }catch(Exception e){
                异常处理的逻辑代码;
                记录日志,打印异常,继续抛出异常;
            }
        注意事项:
            1、try中可能抛出多个异常对象,那么可以使用多个catch捕获处理异常
            2、执行顺序:
                如果try产生异常,try---catch---继续执行catch之后的代码
                如果try没有差生异常,就不会执行catch中的代码,继续执行catch之后的代码
     */
    public static void main(String[] args) {
        //调用方法,并捕获处理异常
        try {
            //产生异常的代码
            readFule("d:\\1.tx");
        }catch (IOException e){
            //异常的处理逻辑
            System.out.println("文件名称后缀不匹配!!!");
        }
        System.out.println("后续执行的代码");
    }

    /*
        需求:
        1、定义读取文件的方法
        2、判断传递的文件名称后缀是否以.txt结尾
        3、不匹配抛出异常给方法的调用者,告知文件后缀名称不匹配
     */
    //定义方法
    public static void readFule(String fileName) throws IOException {
        if (!fileName.endsWith(".txt")){
            throw new IOException("出现异常,后缀不匹配!!!");
        }
        System.out.println("文件名匹配成功!!!");
    }
}

JavaSE-17 【异常】_第15张图片

  • Throwable定义了获取异常信息的三个方法
    • 1、String getMessage(); 返回此Throwable的简短描述
    • 2、String toString(); 返回此Throwable的详细描述
    • 3、void printStackTrace(); JVM打印异常对象,默认调用此方法
import java.io.IOException;

public class Demo06_ThrowableMethods {
    /*
        Throwable定义了三个处理异常的方法
            1、String getMessage();
                返回此Throwable的简短描述
            2、String toString();
                返回此Throwable的详细描述
            3、void printStackTrace();
                JVM打印异常对象,默认调用此方法
     */
    public static void main(String[] args) {
        try {
            //出现异常的代码
            readFile("d:\\1.tx");
        } catch (IOException e) {
            System.out.println("文件名称不匹配!!!");
            /*
                1、String getMessage();
                    返回此Throwable的简短描述
                2、String toString();
                    返回此Throwable的详细描述
                3、void printStackTrace();
                    JVM打印异常对象,默认调用此方法
             */
            System.out.println("简短的信息描述是:" + e.getMessage());
            System.out.println("详细的信息描述是:" + e.toString());
            e.printStackTrace();
        }
        System.out.println("后续的代码");
    }

    private static void readFile(String fileName) throws IOException {
        if (!fileName.endsWith(".txt")) {
            throw new IOException("验证失败,不匹配");
        }
        System.out.println("文件后缀名匹配成功!!!");
    }
}

JavaSE-17 【异常】_第16张图片

2.4 finally 代码块

finally:有一些特定的代码。无论异常发生与否,都需要执行。同时,因为异常导致程序跳转,导致有些代码无法被执行到,而finally就是解决这个问题的,在finally中存放的代码无论无核是会被执行的

  • 什么时候的代码必须最终执行
    • 当在try语句块中执行了一些物理的如:磁盘文件,网络连接,数据库连接等之后,我们必须在使用完毕之后关闭资源,所以finally此时配上了用处

finally语法:

		try {
            //可能出现异常的代码
        }catch (Exception e){
            //异常的处理逻辑以及错误信息的处理
        }finally {
            //释放资源
        }

finally的注意事项:

  • finally并且不能单独使用
  • finally一般用于资源释放,无论程序是否出现异常,最终都要释放资源

案例:

import java.io.IOException;

public class Demo07_Finally {
    /*
        finally的使用:
            不管是否出现异常,都会被执行
            不能单独使用
            一般用于关闭或者释放资源
     */
    public static void main(String[] args) {
        try {
            //可能出现异常的代码
            readFile("d:\\1.txt");
        } catch (IOException e) {
            //异常处理逻辑
            e.printStackTrace();
        } finally {
            //无论是否出现异常都会被执行
            System.out.println("释放资源");
        }
    }

    private static void readFile(String fileName) throws IOException {
        if (!fileName.endsWith(".txt")) {
            throw new IOException("验证失败,不匹配");
        }
        System.out.println("文件后缀名匹配成功!!!");
    }
}

JavaSE-17 【异常】_第17张图片

JavaSE-17 【异常】_第18张图片

2.5 异常注意事项

  • 多个异常捕获之后的处理方式
    • 1、多个异常分别处理
    • 2、多个异常一次捕获,多次处理
    • 3、多个异常一次捕获,一次处理

一般使用一次捕获,多次处理的方式,格式如下

try{
    可能出现异常的代码
}catch(异常类型A e){
    处理异常的代码
    //记录日志,打印异常信息,继抛出异常
}catch(异常类型B e){
    出现异常的代码
    //记录日志,打印异常信息,继抛出异常
}
  • 注意事项:
    • 这种异常处理方式,要求多个catch中的异常不能相同,并且如果catch中的多个异常之间有父子关系,那么子类异常要求在上面的catch中处理,父类异常要求在下面的catch中处理
    • 运行时异常被抛出可以不处理。
    • 如果父类抛出多个异常,子类覆盖父类方法时,只能抛出相同的异常或者其他的子类
    • 父类方法没有抛出异常,子类覆盖该方法不可能抛出异常,子类只能捕获处理
    • 在try-catch后面追加finally代码,其中的代码一定会执行,通常用于资源的释放
    • 如果finally中有return语句,永远返回finally中的结果,但是要避免这种情况的发生

案例:方式一:多个异常分别处理

import java.util.ArrayList;
import java.util.List;

public class Demo8_MoreCatch {
    /*
        多个异常的捕获方式
            1、多个异常分别处理
            2、多个异常一次捕获,多次处理
            3、多个异常一次捕获,一次处理
     */
    public static void main(String[] args) {
        //方式一:多个异常分别处理
        try {
            int[] arr = {1, 2, 3};
            //ArrayIndexOutOfBoundsException
            System.out.println(arr[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e);
        }

        try {
            List<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            //IndexOutOfBoundsException
            System.out.println(list.get(3));
        } catch (IndexOutOfBoundsException e) {
            System.out.println(e);
        }
    }
}

JavaSE-17 【异常】_第19张图片

案例:方式二:多个异常一次捕获多次处理

import java.util.ArrayList;
import java.util.List;

public class Demo8_MoreCatch {
    /*
        多个异常的捕获方式
            1、多个异常分别处理
            2、多个异常一次捕获,多次处理
            3、多个异常一次捕获,一次处理
     */
    public static void main(String[] args) {
        /*
            方式二:多个异常一次捕获多次处理
            注意事项:
                catch里面定义的异常变量,如果有子父类观系,
                那么子类的异常变量必须写在上面,父类的写在下面
         */
        try {
            //数组下标越界
            int[] arr = {1, 2, 3};
            //ArrayIndexOutOfBoundsException
            System.out.println(arr[3]);
            //下标越界
            List<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            //IndexOutOfBoundsException
            System.out.println(list.get(3));
        } catch (ArrayIndexOutOfBoundsException e) {//子类 在上
            System.out.println(e);
        } catch (IndexOutOfBoundsException e) {//父类 在下
            System.out.println(e);
        }
        System.out.println("后续代码");
    }
}

JavaSE-17 【异常】_第20张图片

案例:方式三:多个异常多个异常一次捕获,一次处理

import java.util.ArrayList;
import java.util.List;

public class Demo8_MoreCatch {
    /*
        多个异常的捕获方式
            1、多个异常分别处理
            2、多个异常一次捕获,多次处理
            3、多个异常一次捕获,一次处理
     */
    public static void main(String[] args) {
        /*
            方式二:多个异常一次捕获,一次处理
         */
        try {
            //数组下标越界
            int[] arr = {1, 2, 3};
            //ArrayIndexOutOfBoundsException
            System.out.println(arr[3]);
            //下标越界
            List<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(2);
            list.add(3);
            //IndexOutOfBoundsException
            System.out.println(list.get(3));
        } catch (IndexOutOfBoundsException e) {
            System.out.println(e);
        }
        System.out.println("后续代码");
    }
}

JavaSE-17 【异常】_第21张图片

第三章 自定义异常

3.1 自定义异常概述

什么是自定义异常:

  • 在Java开发的过程中,会有一些异常是JDK未定义的,此时我们需要根据业务的异常情况来自定义异常

如何自定义异常:

  • 自定义一个编译期异常,自定义类,并且继承java.lang.Exception
  • 自定义一个运行期异常,自定义类,并且继承java.lang.RuntimeException

JavaSE-17 【异常】_第22张图片

3.2 自定义异常案例

  • 需求:

JavaSE-17 【异常】_第23张图片

  • 创建自定义异常类
public class Demo09_RegisterException extends Exception{
    /*
        自定义异常:
            语法格式:
                public class XXXException extends Exception/RuntimeException {
                    添加一个空参数的构造方法
                    添加一个带异常信息的构造方法
                }
            注意:
                1、自定义异常类一般都是以Exception结尾,说明该类是一个异常类
                2、自定义异常类:必须继承Exception或RuntimeException
                    a、继承Exception,那么定义的异常类是以个编译期异常类,
                        如果方法内部抛出了编译期异常,那么必须处理这个异常
                        要么抛出异常,要么try-catch捕获异常
                    b、继承RuntimeException。那么自定义的异常类就是运行期异常
                        运行期异常无需处理,交给JVM虚拟机处理---终端处理
     */
    //添加无参构造
    public Demo09_RegisterException() {
        super();
    }
    /*
        添加一个带异常信息的构造方法
     */
    public Demo09_RegisterException(String message) {
        super(message);
    }
}
  • 创建测试类
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class Demo09_TestRegister {
    /*
        需求:模拟注册操作,如果用户名存在,则抛出异常,提示用户名已存在
        步骤:
            1、使用集合保存已经注册的用户名信息
            2、使用Scanner获取用户输入的注册的用户名信息
            3、定义一个方法,对用户输入的用户名进行判断
            4、遍历集合中存储的已经注册的用户名
            5、使用输入的用户名和遍历的用户名进行比对
            6、如果是true。说明用户名已经存在,抛出自定义异常,告知用户名已经存在
            7、如果是false。继续遍历
            8、如果循环比对结束了,还没有找到重复的用户名,提示用户注册成功
     */
    //1、定义集合存放已经注册的用户名
    public static List<String> list = new ArrayList<>();

    public static void main(String[] args) throws Demo09_RegisterException {
        list.add("tom");
        list.add("tony");
        list.add("jack");
        list.add("anny");
        for (int i = 0; i < 5; i++) {
            //2、使用Scanner获取用户输入的用户名
            System.out.println("请输入您要注册的用户名");
            String username = new Scanner(System.in).nextLine();
            //3。调用方法
            checkArray(username);
            //4、打印集合
            System.out.println(list);
        }
    }

    //3、定义方法---抛出异常
    public static void checkArray(String name) throws Demo09_RegisterException {
        //遍历集合中已经存在的用户名
        for (String item : list) {
            if (item.equals(name)) {
                //true:说明用户名已经存在,抛出自定义异常,告知用户名已经存在
                throw new Demo09_RegisterException("用户名已经存在,请重新注册");
            }
        }
        //如果循环比对结束了,还没有找到重复的用户名,提示用户注册成功
        System.out.println("恭喜您注册成功");
        //添加用户名到集合中
        list.add(name);
    }
}
  • 运行结果

JavaSE-17 【异常】_第24张图片

JavaSE-17 【异常】_第25张图片

你可能感兴趣的:(JavaSE,java)