在 Java 中,将程序执行过程中发生的不正常行为称为异常 。语法错误不是异常!!!!
![](https://i-blog.csdnimg.cn/direct/4f73b1d5bc0a4288b1321c714674d5f3.png)
System.out.println(10 / 0);
// 执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
int[] arr = null; System.out.println(arr.length); // 执行结果 Exception in thread "main" java.lang.NullPointerException
Java中的所有不正常类都继承于Throwable类。Throwable主要包括两个大类,一个是Error类,另一个是Exception类;
1. 异常的分类:
异常分类分为编译时异常和运行时异常,在程序编译期间发生的异常,称为编译时异常,也称为受检查异常,在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常
2. 异常的处理:
LBYL:事前防御。我们也接触过,就是每个流程前会进行错误检查,这种方法缺点也很明显:正常流程和错误处理流程代码混在一起 , 代码整体显的比较混乱。
EAFP:就是先操作, 遇到问题再处理,事后认错。
try {
启动洗衣机();
洗衣机自洁();
衣服投放();
选择模式();
开始洗衣();
...
} catch (启动洗衣机异常) {
处理启动洗衣机异常;
} catch (洗衣机自洁异常) {
处理洗衣机自洁异常;
} catch (衣服投放异常) {
处理衣服投放异常;
} catch (选择模式异常) {
处理选择模式异常;
} catch (开始洗衣异常) {
处理开始洗衣异常;
}
......
在 Java 中, 异常处理主要的 5 个关键字: throw 、 try 、 catch 、 final 、 throws 。
3. 异常的抛出
public int[] asde(int[] array,int index){
if(array==null) throw new NullPointerException("array is null");
if(index<0&&index>array.length) throw new IndexOutOfBoundsException("index out of bounds");
return array;
}
- throw必须写在方法体内部
- 抛出的对象必须是Exception 或者 Exception 的子类对象
- 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
- 异常一旦抛出,其后的代码就不会执行
4. 异常的具体处理方式:
异常声明throws:
throws 将异常抛给方法的调用者来处理。如下使用方法,只举例
语法格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}
- 声明的异常必须是 Exception 或者 Exception 的子类
- 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
try-catch捕获并处理:
语法格式:
try{
//可能出现异常的代码
}catch(要捕获的异常类型 e){
// 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时,或者是try中抛出异常的基类时,就会被捕获到
}[catch(异常类型 e){
// 对异常进行处理
}finally{
// 此处代码一定会被执行到
}]
1. [] 中表示可选项,可以添加,也可以不用添加2. try 中的代码可能会抛出异常,也可能不会
class Config {
File file;
public void openConfig(String filename) throws FileNotFoundException {
if(!filename.equals("config.ini")){
throw new FileNotFoundException("配置文件名字不对");
}
// 打开文件
}
public void readConfig(){
}
public static void main(String[] args) {
Config config = new Config();
try {
config.openConfig("config.ini");
System.out.println("文件打开成功");
} catch (IOException e) {
// 异常的处理方式
//System.out.println(e.getMessage()); // 只打印异常信息
//System.out.println(e); // 打印异常类型:异常信息
e.printStackTrace(); // 打印信息最全面
}
// 一旦异常被捕获处理了,此处的代码会执行
System.out.println("异常如果被处理了,这里的代码也可以执行");
}
}
public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
System.out.println("123");
// arr = null;
System.out.println(arr[100]);
System.out.println("456");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组下标越界异常");
e.printStackTrace();
} catch (NullPointerException e) {
System.out.println("空指针异常");
e.printStackTrace();
}
System.out.println("after try catch");
}
//如果多个异常的处理方式是完全相同, 也可以写成这样:
//catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
//...
//}
可以通过一个catch捕获所有的异常,即多个异常,一次捕获(不推荐)
在写程序时, 有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源 :网络连接、数据库连接、IO 流等, 在程序正常或者异常退出时,必须要对资源进进行回收 。另外,因为 异常会引发程序的跳转,可能 导致有些语句执行不到 , finally 语句无论是否发生异常,都会被执行到
在 Java 中,“throw” 和 “throws” 有以下区别:
一、语法和作用位置不同
- “throw”:
- 是一个语句,用于在方法体内抛出一个具体的异常对象。
- 例如:
throw new IllegalArgumentException("Invalid argument.");
,在方法内部明确地抛出一个非法参数异常实例。- “throws”:
- 用在方法声明处,后面跟一个或多个异常类型列表,表示该方法可能抛出这些异常,而不处理它们。
- 例如:
public void someMethod() throws IOException, SQLException {... }
,表示这个方法可能抛出输入输出异常和 SQL 异常。二、异常处理方式不同
- “throw”:
- 当使用 “throw” 抛出异常后,程序的执行流程会立即中断,并从当前位置开始向外层代码寻找异常处理机制(如 try-catch 块)。如果找不到合适的异常处理机制,程序将终止。
2 - “throws”:- 仅仅是声明方法可能抛出的异常,把异常处理的责任交给了方法的调用者。调用者可以选择在自己的代码中使用 try-catch 块来捕获和处理这些异常,或者继续使用 “throws” 声明将异常再往上一层传递。
三、使用场景不同
- “throw”:
- 通常在方法内部,当特定的错误条件发生时,主动抛出一个具体的异常来通知调用者出现了问题。比如在进行参数校验时,如果参数不符合要求,可以抛出一个相应的异常。
- 例如,在一个计算方法中,如果传入的参数为负数,可能抛出一个自定义的业务异常:
finally一定会执行吗?
一、一般情况下一定会执行
当 try 块中代码正常执行完毕,或者 try 块中出现异常被 catch 块捕获并处理后,finally 块中的语句都会被执行。例如:
二、特殊情况下不一定会执行
当程序在 try 或 catch 块中执行了
System.exit(0)
语句来终止 JVM 时,finally 块不会被执行
5.异常的处理流程
public static void main(String[] args) {
try {
ASD();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
System.out.println("after try catch");
}
public static void ASD() {
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
}
// 直接结果
java.lang.ArrayIndexOutOfBoundsException: Index 100 out of bounds for length 3
at main1.ASD(Main.java:165)
at main1.main(Main.java:157)
after try catch
- 程序先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递.
- 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
6. 自定义异常类
Java 中虽然已经内置了丰富的异常类 , 但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我们实际情况的异常结构.
1. 自定义异常类,然后继承自Exception 或者 RunTimeException
2. 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因
class UserNameException extends Exception {
public UserNameException(String message) {//用户名错误异常
super(message);
}
}
class PasswordException extends Exception {//登录密码错误异常
public PasswordException(String message) {
super(message);
}
}
使用:
public static void loginInfo(String userName, String password)
throws UserNameException,PasswordException{
if (!userName.equals(userName)) {
throw new UserNameException("用户名错误!");
}
if (!password.equals(password)) {
throw new PasswordException("用户名错误!");
}
System.out.println("登陆成功");
}
public static void main(String[] args) {
try {
loginInfo("admin", "123456");
} catch (UserNameException e) {
e.printStackTrace();
} catch (PasswordException e) {
e.printStackTrace();
}
}
}
在 Java 中,如果一个方法内部抛出了受检查异常(checked exception),而方法声明中没有使用 throws
关键字声明可能抛出这些异常,那么在编译时就会报错。
在给出的代码中,loginInfo
方法内部可能抛出 UserNameException
和 PasswordException
,这两个异常都是自定义的受检查异常。如果不使用 throws
语句声明这个方法可能抛出这些异常,那么在调用这个方法的地方(比如 main
方法),编译器会认为这个方法不会抛出任何异常,但是当实际执行过程中出现了异常时,就会导致编译错误。
使用 throws
关键字声明方法可能抛出的异常,是一种告知调用者这个方法可能出现问题的方式,调用者可以根据这个信息决定如何处理这些异常,比如使用 try-catch
块捕获并处理异常,或者继续向上一层方法传递异常。
UserNameException
类:
- 构造函数调用了父类
Exception
的构造函数,并将传入的message
传递给父类。这样,当这个异常被抛出时,可以通过getMessage()
方法获取到这个错误信息描述。- 例如,如果用户名不正确,可以抛出这个异常,并传入一个描述性的错误信息,如
throw new UserNameException("用户名不存在")
。
PasswordException
类:
- 与
UserNameException
类似,构造函数也将传入的message
传递给父类Exception
。用于在密码错误的情况下抛出特定的异常,并提供相应的错误信息。- 比如,当密码不匹配时,可以抛出
PasswordException
并传入错误信息,如throw new PasswordException("密码错误")
。通过定义这些自定义异常类,可以使程序在处理用户名和密码错误时更加清晰和可维护,提高代码的可读性和错误处理的准确性。