java异常和错误码定义规范及其使用

前言

  • 本随笔是最近进行实际项目开发时总结的一些经验,可供需要的伙伴借鉴。

场景

  • 使用java提供服务的后端系统,使用者(可能是web前端或者是第三方调用者)通过api形式进行调用。

以前可能遇到的问题

  • 这里会不会发生异常?如果发生了我应该怎么做?
  • 我要定义哪些自定义异常?
  • 捕获到一个"未知"异常时,我该将其抛出去还是就地打印其堆栈信息?

问题分析

  • 为什么会产生异常?
      简单理解就是,异常是程序执行过程中未按预想状态执行的状态。
  • 为什么要自定义异常?
      1)与系统(这里指的是非本项目)异常做区分,这样我们就能争对不同的异常做不同的事情。
      2)自定义异常也是一个自定义对象,可以携带一些额外的信息。
  • 异常产生之后应该做什么?
      1)打印异常堆栈:这种情况下,一般需要运维或开发人员定位问题并人工解决,因为堆栈是打印给人看的,要不然也就不打印了
      2)执行候选方案等(可能也需要打印堆栈)

对异常进行分类

  • 集合项目和业务逻辑对可能产生的异常做分类。不管系统是干什么的,我们都可以将其分为“系统异常”和“非系统异常”,如果觉得太笼统可以将他们二者进行细分(最后你可能会发现没有细分的必要)。
      系统异常: 这类异常通常是大致地提示使用方系统不可用,同时需要相关人员更进处理的
      非系统异常:这类异常通常是一些义务逻辑而已,比如检查到用户参数有误后抛出的异常
  • 异常分类原则:先明确定义好什么样的系统属于系统异常,什么样的异常属于自定义异常,当捕获到一个异常或产生错误时,看这个异常是哪种场景,然后在归类。
  • 对分类异常进行处理。既然我们抛出了异常,那就得对其进行捕获处理,如果你的系统设计很好,那么一般情况是对异常进行统一捕获,这种情况下可能会先捕获我们自定义异常,然后再捕获最大异常,由于自定义异常一般已经携带了相关的错误信息,所以我们很容易控制返回给调用者的,所以要争对最大异常定义一个默认的返回值。

正确对待第三方工具(依赖)抛出的异常

  • 经过上面的定义,对于自己代码产生的异常可能有一个较好的解决方案了,但是捕获到别人抛出的异常时,可能就不知所措(刚开始我就是这样),这时候再回去看看刚刚的描述或许就会更加清晰了,如果抛异常的代码不是我们的依赖,而是我们项目的一部分,那么你肯定也能清楚的知道这个异常在我们的系统中属于一个怎样的级别。从关系上看,我们是依赖方,所以被依赖方不可能使用依赖方定义的异常。
  • 所以当我们捕获到一个依赖方的异常时,需要判断这个异常级别应该对应于我们系统已经定义的哪个异常,然后转换为我们自己定义的异常即可,这里要注意,如果这个异常是需要打印错误堆栈的,那么我们绝对不能把原来的异常直接扔掉,最好是将其作为嵌套异常包含在我们的自定义异常中再将其抛出,这就相当于给依赖方抛出的异常贴上一个标签,以便我们能进行统一捕获处理。

用有限的异常类处理义务中复杂多变的无限可能

  这个问题,其实大家都在使用,那就是配合错误码使用。和定义异常类一样的到了,定义和使用错误码的时候也要将其归类定义,避免重复定义和混乱使用。而且到最后你一定会发现,所定义的错误码大部分是业务错误码居多,即该错误码明确代表了一个错误码描述,而这个错误描述是给调用方看的,而系统级别的异常详细信息已经堆栈形式在日志中打印出来了,那不同的错再多定义一个错误码也意义不大。当然,如果要通过错误码对异常分类进行统计的情况除外,但是如果一个系统已经到了这个地步的话基本可以选择其他方案了。

异常类和错误码定义示例

需求

  • 对本系统的异常类型进行抽象并定义相应的异常。
  • 自定义错误对象能携带相关的错误信息,以便能统一进行捕获处理
  • 需要保证错误码不会混乱使用,即“系统级别”类错误码只能用于“系统”类异常,不能混乱使用

示例代码

public interface ErrorCode {
  String getErrorCode();
  String getErrorDesc();
}

获取错误码及其藐视信息接口定义

/**
 * 用户异常错误码枚举
 */
public enum UserErrorCode implements ErrorCode {
  /**
   * 用户名错误
   */
  USERNAME_ERR("username-err", "用户名错误"),
  /**
   * 密码错误
   */
  PASSWORD_ERR("PASSWORD-ERR", "密码错误"),
  ;

  UserErrorCode(String errorCode, String errorDesc) {
    this.errorCode = errorCode;
    this.errorDesc = errorDesc;
  }

  // 错误码.
  @Getter
  private String errorCode;
  // 错误码对应的外部描述信息.
  @Getter
  private String errorDesc;
}

用户异常错误码枚举示例

/**
 * 系统异常错误码枚举
 */
public enum SystemErrorCode implements ErrorCode {
  /**
   * 系统内部错误
   */
  SYSTEM_ERR("system-err", "系统内部错误"),
  ;

  SystemErrorCode(String errorCode, String errorDesc) {
    this.errorCode = errorCode;
    this.errorDesc = errorDesc;
  }

  @Getter
  private String errorCode;
  @Getter
  private String errorDesc;
}

系统异常错误码枚举示例



import lombok.Getter;

public class UserException extends RuntimeException implements ErrorCode {

  @Getter
  private String errorCode;
  @Getter
  private String errorDesc;

  /**
   * @param errorCode 枚举的错误码类型,其中包含了错误码和对应的错误码描述信息.
   */
  public UserException(UserErrorCode errorCode) {
    super();
    this.errorCode = errorCode.getErrorCode();
    this.errorDesc = errorCode.getErrorDesc();
  }

  /**
   * @param errorCode 枚举的错误码类型,其中包含了错误码和对应的错误码描述信息.
   * @param message   最好是自定义的详细描述信息,用于打日志,也可以和{@link ErrorCode#getErrorDesc()}相同
   */
  public UserException(UserErrorCode errorCode, String message) {
    super(message);
    this.errorCode = errorCode.getErrorCode();
    this.errorDesc = errorCode.getErrorDesc();
  }

  /**
   * @param errorCode 枚举的错误码类型,其中包含了错误码和对应的错误码描述信息.
   * @param cause     嵌套的错误堆栈
   */
  public UserException(UserErrorCode errorCode, Throwable cause) {
    super(cause);
    this.errorCode = errorCode.getErrorCode();
    this.errorDesc = errorCode.getErrorDesc();
  }

  /**
   * @param errorCode 枚举的错误码类型,其中包含了错误码和对应的错误码描述信息.
   * @param message   最好是自定义的详细描述信息,用于打日志,也可以和{@link ErrorCode#getErrorDesc()}相同
   * @param cause     嵌套的错误堆栈
   */
  public UserException(UserErrorCode errorCode, String message, Throwable cause) {
    super(message, cause);
    this.errorCode = errorCode.getErrorCode();
    this.errorDesc = errorCode.getErrorDesc();
  }
}

包含各种构造方法的自定义异常

ps: 如果你看懂了这个异常类的构造方法定义的话,那你一定知道为什么错误码定义要用枚举了,因为只要不是字符串就行,否则会和官方默认的冲突,官方的默认字符串表示该错的描述性信息,而我们的不仅要利用描述信息同时能在堆栈中打印的特性,还需要将我们错误码和错误码描述信息传入(这里的描述信息一般是传到到调用方的,不是用来打日志的,所以这两处的描述一般是不相同的),一次传入多个信息,所以对象是我们最好的选择。

如有不足之处欢迎留言交流。

原文地址:https://www.cnblogs.com/laeni/p/11265101.html

时间: 2024-10-10 20:36:31

java异常和错误码定义规范及其使用的相关文章

【转】java异常报错大全

算术异常类:ArithmeticExecption 空指针异常类:NullPointerException 类型强制转换异常:ClassCastException 数组负下标异常:NegativeArrayException 数组下标越界异常:ArrayIndexOutOfBoundsException 违背安全原则异常:SecturityException 文件已结束异常:EOFException 文件未找到异常:FileNotFoundException 字符串转换为数字异常:NumberF

C++异常 返回错误码

一种比异常终止更灵活的方法是,使用函数的返回值来指出问题.例如,ostream类的get(void)成员ASCII码,但到达文件尾时,将返回特殊值EOF.对hmean()来说,这种方法不管用.任何树脂都是有效的返回值,因此不存在可用于指出问题的特殊值.在这种情况下,可使用指针参数或引用参数来将值返回给调用能够程序,并使用函数的返回值来指出成功还是失败.istream族重载>>运算符使用了这种技术的变体.通过告知调用程序是成功了还是失败了,使得程序可以采取异常终止程序之外的其他措施.下面的程序是

CMPP错误码说明

与中国移动代码的对应关系. MI::zzzzSMSC返回状态报告的状态值为EXPIREDMJ:zzzzSMSC返回状态报告的状态值为DELETEDMK:zzzzSMSC返回状态报告的状态值为UNDELIVML:zzzzSMSC返回状态报告的状态值为ACCEPTDMM:zzzzSMSC返回状态报告的状态值为UNKNOWNMN:zzzzSMSC返回状态报告的状态值为REJECTD 回页首 CMPP发送失败代码对照表 值(4位,不足4位前面补0) 含义1 消息结构错2 命令字错误3 消息序列号重复4

转!!CMPP 网关错误码说明

与中国移动代码的对应关系. MI::zzzzSMSC返回状态报告的状态值为EXPIREDMJ:zzzzSMSC返回状态报告的状态值为DELETEDMK:zzzzSMSC返回状态报告的状态值为UNDELIVML:zzzzSMSC返回状态报告的状态值为ACCEPTDMM:zzzzSMSC返回状态报告的状态值为UNKNOWNMN:zzzzSMSC返回状态报告的状态值为REJECTD 回页首 CMPP发送失败代码对照表 值(4位,不足4位前面补0) 含义1 消息结构错2 命令字错误3 消息序列号重复4

蓝牙-HCI错误码列表

错误码定义: 1 /* Success code */ 2 #define HCI_SUCCESS 0x00 3 /* Possible error codes */ 4 #define HCI_UNKNOWN_HCI_COMMAND 0x01 5 #define HCI_NO_CONNECTION 0x02 6 #define HCI_HW_FAILURE 0x03 7 #define HCI_PAGE_TIMEOUT 0x04 8 #define HCI_AUTHENTICATION_FAI

Java异常封装(自定义错误码和描写叙述,附源代码)

真正工作了才发现.Java里面的异常在真正工作中使用还是十分普遍的. 什么时候该抛出什么异常,这个是必须知道的. 当然真正工作里面主动抛出的异常都是经过分装过的,自己能够定义错误码和异常描写叙述. 以下小宝鸽就为大家介绍一个Java异常简单封装的样例. 在给出异常分装样例之前.须要给大家普及Java里面的checked异常和unchecked异常的个概念. 一.checked异常和unchecked异常 这里之所以让大家清楚checked异常和unchecked异常概念,是由于:待会我们的异常是

安装APK的错误码(PackageManager.java)

安装APK的错误码,定义在android源码中的这个文件中:frameworks\base\core\java\android\content\pm\PackageManager.java /** * if the package is already installed. * 程序已经存在 */ public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; /** * if the package archive file is inv

程序常见错误码及定义

一.授权/令牌请求接口返回码 描述应用发起授权请求或令牌请求时,开放平台的返回码. 错误码 错误描述 Error Description 10000 非法的请求参数 Invalid request 10001 用户认证失败 Invalid client 10002 非法的授权信息 Invalid grant 10003 应用没有被授权,无法使用所指定的grant_type Unauthorized client 10004 grant_type字段超过定义范围 Unsupported grant

Linux运维之Shell编程------(一)监控MySQL错误码及主从复制同步异常

Linux运维之Shell编程 一.监控MySQL错误码及主从复制同步异常 题目:监控MySQL主从同步是否异常,如果异常,则发送短信或者邮件给管理员.提示:如果没主从同步环境,可以用下面文本放到文件里读取来模拟:阶段1:开发一个守护进程脚本每30秒实现检测一次.阶段2:如果同步出现如下错误号(1158,1159,1008,1007,1062),则跳过错误.阶段3:请使用数组技术实现上述脚本(获取主从判断及错误号部分) 阶段2: #!/bin/bash#CONTACT='[email prote