• 《Java核心技术(卷1)》笔记:第7章 异常、断言和日志


    异常

    1. (P 280)异常处理需要考虑的问题:

      • 用户输入错误
      • 设备错误
      • 物理限制
      • 代码错误
    2. (P 280)传统的处理错误的方法是:返回一个特殊的错误码,常见的是返回-1或者null引用

    3. (P 280)在Java中,方法出现错误时,它会立即退出,不返回任何值,而是抛出一个封装了错误信息的对象

    4. (P 280)Java中所有的异常都是由Throwable继承而来,它下面又分解为两个分支:ErrorException

      • Error:描述了Java运行时系统的内部错误资源耗尽错误(对于处理这种错误,你几乎无能为力
      • Exception:又分解为两个分支:RuntimeException(由编程错误导致,如果出现该异常,那一定是你的问题)和其他异常(诸如IO错误这类问题)
        • 派生于RuntimeException的异常:
          • 错误的强制类型转换
          • 数组访问越界
          • 访问null指针
        • 不是派生于RuntimeException的异常:
          • 试图超越文件末尾继续读取数据
          • 试图打开一个不存在的文件
          • 试图根据特定的字符串查找Class对象,而这个字符串表示的类并不存在
      graph TD Throwable[Throwable]-->Error[Error] Throwable[Throwable]-->Exception[Exception] Error[Error]-->OtherError1[...] Error[Error]-->OtherError2[...] Error[Error]-->OtherError3[...] Exception[Exception]-->RuntimeException[RuntimeException] Exception[Exception]-->IOException[IOException] Exception[Exception]-->OtherException[...] IOException[IOException]-->OtherIOException1[...] IOException[IOException]-->OtherIOException2[...] IOException[IOException]-->OtherIOException3[...] RuntimeException[RuntimeException]-->OtherRuntimeException1[...] RuntimeException[RuntimeException]-->OtherRuntimeException2[...] RuntimeException[RuntimeException]-->OtherRuntimeException3[...]
    5. (P 281)派生于ErrorRuntimeException的所有异常称为非检查型异常,所有其他异常称为检查型异常

    6. (P 282)如果没有处理器捕获异常对象,那么当前执行的线程就会终止

    7. (P 283)必须在方法的首部列出所有检查型异常类型,但是不需要声明从Error继承的异常,也不应该声明从RuntimeException继承的那些非检查型异常

    8. (P 283)如果在子类中覆盖了超类的一个方法,子类方法中声明的检查型异常不能比超类方法中声明的异常更通用(子类方法可以抛出更特定的异常,或者根本不抛出任何异常)。如果超类方法没有抛出任何检查型异常,子类也不能抛出任何检查型异常

    9. (P 288)同一个catch子句中可以捕获多个异常类型,如果一些异常的处理逻辑是一样的,就可以合并catch子句。只有当捕获的异常类型彼此之间不存在子类关系时才需要这个特性

      try {
          ...
      } catch (FileNotFoundException | UnknownHostException e) {
          ...
      } catch (IOException e) {
          ...
      }
      
    10. (P 289)可以在catch子句中抛出一个异常,此时,可以把原始异常设置为新异常的“原因”

      try {
          ...
      } catch (SQLException original) {
          var e = new ServletException("database error");
          e.initCause(original);
          throw e;
      }
      

      捕获异常时,获取原始异常

      Throwable original = caughtException.getCause();
      
    11. (P 292)一种推荐的异常捕获写法:内层try语句块只有一个职责,就是确保释放资源外层try语句块也只有一个职责,就是确保报告出现的错误

      try {
          try {
              ...
          } finally {
              // 释放资源
          }
      } catch (Exception e) {
          // 报告错误
      }
      
    12. (P 293)Java 7中,对于实现了AutoCloseable接口的类,可以使用带资源的try语句(try-with-resources):

      try (Resources res = ...) {
          // Work with res
          ...
      }
      

      Java 9中,可以在try首部中提供之前声明的事实最终变量(effectively final variable):

      try (res) {
          // Work with res
          ...
      } // res.close() called here
      
    13. (P 294)在try-with-resources语句中,如果try块抛出一个异常,而且close方法也抛出一个异常,则原来的异常会重新抛出,而close方法抛出的异常会“被抑制”(可以通过getSuppressed方法得到这些被抑制的异常)

    14. (P 294)可以通过StackWalker类处理堆栈轨迹

      var walker = StackWalker.getInstance();
      walker.forEach(frame -> ...); // 例如:walker.forEach(System.out::println);
      
    15. (P 298)使用异常的一些技巧:

      • 异常处理不能代替简单的测试(捕获处理异常的成本很高,只在异常情况下使用异常
      • 不要过分的细化异常(有必要将整个任务包在一个try语句块中,将正常处理与错误处理分开
      • 充分利用异常层次结构
      • 不要压制异常(异常非常重要时,应该适当地进行处理)
      • 在检测错误时,“苛刻”要比放任更好
      • 不要羞于传递异常(最好继续传递异常,而不是自己捕获)

    断言

    1. (P 301)Java中引入了关键字assert,其有如下两种形式:

      assert condition;
      assert condition : expression;
      

      这两个语句都会计算条件,如果结果为false,则抛出一个AssertionError异常。在第二个语句中,表达式将传入AssertionError对象的构造器,并转换为一个消息字符串

    2. (P 301)默认情况下,断言是禁用的,可以使用-enableassertions或者-ea选项启用断言:

      java -enableassertions MyApp
      

      禁用断言可以使用-disableassertions-da

    3. (P 302)断言只应该用于在测试阶段确定程序内部错误的位置

    日志

    1. (P 305)基本日志的使用:

      • 生成简单的日志记录

        Logger.getGlobal().info("hello world!");
        
      • 取消所有日志

        Logger.getGlobal().setLevel(Level.OFF);
        
    2. (P 305)高级日志的使用:

      • 创建或获取日志记录器

        private static final Logger myLogger = Logger.getLogger("className"); // className是全限定类名
        
      • 设置日志级别

        logger.setLevel(Level.FINE); // FINE以及更高级别的日志都会被记录
        
      • 记录日志

        // 调用相应级别的日志记录方法
        logger.warning(message);
        logger.fine(message);
        
        // 使用log方法并指定级别
        logger.log(Level.FINE, message);
        
        // 跟踪执行流的方法
        logger.entering("className", "methodName", new Object[]{ params... });
        logger.exiting("className", "methodName", result);
        
        // 在日志记录中包含异常的描述
        logger.throwing("className", "methodName", exception);
        logger.log(Level.WARNING, message, exception);
        
    3. (P 305)7个日志级别:

      • SEVERE
      • WARNING
      • INFO
      • CONFIG
      • FINE
      • FINER
      • FINEST
    4. (P 307)可以通过配置文件修改日志系统的各个属性,默认情况下,配置文件位于:

      conf/logging.properties
      

      指定特定位置的配置文件:

      java -Djava.util.logging.config.file=configFile MainClass
      

      指定日志记录器的日志级别:在日志记录器名后面追加后缀.level,例如

      com.mycompany.myapp.level=FINE
      
    5. (P 313)日志技巧

      • 对一个简单的应用,选择一个日志记录器,可以把日志记录器命名为与主应用包一样的名字
      • 默认的日志配置会把级别等于或高于INFO的所有消息记录到控制台,用户可以覆盖这个默认配置,最好在你的应用中安装一个更合适的默认配置
      • 所有级别为INFO、WARNING和SEVERE的消息都将显示到控制台上
        • 只将对程序用户有意义的消息设置为以上这几个级别
        • 程序员想要的日志消息设定为FINE级别是一个很好的选择
    6. (P 321)调试技巧

      • 打印或日志记录变量的值

      • 在每一个类中放置一个单独的main方法,以便独立地测试类

      • 使用JUnit

      • 日志代理,它是一个子类的对象,可以截获方法调用,记录日志,然后调用超类中的方法

        var generator = new Random() {
            public double nextDouble() {
                double result = super.nextDouble();
                Logger.getGlobal().info("nextDouble: " + result);
                return result;
            }
        }
        
      • 利用Throwable类的printStackTrace方法,可以从任意的异常对象获得堆栈轨迹

      • 一般来说,堆栈轨迹显示在System.err上。如果想要记录或显示堆栈轨迹,可以将它捕获到一个字符串中

        var out = new StringWriter();
        new Throwable().printStackTrace(new PrintWriter(out));
        String description = out.toString();
        
      • 通常,将程序错误记入一个文件会很有用:

        java MyProgram > errors.txt        # 错误,错误被发送到System.err而不是System.out
        java MyProgram 2> errors.txt       # 正确,只输出System.err
        java MyProgram 1> errors.txt 2>&1  # 同时捕获System.out和System.err
        
      • 将未捕获的异常的堆栈轨迹记录到一个文件中,而不是直接输出到System.err,可以使用静态方法Thread.setDefaultUncaughtExceptionHandler改变未捕获异常的处理器

        Thread.setDefaultUncaughtExceptionHandler(
            new Thread.UncaughtExceptionHandler() {
                public void uncaughtException(Thread t, Throwable e) {
                    // save information in log file
                }
            }
        )
        
      • 要想观察类的加载过程,启动Java虚拟机时可以使用-verbose标志

      • -Xlint选项告诉编译器找出常见的代码问题

        javac -Xlint sourceFiles
        
      • Java虚拟机增加了对Java应用程序的监控和管理支持,允许在虚拟机中安装代理来跟踪内存消耗、线程使用、类加载等情况。jconsole工具可以显示有关虚拟机性能的统计结果

      • Java任务控制器:一个专业级性能分析和诊断工具

  • 相关阅读:
    unix网络编程源码编译问题
    ubuntu15.04下安装docker
    hexo博客的相关配置
    hexo的jacman主题配置
    使用github和hexo搭建静态博客
    操作系统简单认识
    github for windows安装以及教程
    编译原理第五单元习题
    python3入门之列表和元组
    Python3入门之软件安装
  • 原文地址:https://www.cnblogs.com/qinjinyu/p/13182308.html
Copyright © 2020-2023  润新知