Skip to content

1.异常:

指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。 > 异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.

2.异常体系

异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Errorjava.lang.Exception,平常所说的异常指java.lang.Exception

Throwable体系:

  • Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
  • Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。

Throwable中的常用方法:

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

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

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

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

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

出现异常,不要紧张,把异常的简单类名,拷贝到API中去查。

3.异常分类

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

异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?

  • 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
  • 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常,)


异常作用

  1. 查看bug信息
  2. 作为特殊返回值告诉调用者底层执行情况

4.异常的产生过程解析

先运行下面的程序,程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。我们通过图解来解析下异常产生的过程。

工具类

java
public class ArrayTools {
    // 对给定的数组通过给定的角标获取元素。
    public static int getElement(int[] arr, int index) {
        int element = arr[index];
        return element;
    }
}

测试类

java
public class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        intnum = ArrayTools.getElement(arr, 4)
        System.out.println("num=" + num);
        System.out.println("over");
    }
}

上述程序执行过程图解:

5.异常处理方式

1.JVM默认处理异常方式

  • 把异常单名称,异常原因,异常位置等信息输出在控制台
  • 程序停止,代码不再执行

2.捕获try...catch

一般在调用处,能让代码继续运行下去

3.抛出throw/throws

结束方法运行并将异常抛出给调用者

throw
  1. 用途throw 关键字用于显式抛出一个异常实例。它通常用在方法体内部,用于指示发生了特定的异常情况。
  2. 用法:你可以使用 throw 关键字后跟一个异常对象来抛出异常。这个异常对象可以是一个已经存在的异常实例,或者是新创建的异常实例。
  3. 示例
java
throw new IllegalArgumentException("参数错误");
  1. 应用场景:当方法内部发生了某些不能继续执行操作的情况时,可以使用 throw 来终止方法执行并将异常传递给调用者。
throws
  1. 用途throws 关键字用于在方法签名中声明该方法可能抛出的异常类型。它告诉方法的调用者,调用这个方法可能会遇到这些异常,因此调用者应该准备好处理它们。
  2. 用法:在方法声明中使用 throws 关键字,后面跟上一个或多个可能被抛出的异常类型。
  3. 示例
java
public void readFile(String fileName) throws IOException {
    // 方法实现
}
  1. 应用场景:在创建一个可能产生异常的方法时,使用 throws 来通知调用者此方法可能抛出的异常类型。
总结
  • throw 用于抛出一个具体的异常实例,通常用在方法体内。
  • throws 用于声明一个方法可能抛出的异常类型,用在方法的声明上。
  • throw 引发异常,throws 声明异常,这两者通常会一起使用:一个方法通过 throws 声明它可能抛出的异常,然后在方法体内通过 throw 来实际抛出这些异常

4.两种异常区别区别

非检查型异常(Unchecked Exception)

  • 您可以在方法中抛出非检查型异常(例如 RuntimeException 及其子类)而无需在方法签名中声明它们。
  • 这些异常包括 RuntimeExceptionIllegalArgumentExceptionNullPointerException 等。
  • 示例:
java
public void someMethod() {
    throw new IllegalArgumentException("无效参数");
}

检查型异常(Checked Exception)

  • 如果您想在方法中抛出检查型异常(例如 IOExceptionSQLException 等),则必须在方法签名中使用 throws 关键字声明这些异常,除非您在方法内部处理了它们(例如,使用 try-catch 块)。
  • 如果您没有声明也没有处理检查型异常,编译器将报错。
  • 示例:
java
public void readFile(String fileName) throws IOException {
    throw new IOException("文件无法读取");
}
  1. 为什么区分

    • 非检查型异常通常代表编程错误,如逻辑错误、非法参数等,而检查型异常代表那些即使程序正确也可能发生的情况,如文件未找到、网络问题等。
    • Java 设计这种区分是为了让程序员更清楚地了解他们需要处理的异常类型,以及哪些异常是由程序逻辑错误导致的。

总结:您可以在不声明异常的情况下抛出非检查型异常,但如果您要抛出检查型异常,必须在方法签名中使用 throws 来声明这些异常,或者在方法内部处理它们。

6. 抛出异常throw

使用格式:

throw new 异常类名(参数);

实验代码:

java
throw new NullPointerException("要访问的arr数组不存在");

throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");

学习完抛出异常的格式后,我们通过下面程序演示下throw的使用。

java
public class Exceptiondemo {  
public static void main(String[] args) {  
int[] arr = null;//new int[0];//{1,2,3,6,5,48,8,};  
int max = 0;  
try {  
max = getMax(arr);  
} catch (NullPointerException e) {  
System.out.println("空指针异常");  
//throw new RuntimeException();  
} catch (ArrayIndexOutOfBoundsException e) {  
System.out.println("索引越界异常");  
//throw new RuntimeException();  
}  
System.out.println(max);  
}  
  
public static int getMax(int[] arr) throws NullPointerException,ArrayIndexOutOfBoundsException{  
if (arr == null){  
//手动创建一个异常对象,并把异常交给方法调用者处理  
//此时方法结束,下面代码不再执行  
throw new NullPointerException();  
}  
  
//  
if(arr.length == 0){  
throw new ArrayIndexOutOfBoundsException();  
}  
  
System.out.println("看看我执行了吗");  
int max = arr[0];  
for (int i = 1; i < arr.length; i++) {  
if(arr[i]>max){  
max = arr[i];  
}  
}  
return max;  
}  
}

> 注意:如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。 > > 那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续讲问题声明出去,使用throws声明处理。

GPT解释

在 Java 中,throw 关键字用于显式抛出一个异常。它是 Java 异常处理的一个重要部分,允许您手动触发特定类型的异常。以下是关于 throw 关键字的详细解释:

基本用法
  • throw 后面跟随一个异常对象。这个对象必须是 Throwable 类或其子类的实例。
  • 异常可以是预先创建的实例,或者是在 throw 语句中新创建的。
  • throw 语句会立即终止当前的执行流程,并将异常传递给最近的适配的 catch 块处理。如果没有适配的 catch 块,程序将终止,并且异常会被传递给调用者。
示例
java
public void checkAge(int age) {
    if (age < 18) {
        throw new IllegalArgumentException("年龄不符合要求。");
    }
    // 其他逻辑
}

在这个例子中,如果 age 小于 18,将抛出一个 IllegalArgumentException

注意事项
  1. 异常类型

    • 您可以抛出任何类型的异常,但通常应该选择描述问题的最合适的异常类型。
    • Java 标准库提供了多种异常类型,如 IllegalArgumentExceptionIllegalStateExceptionNullPointerException 等。
  2. 异常处理

    • 如果抛出的是检查型异常(即非 RuntimeException 的子类),那么要么必须在当前方法的 throws 子句中声明该异常,要么必须在方法内部处理它(使用 try-catch)。
    • 对于非检查型异常RuntimeException 及其子类),不需要在方法签名中声明。
  3. 程序流程

    • 抛出异常后,程序会立即停止当前执行的代码块,直到找到合适的异常处理器(catch 块)或终止程序。
    • 这意味着 throw 之后的代码不会被执行。
使用场景
  • 验证参数。
  • 检查方法的调用是否符合预期的状态。
  • 显式触发错误条件,以便更上层的调用者可以捕获并处理异常。
  • 测试目的,例如模拟异常情况。

throw 关键字是 Java 异常处理机制的一个核心组成部分,它提供了一种强大的方式来处理错误和异常情况。

7.声明异常throws

声明异常

将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。 ==关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).==

声明异常格式:

写在方法定义处

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }

GPT解释

在 Java 中,throws 关键字用于声明一个方法可能抛出的异常。这是异常处理的一部分,它提供了一种方式来标识方法可能在执行时遇到的问题,并且需要调用者来处理这些问题。

作用和用法
  • 当一个方法可能抛出某种异常,但不希望在方法内部处理这个异常时,可以在方法声明中使用 throws 关键字后跟异常类型。
  • 这意味着任何调用该方法的代码都必须处理或再次声明这些异常,这通常通过 try-catch 块或继续使用 throws 关键字在其自身的方法声明中实现。
  • throws 关键字主要用于检查型异常(checked exceptions),即那些在编译时就需要被捕获或声明的异常。
示例

假设有一个方法,它从文件中读取数据。读取文件可能会引发 IOException,如果你不想在这个方法内部处理这个异常,你可以在方法签名中使用 throws

java
public void readFile(String fileName) throws IOException {
    // 代码尝试打开文件
    // 如果文件不存在,将抛出 IOException
}

在这个例子中,任何调用 readFile 方法的代码都需要处理 IOException,例如:

java
public void callingMethod() {
    try {
        readFile("data.txt");
    } catch (IOException e) {
        // 处理异常,比如打印错误信息或尝试打开不同的文件
    }
}
异常类型
  • 检查型异常(Checked Exception):这些异常==必须==在编译时被捕获或声明。它们通常是外部错误,程序员应该提前处理。
  • 非检查型异常(Unchecked Exception):包括运行时异常(RuntimeException)和错误(Error)。它们不需要在方法签名中声明,通常表示编程错误或不可恢复的系统错误。

总结

  • 使用 throws 关键字可以提醒方法的调用者需要处理潜在的异常。
  • 它有助于提高程序的健壮性,因为它强制调用者考虑到异常情况并作出相应的处理。
  • 过度使用 throws 可能会导致代码中异常处理的滥用,应当在设计时考虑合理的异常处理策略。

9. finally 代码块

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

示例

java
try {
    // 可能会抛出异常的代码
} catch (Exception e) {
    // 捕获异常并处理
} finally {
    // 无论是否发生异常,都会执行的代码
}

当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

finally的语法:

try...catch....finally:自身需要处理异常,最终还得关闭资源。

> 注意:finally不能单独使用。

比如在我们之后学习的IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。

finally代码参考如下:

java
public class TryCatchDemo4 {
    public static void main(String[] args) {
        try {
            read("a.txt");
        } catch (FileNotFoundException e) {
            //抓取到的是编译期异常  抛出去的是运行期 
            throw new RuntimeException(e);
        } finally {
            System.out.println("不管程序怎样,这里都将会被执行。");
        }
        System.out.println("over");
    }
    /*
     *
     * 我们 当前的这个方法中 有异常  有编译期异常
     */
    public static void read(String path) throws FileNotFoundException {
        if (!path.equals("a.txt")) {//如果不是 a.txt这个文件 
            // 我假设  如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new FileNotFoundException("文件不存在");
        }
    }
}
> 当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。

在Java中,finally 块通常是一种确保无论是否发生异常都执行的代码块。然而,有一些情况下 finally 块不会执行,主要是因为以下情况:

  1. 在调用 System.exit() 方法时,虚拟机将立即退出,不会执行 finally 块中的代码。System.exit() 用于强制终止Java虚拟机的运行。

  2. 如果在 finally 块中发生了无法捕获的异常(例如 StackOverflowErrorOutOfMemoryError),则 finally 块可能不会完全执行,因为这些错误通常是不可恢复的。

除了上述情况,正常情况下,finally 块中的代码应该会执行,即使在 try 块中发生异常也一样。这是用于确保资源释放或清理等操作的一种可靠方式。

10.异常注意事项

  • 运行时异常被抛出可以不处理。即不捕获也不声明抛出。
  • 如果父类抛出了多个异常,子类覆盖父类方法时,只能抛出相同的异常或者是他的子集。
  • 父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
  • 当多异常处理时,捕获处理,前边的类不能是后边类的父类
  • 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。

11.自定义异常

==写法:== 1.定义异常类 2.写继承关系 3.写构造方法

==要求==:见名知意 ==目的==:让控制台信息见名知意

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

实验代码

Test
java
import java.util.Scanner;  
  
public class Test {  
public static void main(String[] args) {  
Scanner sc = new Scanner(System.in);  
Girlfriend gf = new Girlfriend();  
  
try {  
System.out.println("请输入名字");  
String name = sc.nextLine();  
gf.setName(name);  
  
System.out.println("请输入年龄");  
String ageStr = sc.nextLine();  
int age = Integer.parseInt(ageStr);  
gf.setAge(age);  
} catch (NameFormatException e) {  
e.printStackTrace();  
//throw new NameFormatException();  
} catch (AgeOutOfRangeException e){  
e.printStackTrace();  
//throw new AgeOutOfRangeException();  
} catch (RuntimeException e){  
e.getStackTrace();  
}  
  
System.out.println(gf);  
}  
}
NameFormatException
java
public class NameFormatException extends RuntimeException{  
public NameFormatException() {  
}  
  
public NameFormatException(String message) {  
super(message);  
}  
  
}
AgeOutOfrAangeException
java
public class AgeOutOfRangeException extends RuntimeException {  
public AgeOutOfRangeException() {  
}  
  
public AgeOutOfRangeException(String message) {  
super(message);  
}  
  
}
Girlfriend
java
public class Girlfriend {  
private String name;  
private int age;  
  
public Girlfriend() {  
}  
  
public Girlfriend(String name, int age) {  
this.name = name;  
this.age = age;  
}  
  
public String getName() {  
return name;  
}  
  
public void setName(String name){  
if (name.length()>40&&name.length()<18) {  
throw new NameFormatException(name+"姓名格式错误");  
//System.out.println("名字长度不是3-10");  
}  
this.name = name;  
}  
  
public int getAge() {  
return age;  
}  
  
public void setAge(int age) {  
if (age>=18&&age<40) {  
this.age = age;  
}else  
throw new AgeOutOfRangeException(age+"年龄不属于18-40");  
//System.out.println("年龄不属于18-40");  
}  
  
@Override  
public String toString() {  
return "Girlfriend{" +  
"name='" + name + '\'' +  
", age=" + age +  
'}';  
}  
}

评论区

欢迎留言、补充或勘误。

xiaoba.blog