Java SE面向对象--14.Java异常

版权声明:转载请注明原始链接 https://blog.csdn.net/sswqzx/article/details/82850805

一、异常

1、概述

异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处
理异常的方式是中断处理。

2、异常体系

Throwable体系:

  • Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。

  • Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的

Throwable常用方法:

  • public void printStackTrace():打印异常的详细信息。

    包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

  • public String getMessage():获取发生异常的原因。

    提示给用户的时候,就提示错误原因。

  • public String toString():获取异常的类型和异常描述信息(不用)。

3、异常分类

  • 编译时期异常: checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)

  • 运行时期异常: runtime异常。在运行时期,检查异常.在编译时期,运行异常不会被编译器检测(不报错)。(如数学异常)

异常产生图解

二、异常的处理

1、抛出异常throw

格式 : throw new  异常类;   小括号中可以自定义显示信息.

下面演示空指针和索引越界异常

public class ExceptionTest1 {
    public static void main(String[] args) {

        int[] arr = {10, 20, 30};

        // 调用方法
        int result = getElement(arr, 2);
        System.out.println("result = " + result);

        // 后续的代码逻辑 ...
        int num1 = 10;
        int num2 = 20;
        int sum = num1 + num2;
        System.out.println("sum = " + sum);
    }

    // 方法 : 获取执行下标数组中的元素, 并返回
    public static int getElement(int[] array, int index) {

        // 演示 : 手动抛出异常
        // 格式 : throw new 异常对象(自定义信息);

        // 判断1. 空引用异常
        if (array == null) {
            throw new NullPointerException("数组引用不能为空, 请修正!");
        }

        // 判断2. 下标越界
        if (index < 0 || index >= array.length) {
            throw new ArrayIndexOutOfBoundsException("数组下标越界, 请修正. " + index);
        }

        // 正常逻辑 ...
        int element = array[index];
        return element;
    }
}

2、Objects非空判断

如果遇到要判断对象是否为空可以使用

Objects.requireNonNull(array, "数组引用不能为空, 请修正!");

底层实现 :

public static <T> T requireNonNull(T obj, String message) {
    if (obj == null)
        throw new NullPointerException(message);
    return obj;
}
public class Test {
    public static void main(String[] args) {

        String[] str = null;
        Objects.requireNonNull(str,"数组不能为空");
    }
}

3、声明异常throws

格式 : 方法名称 throws 异常类型  { }
public class ParseDateTest1 {
    public static void main(String[] args) throws ParseException {

        Scanner sc = new Scanner(System.in);
        System.out.println("亲, 请输入一个日期字符串 (格式:yyyy-MM-dd) :");
        String date = sc.nextLine();
        sc.close();

        // 调用方法
        parseDate(date);

        System.out.println("程序正常结束完成 ...");
    }

    // 实现 : 定义一个方法, 接收字符串参数, 并将字符串解析为一个日期对象.
    public static void parseDate(String str) throws ParseException {

        // 1. 创建一个日期格式化对象
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");

        // 2. 解析
        // 处理方案一 : 继续声明 (继续抛)
        // 翻译 : 如果发生了异常, 我不处理, 交给我的调用者处理. ( throws ParseException )
        Date date = df.parse(str);
        System.out.println("date = " + date);
    }
}

4、捕获异常try...catch

格式 :
	try {
		// 编写可能发生异常的代码
	} catch(异常类型 e) {
		// 捕获的处理方案 (什么都不写, 也叫处理方案)
	}
public class ParseDateTest1 {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("亲, 请输入一个日期字符串 (格式:yyyy-MM-dd) :");
        String date = sc.nextLine();
        sc.close();

        // 调用方法
        // 处理方案二 : 异常捕获   try - catch ...
        try {
            parseDate(date);
        } catch (ParseException e) {
            e.printStackTrace();   // 日志信息 (文件)
        }

        System.out.println("程序正常结束完成 ...");
    }

    // 实现 : 定义一个方法, 接收字符串参数, 并将字符串解析为一个日期对象.
    public static void parseDate(String str) throws ParseException {

        // 1. 创建一个日期格式化对象
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");

        // 2. 解析
        // 处理方案一 : 继续声明 (继续抛)
        // 翻译 : 如果发生了异常, 我不处理, 交给我的调用者处理. ( throws ParseException )
        Date date = df.parse(str);
        System.out.println("date = " + date);
    }
}

5、finally 代码块的使用 : try – catch- finally (资源)

使用场景 : IO流读写操作中.finally是用来释放资源的。

格式 :
	try {
		// 编写可能发生异常的代码
	} catch(异常类型 e) {
		// 捕获的处理方案 (什么都不写, 也叫处理方案)
	} finally {
		// 释放资源 (无论程序是否发生异常, 都会执行 finally 代码块)
}

public class FileReaderTest1 {
    public static void main(String[] args) {

        boolean result = readFile("C:\\Users\\xieco\\Desktop\\day05-异常、线程\\a.txt");
        System.out.println("result = " + result);

        System.out.println("程序正常执行结束...");
    }

    // 定义一个方法 : 读取文件, 并返回是否读取成功
    public static boolean readFile(String fileName) {

        // 处理方案 : try - catch - finally    (数据库: 连接对象)
        FileReader reader = null;
        try {
            // 1. 模拟一段代码, 创建一个文件读取对象
            // 说明 : reader 与硬盘上的文件关联了, 需要使用完毕后进行关闭
            reader = new FileReader(fileName);

            // 读写操作中可以发生异常 ...

            // try 的最后一条语句返回结果
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            // 作用 : 为关闭资源而生. 
            System.out.println("资源被关闭了...");
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    // ignore 忽略
                }
            }
        }
    }
}

6、异常注意事项

注意点一:

1. 如果子类重写父类的方法, 父类该方法没有声明编译时期异常, 子类重写时, 也不能声明编译时期异常.
2. 如果实现类重写接口中的抽象方法, 接口中该方法没有声明编译时期异常, 实现类重写时也不可以声明编译时期异常.
3.如果存在多个 catch, 父类异常类型必须要写在后面.   FileNotFoundException, IOException (后面)

注意点二:

如果子类方法中有异常, 怎么办 ??? 子类只能自己 try-catch, 不能向外抛.
        // 原因 : 一旦子类向外 `抛出 / 声明` 异常, 那么子类该方法就父类不一致了.

 

注意点三:

多个catch捕获的注意点 : catch 异常类型中, 子类类型必须在父类类型之前被捕获,因为多态语法为父类引用可以接收子类对象.
public class DetailException {
    public static void main(String[] args) {

        try {
            FileReader reader = new FileReader("");
            reader.read();
        } catch (FileNotFoundException e) {     // 子类
            e.printStackTrace();
        } catch (IOException e) {               // 父类
            e.printStackTrace();
        }
    }
}

 

三、自定义异常

1、说明 : 程序开发中, 遇到一些特殊的业务逻辑, Java语言没有提供该逻辑对应的异常类, 此时, 就需要程序员自定义异常类实现.

2、异常类定义要求:

1. 自定义一个编译期异常: 自定义类 并继承于java.lang.Exception。
2. 自定义一个运行时期的异常类:自定义类 并继承于java.lang.RuntimeException。

3、场景 : 注册异常, 如果用户名已经被注册过了. 此时, 不能再使用该用户名注册了, 此时, 需要程序抛出一个注册异常进行代码逻辑的提醒. (提前处理)

定义一个异常类:

/*
    顶层类 : Throwable

    异常类 :
    1. Exception  (提前处理)
    2. RuntimeException (出现问题后在处理)

    自定义异常类, 仅需要提供两个构造方法集合. (无参, 有参)
 */

public class RegisterException extends Exception {

    public RegisterException() {
        super();
    }

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

异常测试类:

public class TestRegisterException {

    // 用户名 : 数据库 (使用数组模拟)
    private static String[] names = { "Jacker", "Peter", "lily" };

    // 测试 : main 方法
    public static void main(String[] args) {

        String name = "Peters";

        // 调用方法
        try {
            boolean result = checkUsername(name);
            System.out.println("result = " + result);
        } catch (RegisterException e) {
            System.out.println("异常信息为 : " + e.getMessage());
        }
    }

    // 提供检查用户名是否存在的方法
    public static boolean checkUsername(String name) throws RegisterException {

        // 1. 遍历 names 数组
        for (int i = 0; i < names.length; i++) {

            // 2. 取出
            String username = names[i];

            // 3. 判断
            if (username.equals(name)) {

                // 4. 名字重复了, 使用异常提醒
                // 注意点 : 编译时期异常. 当前方法必须声明
                throw new RegisterException("用户名已经存在, 请重新输入.");
            }
        }

        return true;
    }
}

猜你喜欢

转载自blog.csdn.net/sswqzx/article/details/82850805