JAVA 关于异常处理

    所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制。 运行时指的是程序 已经编译通过 得到 class 文件了, 再由 JVM 执行过程中 出现的错误。而对编译错误而言,在编译过程中如果编译通过那么一定不存在编译错误。
    错误在代码中是客观存在的。 因此要让程序出现问题的时候及时告知程序员 有两种主要的方式:
1)LBYL: Look Before You Leap。即 操作之前就做充分的检查。检查完上一步之后再做下一步操作,上一步失败就不会执行下一步操作了。以进行一局游戏为例:
boolean ret = false;
ret = 登陆游戏();
if (!ret) {
 处理登陆游戏错误;
    return; }
ret = 开始匹配();
if (!ret) {
 处理匹配错误;
    return; }
ret = 游戏确认();
if (!ret) {
 处理游戏确认错误;
    return; }
ret = 选择英雄();
if (!ret) {
    处理选择英雄错误;
    return; }
ret = 载入游戏画面();
if (!ret) {
 处理载入游戏错误;
    return;
}
......

2)EAFP:It's Easier to Ask Forgiveness than Permission。即先操作,遇到问题时再处理异常的核心思想就是 EAFP 。同样以进行一局游戏为例:

try {
    登陆游戏();
    开始匹配();
    游戏确认();
    选择英雄();
    载入游戏画面();
    ...
} catch (登陆游戏异常) {
    处理登陆游戏异常;
} catch (开始匹配异常) {
 处理开始匹配异常;
} catch (游戏确认异常) {
 处理游戏确认异常;
} catch (选择英雄异常) {
 处理选择英雄异常;
} catch (载入游戏画面异常) {
 处理载入游戏画面异常; 
}......

一、捕捉异常

    基本语法如下:

try{ 
     //有可能出现异常的代码; 
}catch (异常类型 异常对象) {
     //出现异常后的处理行为;
} 
finally {
     //处理善后工作,会在最后执行;
}

• 其中 catch 和 finally 都可以根据情况选择加或者不加。

• try 中的代码按顺序执行,一旦 try 中的代码出现异常,那么 try 代码块中的程序就不会继续执行而是交给对应异常种类 catch 语句来处理,catch 中的代码执行完毕后会继续往下执行

• catch 可以有多个 。如果多个异常对应的处理方式是完全相同的,则可以在不同异常种类之间用‘ | ’连接放在一个 catch() 中。

• finally 中的语句保证一定会执行到,无论前面是否触发了异常,因此我们可以利用 finally 进行资源释放、关闭文件、多线程解锁等。finally 执行的时机是在方法返回之前,因此在 try 或者 catch 中如果有 return 会在这个 return 之前执行 finally;但是如果 finally 也存在 return 语句,那么就会执行 finally 中的 return从而不会执行到 try 中原有的 return。

※关于异常处理方式:
1. 对于比较严重的问题 ( 例如和计算钱有关 ), 应该让程序直接崩溃 防止造成更严重的后果;
2. 对于不太严重的问题 ( 大多数场景 ), 可以记录错误日志 并通过监控报警程序及时通知程序员;
3.对于可能会恢复的问题 ( 和网络相关的场景 ), 可以尝试进行重试重连
 
※关于“调用栈”:
方法之间是存在相互调用关系的, 这种调用关系我们可以用 " 调用栈 " 来描述。 JVM 中有一块内存空间称为 " 虚拟机栈" 专门存储方法之间的调用关系。 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); (会把数据写到 System.err 文件里,与 System.out 是两个独立的文件,由于二者缓冲区的刷新策略不同,会导致二者之间的执行顺序不一定严格) 的方式查看出现异常代码的调用栈。其中,e 是个形参,对应着 try 代码抛出的异常对象,通过 e 可以获取到很多有关此异常的信息。
 
※  Exception 是所有异常类的父类 因此可以用这个类型表示 捕捉所有异常 。catch 进行类型匹配的时候 不光会匹配相同类型的异常对象, 也会捕捉目标异常类型的子类 对象。
 
使用 try 负责回收资源,举个例子:
try (Scanner sc = new Scanner(System.in)) {
    int num = sc.nextInt();
    System.out.println("num = " + num);
} catch (Exception e) {
    e.printStackTrace();
}
Scanner 对象在 try ( ) 中创建, 能保证 try 执行完毕后 自动调用 Scanner close 方法。

  如果本方法中没有合适的处理异常的方式, 就会 沿着调用栈向上传递 (即向上转型)。如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理 程序就会异常终止 ( 未使用 try catch 时是一样的 )。
 

二、抛出异常

    除了 Java 内置的类会抛出一些异常之外,程序员 也可以 手动抛出 某个异常。 使用 throw 关键字 完成这个操作。并且 我们在处理异常时 通常希望知道这段代码中究竟会出现哪些可能的异常, 就可以使用 throws 关键字 把可能抛出的异常 显式的标注在方法定义的位置 从而提醒调用者注意
捕获这些异常。举个例子:
public static int divide(int x, int y) throws ArithmeticException {
    if (y == 0) {
        throw new ArithmeticException("抛出除 0 异常");
   }
    return x / y; }

三、Java异常体系

上图表示了 Java 内置的异常类之间的继承关系。 顶层类 Throwable 派生出两个重要的子类: Error Exception。其中 Error 指的是 Java 运行时 内部错误和资源耗尽错误 (JVM系统级别) 应用程序不抛出此类异常。 这种内部错误一旦出现,除了告知用户并使程序终止之外 再无能无力, 这种情况很少出现。 Exception 程序员所使用的异常类的父类。其中 Exception 有一个子类称为 RuntimeException, 这个类又派生出很多我们常见的异常类,比如:NullPointerException, IndexOutOfBoundsException 等。 Java语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为 非受查异常  所有的其他异常称为 受查异常 受查异常 必须显式进行处理 使用 try catch 包裹 起来; 方法上加上异常说明 相当于将处理动作 交给上级调用者 ,非受查异常可不必。
 

四、自定义异常类

    举个用户登录功能的例子:

//Main.java
package Exception;
import java.util.Scanner;

public class Main {
    private static String userName = "admin";
    private static String userPassword = "123456";

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.next();
        System.out.println("请输入密码:");
        String password = sc.next();
        try{
            login(name,password);
        }catch (NameException nameException){
            nameException.printStackTrace();
        }catch (PasswordException passwordException){
            passwordException.printStackTrace();
        }
    }

    public static void login(String name, String password) throws NameException, PasswordException {
        if(!userName.equals(name))
            throw new NameException("用户名输入有误");
        if(!userPassword.equals(password))
            throw new PasswordException("密码输入有误");
        System.out.println("登陆成功");
    }
}

//NameException.java
package Exception;

public class NameException extends  Exception {
    public NameException(String message) {
        super(message);
    }
}

//PasswordException.java
package Exception;

public class PasswordException extends Exception {
    public PasswordException(String message) {
        super(message);
    }
}
• 自定义异常通常会继承自 Exception 或者 RuntimeException。 继承自 Exception 的异常默认是受查异常 继承自 RuntimeException 的异常默认是非受查异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值