第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 使用断言

  • assert关键字有两个表达形式

    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。 发送到特定的主机和端口
  • 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 日志记录说明

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

  1. [core java学习笔记][第六章接口与内部类]

    接口域内部类 接口 描述类具有什么功能,不给出具体实现. 内部类 用于设计协作关系的类集合 代理 实现任意接口的对象. 6.1 接口 接口声明 public interface Comparable ...

  2. [core java学习笔记][第四章对象与类]

    4.3 用户自定义类 4.3.1 类数组的声明 需要两次new Employee[]=staff=new Employedd[3]; staff[0]=new Employedd(参数列表); sta ...

  3. [core Java学习笔记][第一二三章基本语法]

    基本语法 1 Java 简单的类型 1.1 一些常量 正无穷大 Double.POSITVE_INFINITY 负无穷大 Double.NEGATIVE_INFINITY 不存在 Double.NaN ...

  4. [core java学习笔记][第五章继承]

    5.1 类.超类和子类 定义 class Manager extends Employee { 添加方法和域 } 权限 派生类不能使用基类的私有变量 派生类调用基类的方法 利用super这个关键词 s ...

  5. 20145330《Java学习笔记》第一章课后练习8知识总结以及IDEA初次尝试

    20145330<Java学习笔记>第一章课后练习8知识总结以及IDEA初次尝试 题目: 如果C:\workspace\Hello\src中有Main.java如下: package cc ...

  6. 【马克-to-win】学习笔记—— 第五章 异常Exception

    第五章 异常Exception [学习笔记] [参考:JDK中文(类 Exception)] java.lang.Object java.lang.Throwable java.lang.Except ...

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

    今天起开始学习Java,学习用书为Core Java.之前有过C的经验.准备把自己学习这一本书时的各种想法,不易理解的,重要的都记录下来.希望以后回顾起来能温故知新吧.也希望自己能够坚持把自己学习这本 ...

  8. o'Reill的SVG精髓(第二版)学习笔记——第十一章

    第十一章:滤镜 11.1滤镜的工作原理 当SVG阅读器程序处理一个图形对象时,它会将对象呈现在位图输出设备上:在某一时刻,阅读器程序会把对象的描述信息转换为一组对应的像素,然后呈现在输出设备上.例如我 ...

  9. [core java学习笔记][第十章部署应用程序]

    第10章 部署应用程序和applet jar文件 Java Web Start 10.1 jar文件 jar文件就是一个压缩了类,图像和声音的ZIP压缩文件 创建一个新的JAR文件应该使用的常见命令格 ...

随机推荐

  1. 配置linux中文

    1.~/.bash_profile文件添加一下内容并执行source  ~/.bash_profile export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK 2./etc ...

  2. JavaScript获取元素样式

    原生的JavaScript获取写在标签内部的样式很简单: <div class="test" id="test" style="width:10 ...

  3. 前端--关于HTML

    在讲HTML之前不得不先简单粗略提一下浏览器以及浏览器与HTML的关系.众所周知,浏览器就是一个应用程序,这个应用程序可以完成网络调用.展示接收的html文档等.严格来讲HTML文档就是按照某个规则写 ...

  4. Jmail组件发送邮件说明ASP.NET

    ASP.Net环境下使用Jmail组件发送邮件2008-01-25 18:59实现过程: 不同于在Asp中使用Jmail,直接使用 Server.CreateObject("Jmail.Me ...

  5. Ubuntu 12.04.5 LTS 上安装hadoop 2.6.0后运行自带的例程wordcount

    注:我所有的操作均通过Xshell 5远程连接Ubuntu进行实施 第一步:启动hadoop,利用jps查看hadoop是否已经启动,如果没有启动用start-dfs.sh脚本启动(hadoop2.X ...

  6. Solr-4.10.2与Tomcat整合

    1.将下载的solr解压至D:\solr,拷贝d:\solr\solr-4.10.2\example\webapps\solr.war到Tomcat的webapps\目录中.直接解压 solr.war ...

  7. weblogic开机启动-超简单

    1.编写weblogic启动脚本,命名为start_weblogic_server.sh,内容如下:  #!/bin/bashnohup /home/weblogic/Oracle/Middlewar ...

  8. asp.net mvc4 远程验证

    [HttpGet] public ActionResult CheckToolsIdExists(string ToolsID) { using (BaseContext context = new ...

  9. 配置Kestrel 网址Urls

    配置Kestrel 网址Urls ASP.NET Core中如何配置Kestrel Urls呢,大家可能都知道使用UseUrls() 方法来配置. 今天给介绍全面的ASP.NET Core 配置 Ur ...

  10. Java 网络编程---分布式文件协同编辑器设计与实现

    目录: 第一部分:Java网络编程知识 (一)简单的Http请求 一般浏览网页时,使用的时Ip地址,而IP(Internet Protocol,互联网协议)目前主要是IPv4和IPv6. IP地址是一 ...