[core java学习笔记][第十一章异常断言日志调试]

第11章 异常,断言,日志,调试

  • 处理错误
  • 捕获异常
  • 使用异常机制的技巧
  • 使用断言
  • 日志
  • 測试技巧
  • GUI程序排错技巧
  • 使用调试器

11.1 处理错误

11.1.1异常分类

  • 都继承自Throwable类
  • 分成Error和Exception
    • Error类

      描写叙述了Java运行时系统的内部错误和资源耗尽错误。

      应用程序不应该抛出此种类型的错误。假设出现了这样的内部错误。除了通告给用户,并尽力使程序安全地终止外,再也无能为力

    • Exception层次结构:最需关注的
      • RuntimeException 程序错误导致的异常

        • 错误的类型转换
        • 数组訪问越界
        • 訪问空指针
      • 不是派生于RuntimeException 因为像I/O错误,程序本身没有问题导致的异常
        • 试图在文件尾部读取后面数据
        • 试图打开一个不存在的文件
        • 试图依据给定的字符串查找Class对象。而那个字符串表示的类不存在
  • java语言规范 派生于 Error类或RuntimeException类的全部异常称为未检查(unchecked)异常,全部其它异常称为已检查(checked)异常。

11.1.2 声明已检查异常

  • 下面四种情况自己编写方法时。须要抛出异常

    • 调用一个抛出checked异常的方法。比如,FileInputStream构造器
    • 程序运行发现错误,而且利用throw语句抛出一个checked异常
    • Java虚拟机和运行时库出现的内部错误
  • 对于可能被他人使用的Java方法,更具异常规范(exception specification),在方法的首部声明这种方法可能抛出异常,假设有多个用逗号隔开
    class MyAnimation
    {
        ...
        public Image loadImage(String s) throws IOException FileNotFoundException
        {
                ...
        }
    }
    
  • 关于子类覆盖超类的方法那一块没看懂

11.1.3 怎样抛出异常

  1. 找到一个合适的异常类
  2. 在方法声明
  3. 创建这个类的一个对象
  4. 将对象抛出
    String readData(Scanner in) thros EOFException
    {
        ...
        while(...)
        {
            if(!in.hasNext())
            {
                if(n<len)
                    throw new EOFException();
            }
        }
        ...
        return S;
    }
    
    //还能含有一个字符串參数的构造器
    
    String girpe="Content-length " +len +",Recived" + n ;
    throw new EOFException(girpe);
    

11.1.4 创建异常类

  • 创建一个派生于Exception的类 ,或者派生于Exception子类的类。
  • 一般须要定义两个构造器,一个是默认的,另一个是带有具体描写叙述信息的构造器(超类Throwable的toString方法将会打印这些具体信息)
    class  FileFormatException extends IOException
    {
        public FileFormatException() {}
        public FileFormatException(String gripe)
        {
            super(gripe);
        }
    }
    
  • String getMessage()

    能获得Throwable对象具体描写叙述信息,即构造时丢进去的String。

11.2 捕获异常

  • 假设异常没有被捕获,程序将会停止运行
  • 假设想捕获异常,下面是最简单的try/catch 语句块
    try
    {
        code
        more code
        more code
    }
    catch (ExceptionType e)
    {
        handler for this type
    }
    
  • 假设try语句块中的不论什么代码抛出了在catch子句中说明的异常。那么
    1. 跳过剩下的try 语句
    2. 将运行catch子句中的处理器代码
  • 下面是个简单的样例
    public void read(String filename)
    {
        try
        {
            InputStrem in = new FileInputStream(filename);
            int b;
            while((b!=in.read())!=-1)
            {
                process input;
            }
        }
        catch (IOException exception)
        {
            exception.printStackTrace();
        }
    
    }
    

对于以上代码

通常最好的选择是什么都不做,而是将异常传递给调用者。

假设read方法出现了错误,就让read方法的调用者去担心!

假设採用这样的处理方式,就必须声明这种方法可能会抛出一个IOException

public void read(String filename) throws IOException
{
        InputStrem in = new FileInputStream(filename);
        int b;
        while((b!=in.read())!=-1)
        {
            process input;
        }
}
  • 不同意子类覆盖超类的方法中throws说明符超过超类所列出的异常范围。假设有不属于的,不能throws掉,仅仅能自己捕获处理

11.2.1 捕获多个异常

  • 基本语句

    try
    {
    
    }
    catch (FileNotFoundException e)
    {
    
    }
    catch (UnknownHostException e)
    {
    
    }
    catch (IOException e)
    {
    
    }
    
  • 假设须要获得具体信息
    e.getMessage() //具体错误信息
    
    e.getClass().getName()  //异常对象的实际类型
    
  • 假设处理方式一样 合并catch语句
    try
    {
        code..
    }
    catch (FileNotFoundException | UnknownHostException e)
    {
    
    }
    

11.2.2 再次抛出异常或异常链

  • 捕获异常再次抛出的基本方法

    try
    {
        access the database
    }
    catch (SQLException e)
    {
        throw new ServletException("database error: "+e.getMessage());
    }
    
  • 另一种能不丢失原始异常的方法
    try
    {
        access the database
    }
    catch (SQLException e)
    {
        Throwable se=new ServletException("database error");
        se.initCause(e);
        throw se;
    }
    

    当捕获异常时。能够用下面语句又一次得到原始异常:

    Throwable e = se.getCause();
    

    书上强烈建议这样的包装方式,不丢失原异常的细节。

  • 有时仅仅是记录异常
    try
    {
        access the database
    }
    catch (SQLException e)
    {
        logger.log(level,message,e);
        throw e;
    }
    

11.2.3 finaly子句

  • 基本的语法

    try
    {
    }
    catch(Exception e)
    {
    }
    finally
    {
            in.close();
    }
    
  • 不管try中抛出异常,还是catch中抛出异常,总而言之finall总会运行
  • 强烈使用try/catch 和 try/finally语句块单独,而不是基本的语法的使用方法。例如以下:
    InputStream in= ... ;
    try
    {
        try
        {
            code that might throw exceptions
        }
        finally
        {
            in.close();
        }
    }
    catch(IOException e)
    {
        show error message
    }
    

    内层的try仅仅有一个职责,确认关闭输入流。外层的try 用来确保报告出现的错误。

  • 注意:当finallycatch包括return语句,会运行finallyreturn
  • 注意finally 也可能抛出异常,而导致本来要catch抛出的异常被覆盖

11.2.4 带资源的try语句

  • 假如资源实现了AutoCloseable/Closeable接口的类。能够利用带资源的try语句

    try(Resource res=...)
    {
        work with res
    }
    
  • try 退出时,会自己主动调用res.close()。下面给出一个典型的样例。
    try (Scanner in=new Scanner(new FileInputStream("/usr/share/dict/words")))
    {
        while (in.hasNext())
            System.out.println(in.next());
    }
    
  • 还能指定多个资源,比如:
    try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")),
        PrintWriter out=new PrintWriter("out.txt"))
    {
        while(in.hasNext())
            out.println(in.next().toUpperCase());
    }
    
  • 假设.close()也抛出了异常,可是不会覆盖原来该抛出的异常,而是被抑制。假设你想知道这些被抑制的异常,能够通过getSuppressed方法。

11.2.5 分析堆栈跟踪元素

  • 堆栈跟踪(stack trace)是一个方法调用过程的列表。
  • 比較灵活的方式是使用getStackTrace()

    它会得到StackTraceElement对象的一个数组。

    比如:

    Throwable t = new Throwable();
    StackTraceElement[] frames = t.getStackTrace();
    for(StackTraceElement f : frames)
        System.out.println(f);
    输出
    factorial(4):
    StackTraceTest.factorial(StackTraceTest.java:8)
    StackTraceTest.factorial(StackTraceTest.java:14)
    StackTraceTest.factorial(StackTraceTest.java:14)
    StackTraceTest.factorial(StackTraceTest.java:14)
    StackTraceTest.factorial(StackTraceTest.java:14)
    StackTraceTest.factorial(StackTrcaceTest.java:14)
    StackTraceTest.main(StackTraceTest.java:23)
    

    能获得文件名称,类名。当前运行的代码行号

  • 静态的Thread.getAllStackTrace方法,获得全部线程的堆栈跟踪。下面是样例
    Map<Thread,StackTraceElement[]> map=Thread.getAllStackTraces();
    for(Thread t : map.keySet())
    {
        StackTraceElement[] frames=map.get(t);
            for(StackTraceElement f : frames)
                System.out.println(f);
    }
    

11.3 使用异常机制的技巧

  • 异常不能取代简单的測试
  • 不要过分细化异常
  • 利用异常层次结构
  • 不要羞于传递异常。有时候你是类设计者,应该由使用者决定对异常怎么样

11.4 使用断言

  • assertkeyword有两个表达形式

    assert 条件;
    //为false ,抛出一个AssertionError异常
    assert 条件:表达式;
    //表达式传入异常作为一个消息字符串。
    

11.4.1 启用或者禁用断言

  • -ea或 -enableassertions启用,默认是禁用
  • 也能启动部分包的断言,也能金庸部分包的断言

11.4.2 使用断言完毕參数检查

  • 断言失败是致命的,不可恢复的错误。

  • 断言检查仅仅用于开发和測试阶段。
  • 断言是一种測试和调试阶段所使用的战术性工具,而日志记录是一种在程序的整个生命周期都能够使用的策略工具。

11.5 记录日志

11.5.1 基本日志

  • 日志系统管理着一个名为Logger.global的默认日志记录器,能够用System.out替换它,并通过info方法记录日志信息

    Logger.getGlobal().info("File->Open menu item selected");
    //print
    //三月 15, 2016 7:33:25 下午 log main
    //信息: File->Open menu item selected
    

    自己主动包括了时间,调用的类名和方法。

  • Logger.gelGlobal().setLevel(Level.OFF)来取消全部日志

11.5.2 高级日志

  • 调用getLogger方法能够创建或检索记录器

    Logger myLogger= Logger.getLogger("log.zhouyong");
    
  • 假设对日志设置了日志级别。那么它的子记录器也会继承这个属性
  • 有一下7个日志记录器级别
    • SEVERE
    • WARNING
    • INFO
    • CONFIG
    • FINE
    • FINER
    • FINEST

    在默认情况。仅仅记录前三个级别。也能够设置其它级别。比如:

    logger.setLevel(Level.FINE)
    

    如今,FINE和更高级别的记录都能够记录下来

    另外能够使用Level.ALL 开启全部 Level.OFF 关闭全部

  • 有下面几种记录方式
    logger.warning(message);
    logger.fine(message);
    //同一时候还能够用log方法指定级别
    logger.log(Level.FINE,message);
    
  • 一般用CONFIG,FINE等记录有助于诊断,但对于程序猿没有太大意义的调试信息。
  • 默认的日志记录将显示包括日志调用类名和方法名。可是假设虚拟机进行了优化,可能无法得到准确的信息。此时须要logp方法获得调用类和方法的确切位置,签名例如以下:
    void logp(level l,String className,String methodName,String message)
    

    下面另一些跟踪运行流的方法

    void entering(String className,String methodName)
    void entering(String className,String methodName,Object param)
    void entering(String className,String methodName,Object[] params)
    void exiting(String className,String methodName)
    void exiting(String className,String methodName,Object result)
    

    比如:

    不知道有什么用。。
    
    
  • 记录日志的经常使用用途是记录那些不可预料的异常。能够使用一下两种方式。
    void throwing(String className,String methodName,Throwable t)
    void log(Level l,String message ,Throwable t)
    

    典型的使用方法是:

    if(...)
    {
        IOExcption exception = new IOException("...");
        logger.throwing("com.my","read",exception);
        throw exception;
        //FINER级别
    }
    

    还有

    try
    {
        ...
    }
    catch (IOException e)
    {
        Logger.getLogger("...").log(Level.WARNING,"Reading image",e);
    }
    

11.5.3 改动日志管理器配置

  • 默认情况,配置文件存在于:

    e/lib/logging.properties
    
  • 要想使用自己的配置文件 须要
    java -Djava.util.logging.config.file=configFile MainClass
    
  • 改动默认的日志记录级别
    .level=INFO
    

    还能改动自己日志记录级别

    com.mycompany.myapp.level=FINE

  • 控制台也有输出级别限制

    java.util.logging.ConsoleHandler.level=FINE

  • 日志属性由java.util.logging.LogManager类处理。具体看API

11.5.4 本地化

  • 不太懂 以后了解

11.5.5 处理器

  • 日志记录器先会将记录发送到父处理器中,终于的处理器有一个ConsoleHandle
  • 对于一个要被记录的日志记录,它的日志记录级别必须高于日志记录器和处理器的阈值。

    要想记录FINE级别的日志。就必须改动配置文件里的默认日志记录级别和处理器级别。

    另外,还能够绕过配置文件,安装自己的处理器。

    //控制台处理器

    Logger logger=Logger.getLogger("log.zhouyong");
    logger.setLevel(Level.FINE);
    logger.setUseParentHandlers(false);
    Handler handler = new ConsoleHandler();
    handler.setLevel(Level.FINE);
    logger.addHandler(handler);
    logger.log(Level.FINE,"dddd");
    

    在默认情况,日志记录器会将记录发送给自己的处理器和父处理器。

    父处理器就是一般的默认处理器,可是既然我们有了自己的处理器,能够把父处理器关了。免得控制台发送了两次记录

  • 要想将日志发送到别的地方,就须要其它处理器。
    • FileHandler 收集文件里的日志
    • SocketHandler。 发送到特定的主机和port
  • FileHandler
    FileHandler handler = new FileHandler();
    handler.setLevel(Level.FINE);
    logger.addHandler(handler);
    logger.log(Level.FINE,"dddd");
    

    文件在User底下。格式为XML。例如以下:

    <?xml version="1.0" encoding="GBK" standalone="no"?
    
    >
    <!DOCTYPE log SYSTEM "logger.dtd">
    <log>
    <record>
      <date>2016-03-15T23:40:17</date>
      <millis>1458056417956</millis>
      <sequence>0</sequence>
      <logger>log.zhouyong</logger>
      <level>FINE</level>
      <class>log</class>
      <method>main</method>
      <thread>1</thread>
      <message>dddd</message>
    </record>
    </log>
    
  • 还能有很多其它的复杂方式来处理完毕自己想要的要求

11.5.6 过滤器

  • 每一个记录器和处理器都能够有一个可选的过滤器来完毕附加的过滤
  • 能够通过实现Filter接口并定义下列方法来自己定义过滤器
    boolean isLoggable(LogRecord record)
    
  • setFilter方法安装过滤器

11.5.7 格式化器

  • ConsoleHandler类FileHandler能够生成文本或 XML格式的日志记录。可是也能够自己定义格式。
  • 通过继承Formatter类,并覆盖一下方法。
    String format(LogRecord record)
    
  • 能够依据自己意愿对记录的信息进行格式化,并返回结果字符串。
  • 然后用setFormatter方法将格式化器安装到处理器中。

11.5.8 日志记录说明

时间: 2024-10-20 06:10:54

[core java学习笔记][第十一章异常断言日志调试]的相关文章

Java学习笔记—第十一章 多线程机制

第十一章 Java多线程机制 了解Java中的进程与线程 1.1 进程:一般程序的结构大致可分为一个入口.一个出口和一个顺序执行的语句序列.程序运行时,系统从程序入口开始,按照语句的执行顺序(包括顺序.分支和循环)完成相应指令,然后从出口退出,程序结束.这样的结构称为进程.可以说,进程就是程序的一次动态执行的过程.一个进程既包括程序的代码,同时也包括系统的资源,如CPU.内存空间等.不同的进程所占用的系统资源都是独立的. 1.2 线程:线程是比进程更小的执行单位.一个进程在执行过程中,为了同时完

o&#39;Reill的SVG精髓(第二版)学习笔记——第十一章

第十一章:滤镜 11.1滤镜的工作原理 当SVG阅读器程序处理一个图形对象时,它会将对象呈现在位图输出设备上:在某一时刻,阅读器程序会把对象的描述信息转换为一组对应的像素,然后呈现在输出设备上.例如我们用SVG的<filter>元素指定一组操作(也称作基元,primitive),在对象的旁边显示一个模糊的投影,然后把这个滤镜附加给一个对象: <fliter id="drop-shadow"> <!-- 这是滤镜操作 --> </fliter&g

第十一章 异常,日志,断言和调试

第十一章 异常,日志,断言,调试 由于程序的错误或一些外部环境的影响造成用户数据的丢失,用户就有可能不再使用这个程序了.为了避免,应该做到以下几点: 向用户通告错误 保存所有的操作结果 允许用户以适当的形式退出程序. 11.1 处理异常 当由于出现错误而使得某些操作没有完成,程序应该: 返回到一种安全状态,并能够让用户执行一些其他命令:或者 允许用户保存所有操作的结果,以适当的方式终止程序 异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种错误的处理器.程序中可能出现的错误和问题: 用

我的java学习笔记(一篇文章不定时更新)

Java学习笔记 一.      原则战略思想 l  方法:快速原型 l  小目标:找一个pdf书籍快速有一个小项目出现 l  辅助:计时 记录各个学习环节的时间消耗. l  辅助:音乐 激情 l  您在博客园的登录用户名是:杂家随谈  [email protected] l 二.      WBS以及计时 步骤 计划时间 实际时间 搭建环境 2015/5/26 PDF 2015-5-26 小项目大框架实施 2015-6-1 三.      杂感 感觉难的时候,不妨站高点,站远一点看问题. 天下

Core Java 学习笔记——1.术语 环境配置/Eclipse汉化字体快捷键/API文档

今天起开始学习Java,学习用书为Core Java.之前有过C的经验.准备把自己学习这一本书时的各种想法,不易理解的,重要的都记录下来.希望以后回顾起来能温故知新吧.也希望自己能够坚持把自己学习这本书的整个过程记录下来. I want to put a ding in the universe. 基本术语:       Object Oriented Programming——OOP——面向对象编程 Application Programming Interface——API——应用程序编程接

Android学习笔记—第十一章 Fragment

第十一章 Fragment android-supportV4: Android在新版本新增功能的兼容包,最低兼容1.6 路径:adt-bundle-windows-x86-20131030\sdk\extras\android\support\v4 功能:Fragment.ViewPager.视频播放 查看supportV4源代码: (1)在项目lib文件夹上new→file,文件名为android-support-v4.jar.properties (2)编辑文件内容: src=xxx:\\

《JAVA编程思想》学习笔记——第十一章 持有对象

JAVA容器类类库的用途是 "保存对象",并将其划分为两个不同的概念: 1) Collection.一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的顺序保存元素,而Set不能用重复元素.Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同) 2)Map.一组成对的 "键值对" 对象,允许你使用键来查找值.ArrayList允许你使用数字 Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数

【Java学习笔记之三十一】详解Java8 lambda表达式

Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前,如果想将行为传入函数,仅有的选择就是匿名类,需要6行代码.而定义行为最重要的那行代码,却混在中间不够突出.Lambda表达式取代了匿名类,取消了模板,允许用函数式风格编写代码.这样有时可读性更好,表达更清晰.在Java生态系统中,函数式表达与对面向对象的全面支持是个激动人心的进步.将进一步促进并行

Java学习笔记—第六章 流程控制语句

第六章  熟悉Java的流程控制语句 Java的程序流程控制分为顺序结构.选择结构.循环结构和跳转语句. 顺序结构:按照程序代码自上而下执行,直到程序结束,中间没有任何判断和跳转. 选择结构(分支结构):判断给定的条件,根据判断结果控制程序的流程.包括if语句和switch语句. 2.1 if语句:通过判断给定表达式的值来决定程序的流程.常见if语句的形式有三种: (1)if(expression){ statement: } (2)if(expression){ statement; }els