JavaSE--异常

Java 中,将程序执行过程中发生的不正常行为称为异常 。语法错误不是异常!!!!
学完Java的状态:
1.算术异常
System.out.println(10 / 0);
// 执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
2. 数组越界异常
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
3. 空指针异常
int[] arr = null;
System.out.println(arr.length);
// 执行结果
Exception in thread "main" java.lang.NullPointerException

 Java中的所有不正常类都继承于Throwable类。Throwable主要包括两个大类,一个是Error类,另一个是Exception类;

Error 指的是 Java 虚拟机无法解决的严重问题,比如: JVM 的内部错误、资源耗尽等 ,典型代表: StackOverflowError OutOfMemoryError ,一旦发生回力乏术。
Exception 异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception

1. 异常的分类:

异常分类分为编译时异常和运行时异常,在程序编译期间发生的异常,称为编译时异常,也称为受检查异常,在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常

编译异常要在编写时处理掉,如使用克隆方法时的抛异常。
RunTimeException 以及其子类对应的异常,都称为运行时异常。

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 以及 try-catch 捕获处理。

异常声明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("异常如果被处理了,这里的代码也可以执行");
    }
}
try 中可能会抛出多个不同的异常对象,则必须用多个 catch 来捕获 ---- 即多种异常,多次捕获
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 ,父类异常在后 catch ,否则语法错误:

可以通过一个catch捕获所有的异常,即多个异常,一次捕获(不推荐) 

在写程序时, 有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源 :网络连接、数据库连接、IO 流等, 在程序正常或者异常退出时,必须要对资源进进行回收 。另外,因为 异常会引发程序的跳转,可能 导致有些语句执行不到 finally 语句无论是否发生异常,都会被执行到

在 Java 中,“throw” 和 “throws” 有以下区别:

一、语法和作用位置不同

  1. “throw”:
    • 是一个语句,用于在方法体内抛出一个具体的异常对象。
    • 例如:throw new IllegalArgumentException("Invalid argument.");,在方法内部明确地抛出一个非法参数异常实例。
  2. “throws”:
    • 用在方法声明处,后面跟一个或多个异常类型列表,表示该方法可能抛出这些异常,而不处理它们。
    • 例如:public void someMethod() throws IOException, SQLException {... },表示这个方法可能抛出输入输出异常和 SQL 异常。

二、异常处理方式不同

  1. “throw”:
    • 当使用 “throw” 抛出异常后,程序的执行流程会立即中断,并从当前位置开始向外层代码寻找异常处理机制(如 try-catch 块)。如果找不到合适的异常处理机制,程序将终止。
      2 - “throws”:
    • 仅仅是声明方法可能抛出的异常,把异常处理的责任交给了方法的调用者。调用者可以选择在自己的代码中使用 try-catch 块来捕获和处理这些异常,或者继续使用 “throws” 声明将异常再往上一层传递。

三、使用场景不同

  1. “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 块捕获并处理异常,或者继续向上一层方法传递异常。

  1. UserNameException类:

    • 构造函数调用了父类Exception的构造函数,并将传入的message传递给父类。这样,当这个异常被抛出时,可以通过getMessage()方法获取到这个错误信息描述。
    • 例如,如果用户名不正确,可以抛出这个异常,并传入一个描述性的错误信息,如throw new UserNameException("用户名不存在")
  2. PasswordException

    • UserNameException类似,构造函数也将传入的message传递给父类Exception。用于在密码错误的情况下抛出特定的异常,并提供相应的错误信息。
    • 比如,当密码不匹配时,可以抛出PasswordException并传入错误信息,如throw new PasswordException("密码错误")
 

通过定义这些自定义异常类,可以使程序在处理用户名和密码错误时更加清晰和可维护,提高代码的可读性和错误处理的准确性。

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值