Exception(异常)

!!! note 目录

异常

一、异常的概述

1.1 异常的概念&作用。

  • 异常(Exception)是程序执行过程中出现的非正常情况,它们表示程序在运行时遇到了某种问题。
    Java通过异常处理机制来处理这些问题,以保证程序的健壮性和可维护性。

1.2 异常的存在方式。

  • 在Java中,异常以类的形式存在,通过继承Throwable类来定义。异常分为Error、Exception两大类,其中Exception又分为受检异常和非受检异常。
    通过异常处理机制,Java程序可以捕获和处理运行时的错误,增强了程序的健壮性和可维护性。
  • 当异常发生时,通常是通过创建异常类的对象(使用new关键字)来表示异常的情况。这些异常对象包含了异常的详细信息,例如错误消息和堆栈跟踪信息。

1.3 异常的继承结构

二、 Java中的异常机制

2.1 异常在程序中如何发生?

1
2
3
4
5
6
7
8
@Test
public void testException(){
//异常的发生要经历两个阶段
//第一个阶段:创建异常对象
NullPointerException npe = new NullPointerException();
//第二个阶段:让异常发生(手动抛出异常)
throw npe;
}
1
2
3
4
public void testException(){
//合并成一步
throw new ClassCastException();
}

2.2 自定义异常

自定义异常两步:
第一步:编写类继承RuntimeException或者Exception
第二步:提供两个构造方法,一个无参数,一个带有String参数的,并且在构造方法中调用super(String)

1
2
3
4
5
6
public class IllegalNameException extends Exception{
public IllegalNameException(){}
public IllegalNameException(String message){
super(message);
}
}

三、异常的处理

3.1 使用throws关键字声明异常以抛出

当方法可能抛出异常但不在方法内部处理,而是将其抛给调用者处理时,使用throws关键字声明异常。这种方式适用于方法自身不适合处理异常,而是希望调用者决定如何处理。

方法本身不适合处理异常,而是将其抛给调用者处理。
希望调用者明确知道方法可能抛出的异常,并强制其处理。

示例代码:在这个方法中,需要让调用者知道发生异常(姓名,年龄不合规,自定义抛出异常。)

1
2
3
4
5
6
7
8
9
10
public void save(String name, int age) throws IllegalNameException, IllegalAgeException{
System.out.println("用户["+name+"]的信息正在保存....");
if(name.length() < 6 || name.length() > 12){
throw new IllegalNameException();
}
if(age < 18){
throw new IllegalAgeException();
}
System.out.println("用户["+name+"]的信息保存成功!");
}

3.2 使用try-catch块捕获异常

try-catch块是直接在方法内部捕获和处理异常的方式。
语法格式:

1
2
3
4
5
6
7
8
try {
//需要执行的程序,这里的程序有可能出现问题。
}catch(异常类型1 变量名){
//捕捉到异常,进行异常处理。
}catch(异常类型2 变量名){

} . . .
//后续代码可以执行。

注意:

  1. catch语句可以看作是分支,try catch语句中,最多只有一个catch分支执行。
  2. catch可以写多个,但必须异常从小到大。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CustomExceptions {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("欢迎使用本系统,先进行用户的注册:");
System.out.print("请输入用户名:");
String name = scanner.next();
System.out.print("请输入年龄:");
int age = scanner.nextInt();

// 注册
UserService userService = new UserService();

try {
userService.register(name, age);
} catch (IllegalNameException e) {
System.out.println("用户名不合法");
} catch (IllegalAgeException e) {
System.out.println("年龄不合法");
}
System.out.println("main over!");
}
}

变量e存储的是对象地址

四、多异常捕获

在 Java 7 中,单个 catch 块可以捕获多种类型的异常,使用竖线 (|) 分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CustomExceptions {
public static void main(String[] args){
// 注册
UserService userService = new UserService();

try {
userService.register(name, age);
} catch (IllegalNameException | IllegalAgeException e) {
System.out.println("用户名或者年龄不合法");
}
System.out.println("main over!");
}
}

注意:多异常捕捉只有最后一个有变量,其余只写类型。

五、异常的常用方法。

5.1 getMessage()方法

getMessage() 方法是 Throwable 类(Exception 类和 Error 类的父类)中的一个方法,
用于获取创建异常对象时传递的详细消息。这个消息通常在异常被构造时作为参数传递给异常的构造方法。
这个消息有助于在捕获异常时了解导致异常的原因。

1
2
3
4
5
6
7
8
9
package com.camellia.exception;

public class IllegalAgeException extends Exception{
public IllegalAgeException(){}
public IllegalAgeException(String message){
super(message); //要想错误信息被打印,需要在构造方法调用父类构造方法。
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.camellia.exception;

public class UserServices {
User user;

public UserServices() {}

public UserServices(User user) {
this.user = user;
}

/**
* 用户注册异常处理
* @throws IllegalNameException 用户名异常
* @throws IllegalAgeException 年龄异常
*/
public void register() throws IllegalNameException, IllegalAgeException {
if(user.getName().length()>12||user.getName().length()<6) throw new IllegalNameException("无效名字异常,名字应在【6-12】位");
if(user.getAge()<0||user.getAge()>130) throw new IllegalAgeException("无效年龄异常,年龄应在【0-130】");
System.out.println(user.toString());
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.camellia.exception;

public class UserMain {
public static void main(String[] args) {
User user = new User("camellia",-10);
UserServices userService = new UserServices(user);
try {
userService.register();
} catch (IllegalNameException e) {
System.out.println("Illegal name: " + e.getMessage());
} catch (IllegalAgeException e) {
System.out.println("Illegal age: " + e.getMessage());
}
}
}

5.2 printStackTrace方法

在 Java 中,printStackTrace 方法是 Throwable 类(Exception 类和Error类的父类)的一部分,用于打印异常的堆栈跟踪信息。
堆栈跟踪显示了异常发生时调用堆栈中的方法调用顺序,帮助开发者了解异常的来源及其传播路径,适用于开发人员调试程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.camellia.exception;

public class UserMain {
public static void main(String[] args) {
User user = new User("c",10);
UserServices userService = new UserServices(user);
try {
userService.register();
} catch (IllegalNameException e) {
e.printStackTrace(); //打印异常追踪信息,是个栈信息。
} catch (IllegalAgeException e) {
System.out.println("Illegal age: " + e.getMessage());
}
}
}

六、finally语句块

finally 语句块是 Java 异常处理机制的一部分,用于确保某些代码在异常处理过程中始终被执行,不管是否发生异常。
它通常与trycatch 语句块一起使用(至少和try一起),以保证在 try 语句块中进行的资源释放或清理操作一定会被执行。

6.1 用于资源释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.FileInputStream;
import java.io.IOException;

public class FinallyResourceExample {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("example.txt");
// 读取文件的代码
} catch (IOException e) {
System.out.println("IOException caught: " + e);
} finally {
if (fis != null) {
try {
fis.close(); // 关闭文件流
} catch (IOException e) {
System.out.println("Error closing file: " + e);
}
}
System.out.println("Finally block executed");
}
}
}

当然,只是开发中常用来关闭资源。

七、finally 块与 return 语句

如果trycatch块中有return语句,finally 块仍然会在方法返回之前被执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class FinallyReturnExample {
public static void main(String[] args) {
System.out.println(methodWithFinally());
}

public static String methodWithFinally() {
try {
return "Return from try";
} finally {
System.out.println("Finally block executed");
}
System.out.println("但是这个代码不能执行了。"); //不可到达代码。
}
}
//Out:
//Finally block executed
//Return from try

如果使用exit退出JVM,则finally代码无法执行

1
2
3
4
5
6
7
8
9
10
11
public class FinallyWithExitExample {
public static void main(String[] args) {
try {
System.out.println("Inside try block");
System.exit(0); // 终止 JVM
} finally {
System.out.println("Finally block executed"); // 这行不会被执行
}
}
}

在 Java 中,当一个 try 块中包含 return 语句,并且紧跟其后的 finally 块中也修改了返回的变量,最终返回的值会是 try 块中计算出的值,
而不是 finally 块中修改后的值。这是因为 return 语句会在 try 块中首先确定返回值,
然后在执行 finally 块中的代码,但 finally 块中的修改不会影响已经确定的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FinallyReturnExample {
public static void main(String[] args) {
System.out.println(methodWithFinally());
}

public static String methodWithFinally() {
int i=100;
try {
return i;
} finally {
i++;
}
}
}

注意:子类继承父类不能抛出更多的异常。