在定义方法时,首先需要先对参数数据进行合法判断
数据若不合法,使用抛出异常的方式来告诉调用者,传递合法的数据进来
在方法内使用 throw 抛出指定异常对象,throw new XxxException(“异常产生原因”)
- 创建的是运行时异常,可以选择不处理,由 JVM 在运行时抛出
- 创建的是编译时异常,必须显示处理,要么 throws,要么 try-catch
/**
* 获取数组指定位置元素
* @param arr 数组名称
* @param index 数组索引
* @return
*/
int getElement(int[] arr, int index) {
// 数据校验
if (arr == null) {
throw new NullPointerException("数组索引为null");
}
if (index < 0 || index > arr.length - 1) {
throw new ArrayIndexOutOfBoundsException("数组索引越界");
}
// 具体操作
return arr[index];
}
一、throws关键字
在方法签名中声明抛出异常类型,交给方法的调用者处理,最终交给 JVM 中断处理
方法内抛出的多个异常对象有父子类关系,则在方法签名中只声明父类异常类型即可
public class Demo {
public static void main(String[] args) throws IOException {
readFile("e:\\a.txt");
}
/**
* 读取文件
* IO操作,编译时异常
* java.io.FileNotFoundException extends java.io.IOException
* @param filePath
* @throws IOException
*/
static void readFile(String filePath) throws IOException { // 声明父类异常类型即可
if (!filePath.endsWith(".txt")) {
throw new IOException("文件后缀名错误");
}
if (!filePath.equals("e:\\a.txt")) {
throw new FileNotFoundException("文件路径错误");
}
}
}
二、try-catch代码块
多个异常分别捕获分别处理,先捕获子类异常类型,再捕获父类异常类型,不然报错
Throwable类定义一些查看异常信息的方法
-
public String getMessage():获取异常的描述信息【原因】
-
public String toString():获取异常的类型、描述信息(不用) 【内容、原因】
-
public void printStackTrace(): 打印异常的跟踪栈信息【内容、原因、位置】
JVM 打印异常对象,默认使用printStackTrace方法,异常信息最全面
public class Demo {
public static void main(String[] args) {
try {
// 抛出异常的代码
readFile("e:\\a.tt");
} catch (FileNotFoundException e) {
// 异常的处理逻辑
// 工作中,通常将异常信息记录到日志中
} catch (IOException e) {
// System.out.println(e.getMessage()); // 文件后缀名错误
// System.out.println(e.toString()); // java.io.IOException: 文件后缀名错误
// System.out.println(e); // java.io.IOException: 文件后缀名错误
e.printStackTrace();
}
System.out.println("后续代码...");
}
/**
* 读取文件
* IO操作,编译时异常
* java.io.FileNotFoundException extends java.io.IOException
* @param filePath
* @throws IOException
*/
static void readFile(String filePath) throws IOException, FileNotFoundException {
if (!filePath.endsWith(".txt")) {
throw new IOException("文件后缀名错误");
}
if (!filePath.equals("e:\\a.txt")) {
throw new FileNotFoundException("文件路径错误");
}
}
}
finally 不能单独使用,必须和 try 一起使用
无论 try 是否出现异常,finally 的内容一定执行,两种特殊情况
- try-catch 语句中执行 return 语句,finally 子语句仍然执行
- try-catch 语可中执行 System.exit(0) ,则不执行 finally 子语句,及其后的所有语句
public class Test {
/**
* return 退出方法
* System.exit(0) 退成程序
*/
public static void main(String[] args) {
try {
method(); // 发生异常,转向catch
// return; // 从这开始没有机会执行 // How are you? Fine,thanks. 后续代码...
// System.exit(0); // How are you? Fine,thanks. 后续代码...
} catch (IOException e) {
System.out.println("How are you?");
// return; // How are you? Fine,thanks.
System.exit(0); // How are you?
} finally {
System.out.println("Fine,thanks.");
}
System.out.println("后续代码...");
}
public static void method() throws IOException {
throw new IOException();
}
}
当 try 块和 catch 块中都有 return 语句时,需要注意一些特殊情况
- 如果 try 块中的代码正常执行并且没有抛出异常,那么方法会返回 1
- 如果 try 块中的代码抛出了异常并被 catch 块捕获,那么方法会返回 2
- 如果 finally 块中有 return 语句,无论 try 或 catch 块中是否有 return 语句,方法都会返回 finally 块中的值
- 如果 return 语句返回的是一个对象或数组,那么在 finally 块中对这个对象或数组的修改可能会影响返回的值
- 对于基本数据类型,finally 块中的修改不会影响 try 或 catch 块中的 return 值,基本数据类型是按值传递的
public class ReturnInTryCatch {
public int method() {
try {
// 假设这里有一些可能会抛出异常的代码
// ...
// 正常的返回语句
return 1;
} catch (Exception e) {
// 异常处理代码
// ...
// 在catch块中的返回语句
return 2;
} finally {
// 无论是否发生异常,finally块中的代码都会执行
// 如果其中有return语句,它会覆盖try或catch块中的return语句
// 通常被认为是一种不好的编程实践,代码的逻辑变得难以理解和维护
}
// System.out.println("后续代码..."); // 标红报错 Unreachable statement
}
}