欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java异常知识小结

程序员文章站 2024-03-19 23:18:52
...

1、异常的分类

简单的说,运行期间阻止程序正常运行的就是异常,java的异常处理机制是很重要的一块,它通过少量代码来增强了应用的容错性和健壮性,java中异常的分类大致如下:

 

1、Throwable是Error和Exception的顶层父类;

2、Error是一般是指jvm的错误,比较少出现,不能通过代码层面去控制Error的产生;

3、Exception代表程序运行过程中,"不希望"出现的情况,比如空指针、数组下标越界、文件未找到等异常,通过适当的处理可"避免",也是开发所要关注的重点;

4、SQLException/IPException也被称为checked异常,对于可能发生checked异常的,程序编译期间就会强制捕获;Error被称为unchecked异常;而RuntimeException为unchecked异常,需要程序员手动的捕获处理。

java异常知识小结

2、异常的处理

通过try catch捕获异常

try catch处理异常是最常见的异常处理方式

try {
    //里面放可能出现异常的逻辑
    //发生异常就进入catch中的逻辑
    //如果有finally语句,则最后执行finally中的逻辑
} catch (Exception e) {
    if(e instanceof SQLException){
        
    }
    //可以使用异常的父类Exception来匹配所有异常
    //每个catch会用于捕获特定的异常(包括其子类)
    //如果try中没有捕获到异常,则执行finally中逻辑
    //catch语句中有时会进行try逻辑的重试,视需求而定
} finally {
    //finally语句是可选择的,一般finally里会做一些资源关闭、回收工作,比如关闭流,关闭未使用的线程等
    //这里的逻辑一般都会执行
    //有时候finally语句中自己还要try catch
}

注意事项:

1、catch中支持父类匹配,所有如果要不是直接catch(Exception e),而是catch(SQLException e){}catch(NullPointerException e){}这样的方式去匹配异常,那么要将捕获子类异常放在父类的前面,这样捕获才有意义;

2、try/catch/finally语块中定义的局部变量不能共享使用,如果要共享,就定义在try/catch/finally块外部;

3、catch语句中最好配合日志记录,方便定位异常;

4、对于非运行时异常(Exception),一定要try catch处理;

5、try中的逻辑一旦发生异常,紧跟在异常逻辑后面的代码便不再执行;

6、存在主线程和子线程时(比如使用Executor框架时),当子线程发生异常,主线程是不受影响的(线程只解决线程自己存在的问题),这点很关键,吃过这个亏;  但是可以通过比如Callable对象的异步线程去获取异常,返回给主线程;

7、普通的逻辑处理中,对于可预见或不可预见性的异常,最好try catch捕获下;

8、含有事务处理的逻辑中,如果要让事务顺利回滚,请不要在相应的代码中try catch,这一点可以参考https://blog.csdn.net/fanrenxiang/article/details/83024436

通过throws关键字抛出异常

public static void get() throws Exception{ }

throws抛出异常不同于try catch,它会将可能存在的异常显式的抛向这个方法的调用者,而自己不做任何处理

 

3、自定义异常类和异常处理器

项目中经常需要用到异常处理类来处理两类异常:1.程序逻辑中抛出的异常,比如不小心数组下标越界;2.处理404/500等这样的页面错误码响应,比如你直接访问一个项目域名但是路由却不存在的地址会直接抛出404页面,很不友好。

//自定义异常,用以区分精确的异常种类
public class GlobalException extends RuntimeException {

    private Integer code;
    private String message;

    public GlobalException(Integer code,String message) {
        super(message);
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
import com.simons.cn.springbootdemo.util.Result;
import com.simons.cn.springbootdemo.util.ResultUtil;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 类描述:全局api代码异常处理返回
 */
@ControllerAdvice
public class ApiExceptionHandler {

    @ResponseBody
    @ExceptionHandler
    public Result handleException(Exception e) {
        if (e instanceof GlobalException) {
            GlobalException ge = (GlobalException) e;
            return ResultUtil.success1(ge.getCode(), ge.getMessage());
        }
        return ResultUtil.success1(-1001, "unknown error!");
    }
}
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
/**
 * 类描述:全局异常处理:处理类似于404、500酱紫的页面异常
 * 创建人:simonsfan
 */
@Component
public class ErrorPageExceptionHandle implements ErrorController {

    private static final String ERROR_PATH = "/error";

    /**
     * Returns the path of the error page.
     *
     * @return the error path
     */
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }

    /**
     * Web页面错误处理
     */
    @RequestMapping(value = ERROR_PATH, produces = "text/html")
    public String errorPageHandler(HttpServletResponse response) {
        int status = response.getStatus();
        switch (status) {
            case 403:
                return "403";     //这里的403、404/500页面都需要自定义
            case 404:
                return "404";
            case 500:
                return "500";
        }
        return "index";   //都不是,跳回到首页
    }
}

测试全局api代码异常处理返回

    /**
     * 测试api全局异常统一处理
     *
     * @return
     */
    @GetMapping(value = "/hello")
    @ResponseBody
    public String hello() {
        if (true) {
            throw new GlobalException(200, "我是api,发生异常啦~");
        }
        return "hello";
    }

测试处理类似于404、500酱紫的页面异常的话,直接访问一个项目域名下不存在的url即可跳转到自定义的404页面。

使用自定义异常类使得业务逻辑和异常处理分离解耦;同时自定义异常处理器能更加统一性、专一性的处理特定异常,能减少在try catch中的重复性代码,结合注解使用更加显得方便。

 

4、finally关键字

1、finally里的逻辑都会执行,除非执行过System.exit(0)或者逻辑都没走到try语句块中;

2、finally逻辑总是在return语句之前;

3、finally语句总是伴随着try或catch语句出现,try必须和catch或者finally同时使用;

关于finally语句,引申阅读:finally和return那点事

 

5、主线程如何获取子线程捕获到的异常

思路:使用Callable对象的get方法获取子线程的执行结果,子线程中进行try catch异常捕获

 /**  controller访问路由  **/
    @RequestMapping("/test")
    @ResponseBody
    public String test() {
        CommonResult result = getResult();
        log.info("子线程任务执行结果:message=" + result.getMessage());
        return result.getMessage();
    }
        /**    获取异步任务结果方法  **/
    public static CommonResult getResult(){

        CommonResult result = new CommonResult();
        ExecutorService executorService = Executors.newCachedThreadPool();
        FutureTask<CommonResult> future = new FutureTask<>(new ChargeCallable());
        executorService.submit(future);
        try {
            result =  future.get();
        } catch (Exception e) {
            log.error("主线程捕获到子线程任务中的异常");
        }
        return result;
    }
        /**   异步任务逻辑   **/
    static class ChargeCallable implements Callable<CommonResult> {
    private  final Logger logger = LoggerFactory.getLogger(ChargeCallable.class);
    @Override
    public CommonResult call() {
        CommonResult result = new CommonResult();
        try {
            //TODO 模拟逻辑
            result.setErrorcode("0");
            result.setMessage("success");
            //TODO 模拟发生异常
            throw new Exception("我是非运行时异常");
        } catch (Exception e) {
            logger.info("我是子线程,我发生异常了");
            result.setErrorcode("-1");
            result.setMessage("fail");
        }
        return result;
      }
    }