侧边栏壁纸
博主头像
叩钉壹刻博主等级

7分技术,3分管理,2分运气

  • 累计撰写 30 篇文章
  • 累计创建 13 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Java异常处理

鹿心肺语
2024-08-14 / 0 评论 / 0 点赞 / 21 阅读 / 8908 字

1. 异常概念

异常:是程序在执行过程中,出现的非正常情况。不加以处理,最终会导致JVM的非正常停止。

程序如果出现语法错误和逻辑错误时,是编译不通过,不会产生字节码,也不会运行。

异常触发机制:在Java中把不同的异常用不同的类表示。当发生某种异常时,就创建该类的异常对象并抛出(throw),然后可以在程序中捕获(catch)并处理。如果程序中没有捕获并处理这个异常,那么程序将终止运行。

异常解决方法

  1. 遇到错误终止程序的运行。
  2. 充分考虑各种可能发生的异常和错误,进行预防和避免。当无法避免时,应进行异常检测和处理,从而保证程序的健壮性。

2. 异常类继承体系

异常的基类java.lang.Throwable类,是Java程序执行过程中发生的异常事件对应类的根父类。

Throwable类常用方法

  • public void printStackTrace():打印异常的详细信息。包含异常的类型、原因、出现位置。
  • public String getMessage():获取异常的原因。

Throwable分类:Error和Exception,分别是java.lang.Errorjava.lang.Exception

  1. Error:表示Java虚拟机无法解决的严重问题,一般不针对其进行处理。
  2. Exception:表示因编程错误或偶然的外在因素导致的一般性问题,需要针对具体问题进行处理。否则程序出现异常时,将会停止运行。其从程序角度分为:编译时异常(受检异常)和运行时异常(非受检异常)。

常见异常

异常名分类出现时机含义
java.lang.StackOverflowErrorError运行时异常栈内存溢出
java.lang.OutOfMemoryErrorError运行时异常堆内存溢出,OOM
java.lang.ClassNotFoundExceptionException编译时异常类不存在
java.lang.FileNotFoundExceptionException
编译时异常文件不存在
java.lang.IOExceptionException编译时异常输入输出错误
java.lang.ArrayIndexOutOfBoundsExceptionException运行时异常数组下标越界
java.lang.NullPointerExceptionException运行时异常空指针
java.lang.ClassCastExceptionException
运行时异常类型转化失败
java.lang.NumberFormatExceptionException运行时异常数值格式化错误
java.lang.InputMismatchExceptionException运行时异常输入类型不匹配
java.lang.ArithmeticExceptionException
运行时异常算数错误

常见异常示例

/**  
* Exception in thread "main" java.lang.StackOverflowError  
* */
public static void main(String[] args) {  
    main(args);  
}

/**  
* -Xms10 -Xmx10
* Error occurred during initialization of VM
* Too small maximum heap
* */
public static void main(String[] args) {  
    byte[] bytes = new byte[1024 * 1024 * 1000];  
}

/**  
 * Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 
 * Index 10 out of bounds for length 10 
 * */
public static void main(String[] args) {  
    int[] arr = new int[10];  
    System.out.println(arr[10]);  
}

/**  
 * Exception in thread "main" java.lang.NullPointerException: 
 * Cannot invoke "String.toString()" because "str" is null  
 * */
public static void main(String[] args) {  
    String str = "demo";  
    str = null;  
    System.out.println(str.toString());  
}

/**  
 * Exception in thread "main" java.lang.ClassCastException: 
 * class java.lang.String cannot be cast to class java.util.Date
 * */
public static void main(String[] args) {  
	Object object = new String();  
	Date date= (Date) object;  
}

/**  
 * Exception in thread "main" java.lang.NumberFormatException: 
 * For input string: "abc"  
 */
public static void main(String[] args) {  
    String str = "abc";  
    int number = Integer.parseInt(str);  
    System.out.println(number);  
}

/**  
 * Exception in thread "main" java.util.InputMismatchException 
 * */
public static void main(String[] args) {  
    Scanner scanner = new Scanner(System.in);  
    int number = scanner.nextInt();  
    System.out.println(number);  
}

/**  
 * Exception in thread "main" java.lang.ArithmeticException: 
 * / by zero 
 * */
public static void main(String[] args) {  
    System.out.println(10 / 0);  
}

根据异常出现时机分为:编译时异常和运行时异常

  • 编译时异常(Checked异常,受检异常):在代码编译阶段,编译器能明确警示当前代码可能存在的异常,并期望针对其做出处理。
  • 运行时异常(Runtine异常,Unchecked异常,非受检异常):在代码运行后,根据实际情况发生的异常。通常这类异常是由代码编写不当引起的。java.lang.RuntimeException,运行异常标识。

3. 异常处理 try-catch-finally

try-catch-finally,也称抓抛模型。其过程分为两阶段:

  1. “抛”:在程序在执行过程中,当出现异常时,就会在出现异常的代码处,生成对应异常类的对象并抛出。在抛出后,程序就不在执行其后序代码。
  2. “抓”:针对“抛”中的异常,进行捕获并处理,此时就称为抓。当对异常进行处理后,程序可以正常运行。

try-catch-finally基本的代码段结构

try {  
    // todo... 可能产生异常的代码  
} catch (异常类型 e) {  
    // todo... 异常处理的代码  
} catch (异常类型 e) {  
    // todo... 异常处理的代码  
} finally {  
    // todo... 无论是否发生异常都会执行的代码  
}

try-catch-finally的代码段执行流程

  1. 当程序正常运行,执行到try代码块时,如果try代码块出现异常,那么会生成一个对应异常的对象并抛出。
  2. catch语句块进行逐一匹配抛出的异常,当匹配成功后,进入该语句块内处理,也称捕获成功。如果当前没有一个catch语句块匹配成功,即捕获失败,则会向其调用者抛出异常。
  3. 当try代码块、catch代码块执行完后或方法返回前,finally代码块则会执行。

try代码块注意事项:在try中声明的变量,作用域仅在try中,在其它地方无法使用。

catch代码块注意事项:如果多个异常类型满足父类的关系,则必须将子类声明在父类结构的上面,否则,编译报错。

对于异常处理方式

  • 编译时异常:在异常出现时就需要处理,否则程序将编译不通过。
  • 运行时异常:在程序执行过程中,出现异常,根据异常提示调整。

finally代码块注意事项:当try代码块、catch代码块执行完后或方法返回前,finally代码块则会执行。catch代码块和finally代码块是可选的,但finally不能单独使用。

当一些资源(输入流、输出流、数据库连接、Socket连接等资源)在使用完以后,必须显式的进行关闭操作,否则,GC不会自动回收这些资源,进而导致内存泄漏。
为保证这些资源,不论是正常还是异常情况都能被关闭,必须将这些操作声明到finally中。

final、finally、finalize三者区别

  • final:用来修饰类、方法和变量,表示不可变或不可重写的特性。
  • finally:是try-catch-finally语句的一部分,确保无论是否发生异常,某些代码都会执行。
  • finalize:是一个在Object类中定义的方法,可以被子类重写,在对象被回收前执行清理操作,但垃圾收集器的调用时机是不确定的,所以finalize方法的执行时机也是不确定的,不推荐使用。

4. 异常处理 throws

使用格式:在方法处声明,throws 异常类型1, 异常类型2,....

throws基本的代码段结构

方法属性 返回类型 方法名(参数列表) throws 异常类型1, 异常类型2,....{  
	 // todo... 执行代码块
}

throws处理

  • 从编译角度看,throws通过向上抛出的方式,解决了自身的异常。
  • 从异常处理角度看,throws是将自身异常抛给了调用者,并没有解决异常。

throws在父子类重写的注意事项

  • 子类重写方法throws的异常类型必须为父类方法throws异常类型的子类
  • 如果父类方法没有throws异常类型,子类可以使用RuntimeException异常类型

throws 和 try-catch-finally 异常处理场景

  • 资源必须执行时,使用try-catch-finally处理
  • 父子类重写时,子类可以throws父类处理过的异常类型和子异常类型;当出现父类没有处理的异常时,子类需要使用try-catch-finally自行处理。
  • 当方法调用存在递进关系时,被调用的方法一般采用throws处理异常,当前方法则采用try-catch-finally处理异常。

5. 手动抛出异常 throw

在实际操作中,为了更加的符合业务场景,基于Java自有异常外,手动抛出(throw)一个指定类型的异常。

使用格式:在方法内部,throw 异常类的对象

在手动抛出异常throw的处理方式

  1. 通过try-catch-finally解决异常。
  2. 通过throws向上层抛出异常,由调用者考虑处理异常。

throw异常,如果是运行时异常,一般可不处理;但如果是编译时异常,必须通过上述方式处理。

throw 和 throws的区别

  • throw使用在方法内,是手动的抛出一个异常,创建异常对象。
  • throws使用在方法的声明处,表明向上层抛出异常,处理异常的一种方式。

6. 自定义异常

自定义异常类步骤

  1. 继承当前异常类体系。一般继承于RuntimeException / Exception
  2. 提供对应重载的构造器。
  3. 声明一个全局常量,static final long serivalVersionUID;

自定义异常类的示例

public class BelowZeroException extends RuntimeException {  
    static final long serialVersionUID = 5766939L;  
  
    public BelowZeroException() {  
        super();  
    }  
  
    public BelowZeroException(String message) {  
        super(message);  
    }  
}

使用自定义异常类注意事项

  1. 在使用自定义异常时,是手动抛出的,throw 自定义异常对象
  2. 如果该自定义异常是运行时异常,一般可选择不处理;但如果是编译时异常,必须要考虑如何处理此异常。

需要自定义异常类的原因:为了能够直接通过异常名称和原因定位问题,更加符合实际业务场景。

0

评论区