• 异常处理


    前言

    使用异常所带来的一个相当明显的好处是,它往往能够降低错误处理代码的复杂度。如果不使用异常,那么就必须检査特定的错误,并在程序中的许多地方去处理它。而如果使用异 常,那就不必在方法调用处进行检査,因为异常机制将保证能够捕获这个错误。并且,只需在 —个地方处理错误,即所谓的异常处理程序中。这种方式不仅节省代码,而且把“描述在正常 执行过程中做什么亊”的代码和“出了问题怎么办”的代码相分离。总之,与以前的错误处理 方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。
    所以这样,可以避免了我在编写程序时的写多个if判断来明细每种可能出现的类型错误。

    一、什么是异常

    异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常情况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。比如使用空的引用、数组下标越界、内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图。错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的。假若程序在运行期间出现了错误,如果置之不理,程序便会终止或直接导致系统崩溃,显然这不是我们希望看到的结果。因此,如何对运行期间出现的错误进行处理和补救呢?Java提供了异常机制来进行处理,通过异常机制来处理程序运行期间出现的错误。通过异常机制,我们可以更好地提升程序的健壮性。

    二、为什么要用java异常处理机制

    1、java的基本理念:结构不佳的代码不能运行。
    2、异常处理机制初衷:方便程序员处理错误。在编译期间对可能出现的错误进行捕获---异常处理机制。如果每个方法可能出现的错误都进行情况处理,使得编码过于繁琐。
    3、异常是为了方便程序员将精力放在业务的处理逻辑上。

    4、进行try-catch异常处理,是为了保证程序的正常运行。

    5、详细说明:

    java exception是java独特的一个元素,在我理解中,它用来保证我们将精力放在正常的业务逻辑上,一般的代码本身应该主要用来实现正常业务逻辑的执 行。在正常业务逻辑执行的过程中,会产生可预知及不可预知的意外情况,例如内存泄露属于不可预知的意外情况,参数输错属于可预知的意外情况。从前我们都将 exception想得太严重,觉得是非法参数、数组越界这些很严重的情况才会用到exception,属于“不得以而处理之”的东西。现在,我们是否应 该好好利用这个java中特别的一份子,来帮助我们更好的处理业务流程,将正常流程和意外情况分割开来进行处理,以保证正常业务需求与意外事件分离。
    使用异常还有一个好处是,它显示的声明了可能发生的意外情况。例如,在读取一个数据项是,也许给出的索引是错误的,在从前的代码中,比较细心些的程序员 会留个心眼检查一下调用的代码是否会返回null对象。有一些粗心的程序员会忘记这一点,默认对方会返回一个对象,然后就在自己的代码中直接调用该对象的 方法,从而在调试时,会莫名其妙的收到空指针异常。如果下层代码在撰写时对索引无效这一个意外情况封装了一个Checked类型(检查时异常)的Exception,那 么上层调用的程序员就可以在IDE的提示下获知这一可能发生的意外,从而在代码中避免调用空指针的方法而导致错误。并且,上层程序员还可以视情况的恢复异常,即使他无能为力,至少还可以再抛给上层,看看有没有人可以处理这个意外。也算是程序员之间,一种不见面的契约。

    但是使用异常会有一 个问题,即接口的耦合度变高了。原来只需要参数符合,现在还强制要求处理预见的异常,很多程序员也觉得是一种负担,代码上看起来也会不简洁。而且,如果某 一个业务模块会发生的意外很多,而针对每一个意外情况都定义一种异常的话,会导致接口抛出一长串异常,使代码变得很难看。所以,如何将性质相近的异常抽象 成一个通用的异常也很考验java程序员的功力

    注:checked exception(检查异常),也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,比如常见的IOExeption和SQLException。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过。

    三、程序中的异常处理

    3.1 异常处理的位置

    【1】在最上层,方法的调用处。
    【2】将相近的错误信息尽可能的整理为一个异常处理信息;

    3.2  开发中对异常分析

    3.2.1 业务层不进行异常处理

    1、因为spring框架对事务进行了处理,如果在业务层进行try-catch,则执行失败时,事务不会回滚。

    2、异常都在控制层进行处理,即所有的try-catch均写在control层

    3.2.2 出现异常,报错下面的代码是否还会执行

    3.2.2.1 不进行异常处理

    public static void main(String[] args) {
            int a = 1/0;
            System.out.println("不进行异常处理,报错后的代码不会执行");
        }

    如果不进行异常处理,会阻断程序的继续执行。此时在可控制台有如下信息输出:

    方法与方法调用处均不进行异常处理:

    public static void main(String[] args) {
    
            int a = testException();
            System.out.println(a);
            System.out.println("没有异常处理,直接报错");
        }
    
        public static int testException() {
    
            int b = 1;
            b = 1/0;
            System.out.println("方法中不进行异常处理,也不抛出异常,后续代码均不会再执行,直接返回到方法调用处");
            return b;
        }

    控制台输出:

    3.2.2.2 进行异常处理,在方法调用处进行异常处理

    public static void main(String[] args) {
    
            int a = 0;
            try {
                a = testException();
            } catch (Exception e){
                //e.printStackTrace();
                System.out.println(a);
                System.out.println("在方法调用处catch异常");
            }
            System.out.println("catch后续代码继续执行");
    
        }
    
        public static int testException() {
    
            int b = 1;
            b = 1/0;
            System.out.println("方法中不进行异常处理,也不抛出异常,后续代码均不会再执行,直接返回到方法调用处");
            return b;
        }

    控制台输出:

    3.2.2.3 进行异常处理,在方法里try-catch

    public static void main(String[] args) {
    
            int a = 0;
                a = testException();
            System.out.println(a);
        }
    
        public static int testException() {
    
            int b = 0;
            try {
                b = 1/0;
            }
            catch (Exception e){
                b = 1;
                System.out.println(b);
                System.out.println("进行异常处理,报错后对b重新赋值");
            }
            System.out.println("进行异常处理,报错后catch下面的代码会执行");
            return b;
        }

    注意:当对程序进行了异常处理时,如果不打印输出异常信息,在控制台是没有输出的。

    控制台输出信息如下:只有我自己写的标准输出。

    加上e.printStackTrace()后,才会打印异常信息。

     也可以通过日志管理工具进行信息输出。如logback。

    详见链接:https://www.cnblogs.com/vole/p/12568493.html

    3.2.2.4 进行异常处理,但是在catch中抛出(throw)异常

    在方法中,既可以向上面那样,对异常进行处理,也可以抛出异常。

    如下例所示,抛出异常,表示我不想在方法里处理,要抛出去统一处理。同样,在方法调用处不catch的话,同样可以继续向外抛出。

    throw后面是不能写代码的,编译就不通过。

    public static void main(String[] args) /*throws Exception*/{
    
            try {
                testException();
            } catch (Exception e){
                System.out.println("在方法调用处处理异常");
            }
        }
    
        public static  int testException() throws Exception{
    
            int a = a = 1/0;
            throw new Exception();
            //return a;当对一个方法进行throw后,下面的代码均不会再执行
        }

    控制台输出:

    上面只是个例子,因为实际开发明显不会这样抛出异常,不然怎么return。

    想要抛出异常,直接在方法处throws就行了。如下:

    public static void main(String[] args) throws Exception{
    
            int a = 0;
            a = testException();
            System.out.println("方法处抛出了异常,调用处没有处理异常,报错");
            System.out.println(a);
        }
    
        public static int testException() throws Exception{
    
            int b = 0;
            b= 1/0;
            return b;
        }

    控制台输出:

    这里输出报错,因为main主方法为最外层了,不能再抛了,必须处理。

    如果不抛出的话,也不处理,编译会报错的。

    也可以可以自定义异常,像下面这样抛出异常:

    public static void main(String[] args) {
    
            int a = 0;
            a = testException();
            System.out.println("方法处抛出了异常,调用处没有处理异常,报错");
            System.out.println(a);
        }
    
        public static int testException() {
    
            int b = 0;
            try {
                b = 1/0;
            }
            catch (Exception e){
                b = 1;
                throw new BusinessException ("b抛出自定义异常");
                //System.out.println(a);
                //System.out.println("进行异常处理,报错后对b重新赋值");
            }
            System.out.println("进行异常处理,报错后catch下面的代码会执行");
            return b;
        }

    控制台输出:

     不仅要抛出,抛出去后要记得处理,不然就继续往外抛,最外层一定要处理,不然会报错。正确的如下,在方法调用处处理异常:

    public static void main(String[] args) throws Exception{
    
            int a = 0;
            try{
                a = testException();
            }catch(BusinessException be){
                System.out.println(be.getMessage());
                System.out.println("方法处抛出了异常,调用处处理异常");
            }catch (Exception e){
                System.out.println("走不到这里");
            }
            System.out.println(a);
            System.out.println("异常处理完,继续执行后续代码");
        }
    
        public static int testException() {
    
            int b = 0;
            try {
                b = 1/0;
            }
            catch (Exception e){
                b = 1;
                throw new BusinessException ("b抛出自定义异常");
                //System.out.println(a);
                //System.out.println("进行异常处理,报错后对b重新赋值");
            }
            System.out.println("进行异常处理,报错后catch下面的代码会执行");
            return b;
        }

    控制台输出:

    3.2.2.4 进行异常处理,在catch中return

    如下示例,在catch中进行了return,当出现异常时返回1。最好就是别在catch中些return了。异常就放到调用处,control里来写。

    public static void main(String[] args) {
    
            int a = 0;
            a = testException();
            System.out.println(a);
        }
    
        public static int testException(){
    
            int a = 0;
            try {
                a = 1/0;
            }
            catch (Exception e){
    
                a = 1;
                return a;
                //System.out.println("进行异常处理,报错后对a重新赋值");//return后的代码会出现编译时异常
            }
            a = 2;
            return a;
        }

    经过我的经验,感觉try-catch用的时候一定要慎重,还有严谨,简单,如果将太多的逻辑判断放到try-catch中来判断,也就失去了异常处理机智的初衷,反而让程序看起来更臃肿。

    四、自定义异常

    1、简单的自定义异常

    package com.asd.common.utils;
    
    public class BusinessException extends RuntimeException {
    
    private static final long serialVersionUID = 1L;
    
    public BusinessException(){
    }
    
    public BusinessException(String message){
    super(message);
    }
    }

    2、如下所示,可以在业务层抛出自定义的异常

    private Map<List<Importloss>, List<Assess>> genAssessBreakMapByloss(List<Importloss> importlossList, List<Assess> assessTreatyList) {
    
    int importlossListSize = importlossList.size();//获取上传的
    Map<SDataKey, List<Importloss>> group_loss = SDataGroup.group(importlossList, "assessdate", "comcode", "classcode4", "reinstype4", "uwyear","treatyid","acccurrency","reacccurrency");
    Map<SDataKey, List<Assess>> group_assess = SDataGroup.group(assessTreatyList, "assessdate", "comcode", "classcode4", "reinstype4", "uwyear","businessid","acccurrency","reacccurrency");
    Set<SDataKey> sDataKeys = group_assess.keySet();
    //组织map key为导入表数据,value为评估表数据
    Map<List<Importloss>, List<Assess>> breakMap = new HashMap<>();
    for (SDataKey dataKey : sDataKeys) {
    if (group_loss.containsKey(dataKey)){
    breakMap.put(group_loss.get(dataKey),group_assess.get(dataKey));
    importlossList.removeAll(group_loss.get(dataKey));////assessTreatyList.removeAll(group_assess.get(dataKey));//
    }else{//如果
    Map<SDataKey, List<Importloss>> group_loss2 = SDataGroup.group(importlossList, "assessdate", "comcode", "classcode4", "reinstype4", "uwyear","treatyid","acccurrency");
    List<Assess> assessTreatyList1 = new ArrayList<>();
    List<Assess> assessTreatyList2 = group_assess.get(dataKey);
    assessTreatyList1.addAll(assessTreatyList2);
    Map<SDataKey, List<Assess>> group_assess2 = SDataGroup.group(assessTreatyList1, "assessdate", "comcode", "classcode4", "reinstype4", "uwyear","businessid","acccurrency");
    Set<SDataKey> sDataKeys1 = group_assess2.keySet();
    for (SDataKey dataKey1 : sDataKeys1) {
    if (group_loss2.containsKey(dataKey1)) {
    breakMap.put(group_loss2.get(dataKey1), group_assess2.get(dataKey1));
    }
    }
    }
    }
    if(breakMap.size() != importlossListSize){
    throw new BusinessException("请校对上传的数据");
    }
    return breakMap;
    }

    3、在控制层catch异常,并用日志记录异常信息。可以写多个catch来具体化异常信息。

    /**
    *拆分
    * @param assessdate
    * @return
    */
    @RequestMapping(value = "breakData",produces = "application/text;charset=utf-8")
    @ResponseBody
    public String breakData(String assessdate){
    String result = "成功";
    try {
    //组织拆分需查询的条件
    result = outstandingBreakService.breakData(assessdate);
    }catch (BusinessException e){
    result = e.getMessage();
    log.error(result,e);
    } catch (Exception e){
    result = "失败";
    log.error(result,e);
    }
    return result;
    }

    五、关于异常的捕获与日志管理

    5.1 tomcat日志

    tomcat  有五类日志 :catalina、localhost、manager、admin、host-manager

    1、catalina.out

    catalina.out中记录的控制台打印出的信息。(catalina.out即标准输出和标准出错,所有输出到这两个位置的都会进入catalina.out,这里包含tomcat运行自己输出的日志以及应用里向console输出的日志。默认这个日志文件是不会进行自动切割的,我们需要借助其他工具进行切割(注意:catalina.out文件如果过大会影响tomcat的运行));

    2、catalina.{yyyy-MM-dd}.log

    catalina.{yyyy-MM-dd}.log是tomcat自己运行的一些日志,这些日志还会输出到catalina.out,但是应用向console输出的日志不会输出到

    catalina.{yyyy-MM-dd}.log,它是tomcat的启动和暂停时的运行日志,注意,它和catalina.out是里面的内容是不一样的。

    3、localhost.{yyyy-MM-dd}.log

    localhost.{yyyy-MM-dd}.log主要是应用初始化(listener, filter, servlet)未处理的异常最后被tomcat捕获而输出的日志,它也是包含tomcat的启动和暂停时的运行日志,但它没有catalina.2018-09-19.log 日志全。它只是记录了部分日志。

    参看链接:

    tomcat日志详解:https://www.cnblogs.com/operationhome/p/9680040.html

    5.2 异常捕获与日志输出

    当不进行异常处理时,如果程序报错,程序会中断,且在控制台是不会进行异常信息打印的;(会在 localhost.YYYY-MM-DD中记录异常信息。loglocalhost.{yyyy-MM-dd}.log主要是应用初始化(listener, filter, servlet)未处理的异常最后被tomcat捕获而输出的日志,它也是包含tomcat的启动和暂停时的运行日志,但它没有catalina.2018-09-19.log 日志全。它只是记录了部分日志。)
    想要在控制台打印报错信息,必须对异常进行捕获并进行e.printStackTrace();printStackTrace()方法的意思是:在命令行打印异常信息在程序中出错的位置及原因。或者通过日志管理工具,如log4j;logback进行管理,也会打印报错信息。

    参看链接:

    https://www.cnblogs.com/dolphin0520/p/3769804.html

    https://blog.csdn.net/hguisu/article/details/6155636

    六、常见异常

    1、

    //报错:java.lang.ArithmeticException:divide by zero
    
    //除数不能为零,请务必检查代码是否有机会出现除数为零的情况。

    2、

    //Spring MVC环境报错 No mapping found for HTTP request with URI
    
    //可能原因:
    
    @Controller
    
    @RequestMapping("/outstand")=====》这里不要写value
    
    public class OutstandingBreakController {
    
    
    
        @Autowired
    
        private OutstandingBreakService outstandingBreakService;
    
    
    
        //导入/拆分
    
        @RequestMapping("outstandBreak")
    
        public String getAssessBreakPage(){
    
            return "break/outstandBreak";
    
        }

    3、

    //异常信息:
    
    严重: Servlet.service() for servlet [springMVC] in context with path [] threw exception
    
     [Request processing failed; nested exception is org.springframework.transaction.
    
     UnexpectedRollbackException: Transaction rolled back because it has been marked as 
    
     rollback-only] with root cause
    
    org.springframework.transaction.UnexpectedRollbackException: 
    
    Transaction rolled back because it has been marked as rollback-only
    
    //==》当用Spring框架进行开发时,不能在业务层处理异常。因为Spring有自己的事务处理机制,
    
    //进行try catch后,不会进行回滚事务,会报错。而且在catch中的return也不会进行返回。
    如果错过太阳时你流了泪,那你也要错过群星了。
    在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
    不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。
  • 相关阅读:
    day28 粘包, 合法性连接
    day27 网络通信协议, tcp和udp, 缓冲区, subprocess
    day 26 C/S架构, 网络通信流程, 初识socket
    day25 包语法
    Python9-From-CSS-day48
    Python9-前端基础知识-day47
    Python9-MySQL-MySQL-ORM框架-day48
    Python9-MySQL-MySQL存储过程-视图-触发器-函数-day45
    Python9-MySQL-pymysql模块-day44
    Python9-MySQL-Homework-day43
  • 原文地址:https://www.cnblogs.com/szrs/p/12189597.html
Copyright © 2020-2023  润新知