防御式编程
两种主要方式:
LBYL:Look Before You Leap. 在操作之前就做充分的检查。
EAFP:It’s Easier to Ask Forgiveness than Permission."事后获取原谅比事前获取许可更容易."也就是先操作,遇到问题再处理。
在Java中异常的核心思想是EAFP。
LBYL风格的代码 :
//假设是一个登陆游戏
boolean ret=false;
if(!ret){
//处理登陆游戏错误
return;
}
if(){
//......
}
这种通过if语句处理异常的方法通常在C++中使用。
EAFP风格的代码:
private static String username="byte";
private static String password="12345";
public static void main(String[] args) {
int count=0;
Scanner scanner=new Scanner(System.in);
String u=scanner.next();
String p=scanner.next();
try{
login(u,p);
}catch (UserError userError){
userError.printStackTrace();
}catch (PasswordError passwordError){
passwordError.printStackTrace();
}
}
EAFP风格是通过try-catch捕获异常。
异常的基本用法
基本语法:
try{
可能出现异常的代码块;
}catch(异常类型 异常对象){
处理异常;
}finall{
异常的出口;
}
- try中放的是可能出现异常的代码块。
- catch中放的是出现异常时如何处理异常。
- finally中的代码块是用于处理善后工作,会在最后执行。
- 其中catch和finally可以选择加上也可以选择不加上。
代码示例:
public static void main(String[] args) {
int[] arr={1,2,3};
try {
System.out.println("before");
arr=null;
System.out.println(arr[100]);
System.out.println("after");
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("finally code");
}
}
在上述代码中以及运行结果我们可以看出在try代码块中只执行了前两行代码,而运行结果中报出了空指针异常,这也就代表了执行到了try中第二行代码时发生了异常因此就直接进入catch中的处理异常,这里处理异常的方式是直接将异常打印出来,最后还执行了finally中的代码块。
从上我们可以 看出finally代码块是一定会执行的。
异常处理流程
- 程序先执行 try 中的代码。
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配。
- 如果找到匹配的异常类型, 就会执行 catch 中的代码。
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者。
- 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行)。
- 如果上层调用者也没有处理的了异常, 就继续向上传递。
- 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止。
抛出异常
除了java内置的类会抛出异常时,我们也可以通过throw手动抛出异常。
代码示例:
public static void main(String[] args) {
System.out.println(divide(10,0));
}
private static int divide(int i, int i1) {
if(i1==0){
throw new ArithmeticException("分子为0");
}
return i/i1;
}
Java 异常体系
Java内置了丰富的异常体系,用来表示不同情况下的异常。
下图表示Java内置的异常类之间的继承关系:
-
顶层类 Throwable 派生出两个重要的子类, Error 和 Exception。
-
其中 Error 指的是 Java 运行时内部错误和资源耗尽错误. 应用程序不抛出此类异常. 这种内部错误一旦出现,除了告知用户并使程序终止之外, 再无能无力. 这种情况很少出现。
-
Exception 是我们程序猿所使用的异常类的父类。
-
其中 Exception 有一个子类称为 RuntimeException , 这里面又派生出很多我们常见的异常类有NullPointerException , IndexOutOfBoundsException 等。
自定义异常
Java 中虽然已经内置了丰富的异常类, 但是我们实际场景中可能还有一些情况需要我们对异常类进行扩
展, 创建符合我们实际情况的异常。
如一下代码,这是一个简单的登录系统。
package T0125;
import java.util.Scanner;
public class TE0125 {
private static String username="byte";
private static String password="12345";
public static void main(String[] args) {
int count=0;
Scanner scanner=new Scanner(System.in);
String u=scanner.next();
String p=scanner.next();
try{
login(u,p);
}catch (UserError userError){
userError.printStackTrace();
}catch (PasswordError passwordError){
passwordError.printStackTrace();
}
}
public static void login(String username,String password) throws UserError,PasswordError{
if(!TE0125.username.equals(username)){
throw new UserError("用户名错误");
}
if(!TE0125.password.equals(password)){
throw new PasswordError("密码错误");
}
System.out.println("登陆成功!");
}
}
以下是自定义的异常类:
package T0125;
class UserError extends Exception {
public UserError(String message){
super(message);
}
}
class PasswordError extends Exception{
public PasswordError(String message){
super(message);
}
}
通过上述的操作就可以自定义异常。
注意事项
- 自定义异常通常会继承自 Exception 或者 RuntimeException
- 继承自 Exception 的异常默认是受查异常
- 继承自 RuntimeException 的异常默认是非受查异常.