关于异常,你需要知道这些

一.初识异常

异常是一种程序运行时的机制
例如有这么一串代码

public class Test10_28 {
    public static void main(String[] args) {
        System.out.println(10/0);
    }
}

运行后抛出了这么一个异常,表示不能除以0

Exception in thread “main” java.lang.ArithmeticException: / by zero
at Test10_28.main(Test10_28.java:3)

public class Test10_28 {
    public static void main(String[] args) {
        String s = null;
        System.out.println(s.length());
    }
}

就会抛出一个空指针异常

Exception in thread “main” java.lang.NullPointerException
at Test10_28.main(Test10_28.java:4)

异常是程序运行时产生的一种问题(不是编译期)
异常的种类有很多,不同种类的异常能够表示不同情况的问题

所谓运行时
有些错误是这样的, 例如将 System.out.println 拼写错了, 写成了 system.out.println. 此时编译过程中就会出
错, 这是 “编译期” 出错.
而运行时指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误.

防御性编程

错误在代码中是客观存在的,因此我们要让程序出现问题的时候即时通知程序员。主要有两种方式

  • LBYL: Look Before You Leap. 在操作之前就做充分的检查.
  • EAFP: It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到问题再处理.

异常的思想就是EAFP(牵小手的故事)

异常的基本用法

基本语法

try{ 
有可能出现异常的语句 ; 
}[catch (异常类型 异常对象) {
} ... ]
[finally {
异常的出口
}]
  • try代码块中放的是可能出现异常的代码
  • catch代码块中放的是出现异常后的处理行为。
  • finally代码块中的代码用于处理善后工作,最后执行

例子

public class Test10_28 {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        System.out.println("before");
        System.out.println(arr[100]);
        System.out.println("after");
    }
}

before
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 100
at Test10_28.main(Test10_28.java:5)

我们发现,程序执行到出现异常的地方,就不跑了

public class Test {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        try {
            System.out.println("before");
            System.out.println(arr[100]);
            System.out.println("after");
        }catch (ArrayIndexOutOfBoundsException e){
            //打印调用栈
	        //e.printStackTrace();
        }

        System.out.println("after try");
    }
}

before
after try

处理异常在这里的顺序,先执行try语句,遇到异常,进入catch语句执行,执行完毕后继续向下执行

一个程序如果出现异常,并且程序员没有手动处理的话,异常就会被JVM来处理,JVM会打印出现异常的调用栈及相关的异常信息,同时让程序终止运行(治不了)。
也可以让程序员手动处理,手动处理过程中,e.printStackTrace()也能打印出异常的调用栈和相关信息,同时程序还能继续运行
如果要退出,在catch语句中加System.exit(1)

关于异常的处理方式

异常的种类有很多, 我们要根据不同的业务场景来决定.
对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿
对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试.
在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快
速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息.

关于调用栈

方法之间存在相互调用关系的,这种调用关系我们可以用“调用栈来描述”,在JVM中有一块内存空间称为“虚拟机栈”专门存储方法之间的调用关系。出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.

public class Test {
    private static void fun(){
        int[] arr = {1,2,3,4};
        try {
            System.out.println("before");
            System.out.println(arr[100]);
            System.out.println("after");
        }catch (ArrayIndexOutOfBoundsException e){
            //打印调用栈
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        fun();
        System.out.println("after try");
    }
}

before
java.lang.ArrayIndexOutOfBoundsException: 100
after try
at Test.fun(Test.java:6)
at Test.main(Test.java:14)

一个try可以搭配多个catch

try {
            System.out.println("before");
            System.out.println(arr[100]);
            System.out.println("after");
        }catch (StringIndexOutOfBoundsException e){
            //打印调用栈
            e.printStackTrace();
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
//		catch (StringIndexOutOfBoundsException | ArrayIndexOutOfBoundsException e){
            //多个分支,处理方式相同,可以加个 | 
//            e.printStackTrace();
//        }

也可以在方法外部使用try catch

public class Test {
    private static void fun(){
        int[] arr = {1,2,3,4};
        System.out.println("before");
        System.out.println(arr[100]);
        System.out.println("after");

    }
    public static void main(String[] args) {
        try{
            fun();
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
    }
}

如果代码中抛出异常之后,如果没有立即处理,就会沿着调用栈向调用者方向传递。
比如这里,fun里面抛出异常,会返回到上层main线程里进行处理这个异常

程序先执行 try 中的代码
如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
如果找到匹配的异常类型, 就会执行 catch 中的代码
如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
如果上层调用者也没有处理的了异常, 就继续向上传递.
一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

finally执行的时间是在方法返回(return)之前、之前、之前!
所以说,如果finally中有return就会影响到返回结果,不建议把return放入到finally之中

抛出异常

public class Test {
    public static int divide(int x,int y){
        if(y == 0){
            throw new ArithmeticException("除数是0");
        }
        return x/y;
    }
    public static void main(String[] args) {
        System.out.println(divide(10,0));
    }
}

Exception in thread “main” java.lang.ArithmeticException: 除数是0
at Test.divide(Test.java:4)
at Test.main(Test.java:9)
进程完成,退出码 1//Java虚拟机抛出来的

public class Test {
    public static int divide(int x,int y){
        if(y == 0){
            throw new ArithmeticException("除数是0");
        }
        return x/y;
    }
    public static void main(String[] args) {

        try {
            System.out.println(divide(10,0));
        }catch (ArithmeticException e){
            e.printStackTrace();
        }
    }
}

java.lang.ArithmeticException: 除数是0
at Test.divide(Test.java:4)
at Test.main(Test.java:11)
进程完成,退出码 0 //我们自己抛的异常

异常说明

使用throws关键字提示程序员一个方法可能会抛出哪些异常。更有利于代码的维护,方便编译器进行校验。

public class Test {
    public static int divide(int x,int y)throws ArithmeticException{
        if(y == 0){
            throw new ArithmeticException("除数是0");
        }
        return x/y;
    }
    public static void main(String[] args) {
        System.out.println(divide(10,0));
    }
}

异常小结
1.try基本的用法:放置可能会抛出异常的代码
2.catch基本用法:参数的类型得匹配,catch可以有多个
3.finally基本用法,用来善后工作
4.throw抛出异常
5.throws异常说明

异常体系★★

在这里插入图片描述

Java语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为 非受查异常, 所有的其他异常称为 受查异常.
粉色的叫做受查,蓝色的叫做非受查
受查异常必须得自己写try—catch包起来

方法一

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Test {
    public static int func(){
        //尝试读一个文件
        File file = new File("d:/test.txt");
        Scanner scanner = null;
        try {
            scanner = new Scanner(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return scanner.nextInt();
    }
    public static void main(String[] args) {
        func();
    }
}

方法二

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Test {
    public static int func() throws FileNotFoundException {
        //尝试读一个文件
        File file = new File("d:/test.txt");
        Scanner scanner = new Scanner(file);
        return scanner.nextInt()
    }
    public static void main(String[] args) {
        try {
            func();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

// public static void main(String[] args) throws FileNotFoundException {
//        func();
//    } 如果这样写就是异常说明,交给JVM处理

自定义异常类

一般自定义异常往往不是一下就创建一个类,往往是一个系列

import java.util.Scanner;
class UserException extends Exception{
    //存在最重要的意义,表示出错的情况

    public UserException(String message) {
        super(message);
    }
}
class PassWordException extends Exception{
    public PassWordException(String message) {
        super(message);
    }
}
public class Test {
    private static final String USER_NAME = "jack";
    private static final String PASSWORD = "123456";

    public static void login() throws UserException,PassWordException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名");
        String userName = scanner.next();
        if(!userName.equals(USER_NAME)){
            //TODO 出现情况抛出异常
            throw new UserException("用户名错误");
        }
        System.out.println("请输入密码");
        String password = scanner.next();
        if(!password.equals(PASSWORD)){
            //TODO 出现密码异常
            throw new PassWordException("密码错误");
        }
        System.out.println("登入成功" );
    }
    public static void main(String[] args){
        try {
            login();
        } catch (UserException e) {
            e.printStackTrace();
        } catch (PassWordException e) {
            e.printStackTrace();
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值