JAVA基础-异常/日志

2020-06-15  本文已影响0人  lconcise

异常体系

image.png

Throwable

  1. Error
  2. Exception
    1. RuntimeException 运行期异常,需要修改代码
    2. 非RuntimeException 编译器异常,必须处理,否则编译不通过

异常的处理

try...catch...finally的处理格式:

    try {
        可能出现问题的代码;
    }catch(异常名 变量) {
        针对问题的处理;
    }finally {
        释放资源;
    }

异常处理的变形:
try...catch...finally
try...catch...
try...catch...catch...
try...catch...catch...fianlly
try...finally

常见面试题

  1. 编译器异常和运行期异常的区别?
    编译器异常:必须处理,否则编译不通过
    运行期异常:可以不处理,也可以处理

  2. throw和throws的区别
    a. throw 在方法体中,后面跟的是异常,并且只能是一个
    throw抛出的是一个异常对象,说明这里肯定有一个异常产生了
    b. throws 在方法声明上,后面跟的是异常的类名,可以是多个
    throws 是声明方法有异常,是一种可能性,这个异常并不一定会产生

  3. finally关键字及其面试题
    a. finally{}代码块比return先执行
    b. 多个return是按顺序执行的,多个return执行了一个后,后面的return就不会执行了
    c. 不管有没有异常抛出,finally都会在return返回前执行

    1. finally 用于释放资源,它的代码永远会执行。特殊情况:在执行到finally之前jvm退出了
    2. final、finally、finalize的区别?
      final : 最终的意思,可以修饰类、成员变量、成员方法
      修饰类,类不能被继承
      修饰变量,变量是常量
      修饰方法,方法不能被重写
      finally: 用于释放资源,它的代码永远会执行。特殊情况:在执行到finally之前jvm退出了
      finalize: 是Object类的一个方法,用于垃圾回收
    3. 如果在catch里面有return,请问finally还执行吗?如果执行,在return前还是后
      会,前

自定义异常

/**
 * 自定义异常可以继承Exception.RuntimeException
 */
public class MyException extends RuntimeException {

    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

// 抛出异常
public class Teacher {

    public void check(int score) throws MyException {
        if (score < 0 || score > 100) {
            throw new MyException("分数必须在0-100之间");
        }
        System.out.println("分数没有问题");
    }
}

// 测试异常
public class MainDemo {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入学生成绩:");

        Teacher teacher = new Teacher();
        while (sc.hasNext()) {
            int score = sc.nextInt();
            try {
                teacher.check(score);
            } catch (MyException e) {
                e.printStackTrace();
            }
        }
    }
}

执行结果:


image.png

异常处理 阿里规约

  1. 【强制】 Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过
    catch 的方式来处理,比如: NullPointerException, IndexOutOfBoundsException 等等。
    说明: 无法通过预检查的异常除外,比如,在解析字符串形式的数字时, 可能存在数字格式错误, 不得不通过 catch NumberFormatException 来实现。
    正例: if (obj != null) {...}
    反例: try { obj.method(); } catch (NullPointerException e) {…}
  2. 【强制】异常不要用来做流程控制,条件控制。
    说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。
  3. 【强制】 catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。
    对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。
    说明: 对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,
    这是一种不负责任的表现。
    正例: 用户注册的场景中,如果用户输入非法字符, 或用户名称已存在, 或用户输入密码过于简单,在程序上作出分门别类的判断,并提示给用户。
  4. 【强制】 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层业务使用者,必须处理异常,将其转换为用户可以理解的内容。
  5. 【强制】 事务场景中,抛出异常被 catch 后,如果需要回滚,一定要注意手动回滚事务。
  6. 【强制】 finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
    说明: 如果 JDK7 及以上,可以使用 try-with-resources 方式。
  7. 【强制】不要在finally块中使用return。
    说明:try块中的return语句执行成功后,并不马上返回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try块中的返回点。
    private int x = 0;

    public int checkReturn() {
        try {
            // x=1.此处不返回
            return ++x;
        } finally {
            // 返回结果是2
            return ++x;
        }
    }
  1. 【强制】 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
    说明: 如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

  2. 【强制】 在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable
    类来进行拦截。
    说明: 通过反射机制来调用方法,如果找不到方法,抛出 NoSuchMethodException。什么情况会抛出NoSuchMethodError 呢?二方包在类冲突时,仲裁机制可能导致引入非预期的版本使类的方法签名不匹配,或者在字节码修改框架(比如: ASM)动态创建或修改类时,修改了相应的方法签名。这些情况,即使代码编译期是正确的,但在代码运行期时,会抛NoSuchMethodError。

  3. 【推荐】 防止 NPE,是程序员的基本修养,注意NPE产生的场景:

    1. 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生NPE。
      反例:public inf f() {return Integer 对象},如果为null,自动解箱有可能产生NPE。
    2. 数据库的查询结果可能为null。
    3. 集合里面的元素即使isNotEmpty,取出的数据元素也可能为null。
    4. 远程调用返回对象时,一律要求进行空指针判断,放置NPE。
    5. 对于Session中获取的数据,建议进行NPE检查,避免空指针。
    6. 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
      正例:使用JDK8的Optional类来防止NPE问题。

日志规约(阿里)

  1. 【强制】 应用中不可直接使用日志系统( Log4j、 Logback) 中的 API,而应依赖使用日志框架( SLF4J、 JCL--Jakarta Commons Logging) 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
    说明: 日志框架( SLF4J、 JCL--Jakarta Commons Logging)的使用方式(推荐使用 SLF4J)
    使用 SLF4J:
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    private static final Logger logger = LoggerFactory.getLogger(Test.class);
    使用 JCL:
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    private static final Log log = LogFactory.getLog(Test.class);
  2. 【强制】 所有日志文件至少保存 15 天,因为有些异常具备以“周” 为频次发生的特点。 对于
    当天日志,以“应用名.log” 来保存,保存在/home/admin/应用名/logs/</font>目录下,
    过往日志格式为: {logname}.log.{保存日期},日期格式: yyyy-MM-dd
    说明: 以 mppserver 应用为例,日志保存在/home/admin/mppserver/logs/mppserver.log,历史日志名称为 mppserver.log.2016-08-01
  3. 【强制】 应用中的扩展日志( 如打点、临时监控、访问日志等) 命名方式:
    appName_logType_logName.log。 logType:日志类型, 如 stats/monitor/access 等; logName:日志描述。这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。
    说明: 推荐对日志进行分类, 如将错误日志和业务日志分开存放,便于开发人员查看,也便于通过日志对系统进行及时监控。
    正例: mppserver 应用中单独监控时区转换异常,如: mppserver_monitor_timeZoneConvert.log
  4. 【强制】 在日志输出时,字符串变量之间的拼接使用占位符的方式。
    说明:因为String字符串的拼接会使用StringBuilder的append()方式,有一定的性能损耗。使用占位符仅是替换动作,可以有效提升性能。
    正例: logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);
  5. 【强制】 对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。
    说明: 虽然在 debug(参数)的方法体内第一行代码 isDisabled(Level.DEBUG_INT)为真时( Slf4j 的常见实现
    Log4j 和 Logback),就直接 return,但是参数可能会进行字符串拼接运算。此外,如果 debug(getName())
    这种参数内有 getName()方法调用,无谓浪费方法调用的开销。
    正例:
      // 如果判断为真,那么可以输出 trace 和 debug 级别的日志
      if (logger.isDebugEnabled()) {
          logger.debug("Current ID is: {} and name is: {}", id, getName());
      }
  1. 【强制】 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过
    关键字 throws 往上抛出。
    正例: logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
  2. 【强制】 日志打印时禁止直接用 JSON 工具将对象转换成 String。
    说明: 如果对象里某些 get 方法被重写,存在抛出异常的情况,则可能会因为打印日志而影响正常业务流程的执行。
    正例: 打印日志时仅打印出业务相关属性值或者调用其对象的 toString()方法。
  3. 【推荐】 谨慎地记录日志。生产环境禁止输出 debug 日志; 有选择地输出 info 日志; 如果使用warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘爆,并记得及时删除这些观察日志。
    说明: 大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。 记录日志时请思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?
上一篇 下一篇

猜你喜欢

热点阅读