1、什么是异常
异常(Exception)是指程序在运行过程中出现的意外情况或错误。Java提供了异常处理机制,可以捕获和处理异常,以保证程序的稳定性和可靠性。
Java中的异常分为两种类型:可检查异常 和 不检查异常。
- 可检查异常:这些异常在编译时会被检查,必须显示处理,否则程序无法通过编译。这种异常通常表示程序运行中的预期情况,需要在代码中显式地处理它们,或者通过声明抛出来传递给上层调用者进行处理。常见的可检查异常包括
IOException
、SQLException
等。 - 不检查异常(运行时异常):这些异常在编译时不会被检查,可以选择处理或者不处理。这种异常通常表示程序中的错误情况或者逻辑错误。不检查异常分为
RuntimeException
及其子类,这些异常通常由程序错误导致,如数组索引越界、空指针引用等。常见的不检查异常包括NullPointerException
(空指针异常)、ArrayIndexOutOfBoundsException
(数组越界异常)、ClassCastException
(类转换异常)、ArithmeticException
(算术异常)等。
此外还有Error:Java虚拟机无法解决的严重问题。
2、异常体系结构
在Java中,异常体系结构是以类的继承关系构建的层次结构,其中是所有异常的根类。java.lang.Throwable
异常体系结构主要包括以下几个关键类和接口:
java.lang.Throwable
:Throwable
是所有异常类的基类。它有两个子类:java.lang.Exception
和java.lang.Error
。Throwable
类提供了获取异常信息的方法,如getMessage()
、printStackTrace()
等。java.lang.Exception
:Exception
类是可检查异常的基类,它是绝大多数常见异常的父类。Exception
可以分为两个子类:可检查异常(Checked Exception)和运行时异常(RuntimeException)。java.lang.Error
:Error
类代表程序运行时的严重问题,一般由虚拟机抛出并自动处理。通常,程序无法处理这些错误,例如OutOfMemoryError
、StackOverflowError
等。java.lang.RuntimeException
:RuntimeException
是运行时异常的基类,它是一种特殊类型的不检查异常。它的子类包括由程序错误导致的异常,如、,以及一些其他的运行时异常。NullPointerException
、ArrayIndexOutOfBoundsException
以及一些其他的运行时异常。
除了这些基本类之外,Java还提供了一些专门处理特定异常情况的类和接口:
java.lang.Exception
有一些重要的直接子类,如IOException
、SQLException
等,它们用于处理与输入输出、数据库操作等相关的异常。java.lang.Throwable
的重要接口是java.lang.Throwable.Cause
,它允许异常持有其他异常作为原因,以形成异常链。
3、异常处理
在Java中,异常处理是一种机制,用于处理在程序执行过程中可能发生的异常情况。异常处理机制的主要目的是在异常发生时提供一种结构化的方式来处理异常,以避免程序的崩溃或不可预测的行为。Java的异常处理机制基于以下几个关键词和概念:
- try-catch块:try-catch块是用于捕获和处理异常的基本结构。在try块中,我们放置可能会引发异常的代码。如果在try块中的任何位置发生异常,代码的控制流将立即转移到匹配异常类型的catch块,并执行其中的异常处理代码。
例如:
try {
// 可能引发异常的代码
// ...
} catch (ExceptionType1 exception1) {
// 处理 ExceptionType1 类型的异常
// ...
} catch (ExceptionType2 exception2) {
// 处理 ExceptionType2 类型的异常
// ...
}
在catch块中,我们可以编写异常处理代码,针对不同类型的异常进行不同的处理逻辑。
- finally 块:finally 块是一个可选的块,在异常处理过程中起到清理和资源释放的作用。不管是否发生异常,finally 块中的代码始终会被执行。常用于关闭打开的资源,如文件、数据库连接等。
例如:
try {
// 可能引发异常的代码
// ...
} catch (ExceptionType exception) {
// 处理异常
// ...
} finally {
// 清理和释放资源的代码
// ...
}
- throw语句:使用throw语句可以在代码中显式地引发异常。通过throw语句,我们可以自定义异常的抛出并指定异常类型。
例如:
void divide(int dividend, int divisor) throws ArithmeticException {
if (divisor == 0) {
throw new ArithmeticException("Divisor cannot be zero");
}
// 执行除法操作
// ...
}
在上述示例中,如果传入的除数为零,就会抛出一个新的ArithmeticException异常。这样,调用者可以捕获并处理该异常。
- throws关键字:使用throws关键字可以在方法声明中指定可能抛出的异常。这样做的效果是,方法的调用者在调用这个方法时必须要么捕获这些异常要么再次将这些异常向上抛出。
例如:
void readFile(String fileName) throws IOException {
// 读取文件的操作
// ...
}
在上述示例中,readFile方法声明可能会抛出IOException异常,调用该方法的代码需要处理或者再次声明抛出这个异常。
IDEA快捷键:选中代码块,然后按Ctrl+Alt+T,选择try/catch/finally
4、自定义异常
自定义异常类通常继承自Exception类或其子类,并可以添加额外的属性和方法。以下是创建自定义异常类的一般步骤:
- 创建一个类,并将其扩展为已存在的异常类(如Exception、RuntimeException等)或它们的子类。通常,命名约定是在异常类名称的末尾添加“Exception”,以表示它是一个异常类。
ublic class CustomException extends Exception {
// 添加额外的属性和方法
}
- 可以选择添加构造函数,以便在创建异常对象时进行初始化并提供有用的信息。
public class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
// 添加额外的属性和方法
}
- 可以进一步扩展自定义异常类,以满足特定的异常需求。可以添加任意数量的属性和方法,以及自定义逻辑。
public class CustomException extends Exception {
private int errorCode;
public CustomException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
// 添加额外的方法和逻辑
}
使用自定义异常时,可以像处理其他异常一样使用try-catch块来捕获和处理它们。例如:
try {
// 可能引发自定义异常的代码
throw new CustomException("Something went wrong.", 500);
} catch (CustomException e) {
// 处理自定义异常的逻辑
System.out.println("Custom Exception: " + e.getMessage());
System.out.println("Error Code: " + e.getErrorCode());
}
eg:
package com.oop.test;
public class Girl {
private String name;
private int age;
public Girl() {
}
public Girl(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
int len = name.length();
if (len<=10 && len>=3){
this.name = name;
}
else {
throw new NameFormatException("姓名应由3~10个字符构成");
}
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
if (age<18 || age>25){
throw new AgeOuterException("年龄应在18~25之间");
}
else{
this.age = age;
}
}
public String toString() {
return "Girl{name = " + name + ", age = " + age + "}";
}
}
package com.oop.test;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
/*需求:
键盘录入心仪女生的姓名和年龄;
姓名长度在 3-10 之间;
年龄范围在 18-25 岁之间;
超出这个范围是异常数据不能赋值,需要重新录入,一直录入到正确为止。
提示:
需要考虑用户在键盘录入时所有的情况;
比如: 录入年龄超出范围时,录入年龄时录入了 abc 等情况。*/
//1.创建键盘录入的对象
Scanner sc = new Scanner(System.in);
//创建对象心仪女生
Girl girl = new Girl();
while (true) {
try {
//3.接收女生姓名
System.out.print("请输入心仪女生的姓名: ");
String name = sc.nextLine();
girl.setName(name);
break;
} catch (NameFormatException e) {
System.out.println("姓名由3~10个字符构成,请重新输入");
continue;
}
}
while(true){
try {
//4.接收女朋友的年龄
System.out.print("请输入心仪女生的年龄: ");
String ageStr = sc.nextLine();
int age = Integer.parseInt(ageStr);
girl.setAge(age);
break;
} catch (NumberFormatException e) {
System.out.println("年龄应为数字,请重新输入");
continue;
}catch (AgeOuterException e) {
System.out.println("年龄在18~25之间,请重新输入");
continue;
}
}
//5.打印
System.out.println(girl);
}
}
package com.oop.test;
public class NameFormatException extends RuntimeException{
//NameFormat: 当前异常的名字,表示姓名格式化问题
//Exception: 表示当前类是一个异常类
//编译时异常继承Exception
//运行时异常继承RuntimeException
public NameFormatException() {
}
public NameFormatException(String message) {
super(message);
}
}
package com.oop.test;
public class AgeOuterException extends RuntimeException{
public AgeOuterException() {
}
public AgeOuterException(String message) {
super(message);
}
}
请输入心仪女生的姓名: 云韵
姓名由3~10个字符构成,请重新输入
请输入心仪女生的姓名: 鞠婧祎
请输入心仪女生的年龄: 27
年龄在18~25之间,请重新输入
请输入心仪女生的年龄: 25
Girl{name = 鞠婧祎, age = 25}
进程已结束,退出代码0