【java基础】异常

168 阅读5分钟

异常

一、异常的概念

异常是指程序运行时出现的特殊情况。

任何程序都存在着未知的问题,如果不处理,可能会导致程序崩溃。所以需要处理程序中的问题。

二、异常的分类

异常的顶层父类为Throwable。

分为两大子类:

  • Error:JVM或者硬件问题,无法在程序中解决,不用处理。

  • Exception:程序中需要处理的异常。

    • RuntimeException:运行时异常(未检查(非受检)异常)。这类的异常一般是程序员没有进行足够的检查判断,属于程序员的bug,不需要使用异常处理的方式处理,应该加入检查的代码。

      java.lang.ArithmeticException:算术异常(除数为0)

      下标越界异常

      空指针异常

      类型转换异常

    • CheckedException:已检查(受检)异常。(非运行时异常)程序员已经进行了足够的检查,但是仍旧无法阻止异常的产生。出现异常是由用户引发的,此类异常无法检查,只能使用异常处理。

注意:运行时异常不会提示需要异常处理,而已检查异常会提示需要异常处理。

经典面试题:

1、请简述异常的分类。

2、请列举常见的运行时异常。

三、异常的产生

1、程序产生异常结果。

2、手动抛出异常。

当代码中出现不满足要求的情况时,需要代码中止执行,直接报错,可以抛出异常。当产生异常时,相当于遇到了return,程序会中止。

public class MyArrays {
    //3) 顺序查找
    //返回该数字的下标,如果没有,则返回-1
    public static int search(int[] numbers, int number) {
        if(numbers == null || numbers.length == 0) {
//          System.out.println("数组不能为空");
            throw new RuntimeException("数组不能为空");
        }
        else {
            for (int i = 0; i < numbers.length; i++) {
                if(numbers[i] == number) {
                    return i;
                }
            }
            return -1;
        }
    }
}

四、异常的传递

当代码中有已检查异常出现,又不想处理时,可以在方法后面声明异常,使用throws关键字,可以声明多个异常,使用逗号隔开。

如果在代码中不处理异常,只是将异常抛出,同时声明异常,那么该异常将由方法的调用者处理。最终会抛给JVM,JVM会直接打印异常。

public class Test2 {
    public static void main(String[] args) throws Exception {
        a();
    }
    
    public static void a() throws Exception {
        b();
    }
​
    private static void b() throws Exception {
        c();
    }
​
    private static void c() throws Exception {
        d();
    }
​
    private static void d() throws Exception {
        throw new Exception("出错啦");
    }
}

五、异常的处理

在遇到异常时,可以选择处理异常。

处理异常涉及的关键字为:try、catch、finally

语法结构:

try{

​ // 尝试去执行的,可能出现异常的代码

}catch(Exception e){

​ // 当遇到异常,捕获该异常,并进行处理

}finally{

​ // 最终一定会执行的代码

}


public class Test3 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int n = 5;
        
        System.out.println("请输入一个数字:");
        String numStr = input.nextLine();
        int num;
        try {
            // 可能会出现异常的代码
            num = Integer.parseInt(numStr);
            int m = 20 / num;
            int s = n * m;
            System.out.println("s的值为:" + s);
        } 
        catch (ArithmeticException e) {
            // 当出现了指定异常时
            System.out.println("出错啦,除数为0");
            num = 1;
            int m = 20 / num;
            int s = n * m;
            System.out.println("s的值为:" + s);
        } 
        catch (NumberFormatException e) {
            // 当出现了指定异常时
            System.out.println("出错啦,不是数字");
            num = 2;
            int m = 20 / num;
            int s = n * m;
            System.out.println("s的值为:" + s);
        }
        catch (Exception e) {
            // 当出现了指定异常时
            System.out.println("出错啦");
            num = 1;
            int m = 20 / num;
            int s = n * m;
            System.out.println("s的值为:" + s);
        } 
        
    }
}

注意:

  • 在try中代码,如果某一句代码出错,那么会直接跳转到异常的捕获,那么try中后面其他的代码将不会执行。不要将大量的代码放到一个try中。
  • 可以写多个catch,当出现异常时会从上至下匹配异常的类型,进行捕获。如果写了Exception这种父类异常,应该放到最后,作为一个补救措施,如果放在前面,后面的异常处理会报错。

finally表示一定会执行的,不管是否报错。

语法可以try-finally,也可以try-catch-finally。


public class Test4 {
    public static void main(String[] args) {
        a();
    }
    
    public static int a() {
        try {
            System.out.println("1");
            int n = 5 / 0;
            return 1;
        }catch (Exception e) {
            System.out.println("2");
            return 2;
        }finally {
            System.out.println("3");
        }
    }
}

注意:finally里面一定会执行,在return前执行。finally块一般用来释放或关闭资源。

经典面试题:


// 简单题:
public class Test5 {
    public static void main(String[] args) {
        int n = a();
        System.out.println(n); // n的值为3
    }
​
    private static int a() {
        try {
            int n = 5 / 0;
            return 1;
        } catch (Exception e) {
            return 2;
        }finally {
            return 3;
        }
    }
}

// 中等题
public class Test5 {
    public static void main(String[] args) {
        int n = a();
        System.out.println(n); // 值为3
    }
​
    private static int a() {
        int n = 1;
        try {
            n = 5 / 0;
            n = 2;
            return n;
        } catch (Exception e) {
            n = 3; 
            // 将n的值3放入栈顶,准备返回,此时栈顶是3
            return n;
        }finally {
            n = 4; // 将n的值改为4
        }
    }
}

// 中等题
public class Test6 {
    public static void main(String[] args) {
        int [] n = a();
        System.out.println(n[0]); //值为4
    }
​
    private static int [] a() {
        int [] arr = {1,2,3};
        try {
            int n = 5 / 0;
            arr[0] = 2;
            return arr;
        } catch (Exception e) {
            arr[0] = 3;
            return arr;
        }finally {
            arr[0] = 4;
        }
    }
}

// 高级题
public class Test6 {
    public static void main(String[] args) {
        int [] n = a();
        System.out.println(n[0]); // 值为7
    }
​
    private static int [] a() {
        int [] arr = {1,2,3};
        try {
            int n = 5 / 0;
            arr = new int[] {4,5,6};
            return arr;
        } catch (Exception e) {
            arr = new int[] {7,8,9};
            return arr;
        }finally {
            arr = new int[] {1,1,1};
        }
    }
}

六、自定义异常

注意:异常处理的方式的选择:

有能力处理就处理,没有能力处理的,如果调用者有能力处理时就抛出。

能够由调用者重新调整调用参数就能解决的异常,可以抛出,如果调用者也无能为力的,就处理(使用try-catch处理并记录日志,等待管理员处理)。

系统的异常是用来处理常规异常的,如果项目中需要处理业务异常,需要自定义业务异常。

下面的案例通过自定义一个登录异常来捕获业务异常。


public class LoginException extends Exception{
    public LoginException(String message) {
        super(message);
    }
    
    public LoginException() {
        super();
    }
}
public class Test7 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = input.nextLine();
        System.out.println("请输入密码:");
        String password = input.nextLine();
        try {
            login(username, password);
        }catch(LoginException e) {
            e.printStackTrace();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    private static void login(String username, String password) throws LoginException {
        if(username.equals("zhangsan") && password.equals("666666")) {
            System.out.println("登录成功");
        }else {
            throw new LoginException("用户名或密码错误");
        }
    }
}