11 异常、断言、日志和调试

异常处理(exception handing)

使用断言来启动检测

Java日志框架

调试技巧

11.1 处理错误

如果一个方法不能够采用正常的途径完成任务,就通过另外一个路径退出方法。

在这种情况下,方法不返回任何值,而是抛出一个封装了错误信息的对象。此外,调用这个方法的代码也将无法继续执行。

异常处理机制开始搜索能够处理这种异常情况的异常处理器(exception handler)。

异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。

异常对象是派生于Throwable类的一个实例。

可以创建自己的异常类。

异常的分类:

Throwable

Exception

RuntimeException

ArrayIndexOutOfBoundsException

NullPointerException

IOException

Error

Error类层次描述的是Java运行时系统的内部错误和资源耗尽。

应用程序不应抛出此类错误。

除了通告给用户并尽力使程序安全地中止之外,再也无能为力。

此种情况很少出现。

派生于Error类或RuntimeException类的所有异常称为未检查(unchecked)异常;

所有其他的异常称为已检查(checked)异常。

编译器将检查是否为所有已检查异常提供了异常处理器。

声明已检查异常:

public FileInputStream(String name) throws FileNotFoundException

一个方法要么返回其返回值,要么抛出一个异常。

一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控(Error),要么应避免(RuntimeException)。

如果子类中覆盖了超类的一个方法,子类方法中声明的已检查异常不能比超类方法中声明的异常更通用。

如何抛出异常:

1、找到一个合适的异常类;

2、创建其类对象;

3、将对象抛出。

如:throw new EOFException();

throw new EOFException(String);

一旦方法抛出异常,这个方法就不能返回到调用者。

C++中可以抛出任何类型的值,Java中只能抛出Throwable子类的对象。

创建异常类:

定义一个派生于Exception的类,或者派生于Exception子类的类。

包含两个构造器:默认构造器;带有详细描述信息的构造器。

class FileFormatException extends IOException
{public FileFormatException(){}public FileFormatException(String message){super(massage);}
}

11.2 捕获异常

如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈信息。

捕获异常:

try
{code that might throw exception
}
catch(FileNotFoundException | UnknownHostException e) //捕获多个异常时,e隐含为final变量。
{emergency action for missing files and unknown hosts
}
catch(IOException e)
{emergency action for all other I/O problems
}

如果try语句块中的任何代码抛出了一个在catch子句中说明的异常类,则跳过try语句块的其余代码,执行catch子句中的处理器代码;

如果try语句块中抛出的异常没有在catch子句中说明,则立即退出这个方法;

如果try语句块没有抛出异常,则跳过catch子句。

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

如果调用了一个抛出已检查异常的方法,要么处理,要么传递。

通常,捕获那些知道如何处理的异常,传递那些不知怎样处理的异常。

在catch子句中还可以抛出一个异常,这样做目的是改变异常的类型。

如:

try
{access the database
}
catch (SQLException e)
{Throwable se = new ServletException(“database error”);se.initCause(e);throw se;
}
finally
{close resource;
}Throwable e = se.getCause();

//可以得到原始异常,使用这种包装技术,可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。


如果在一个方法中发生了一个已检查异常,而方法不允许抛出它,那么包装技术可以将这个已检查异常包装成一个运行时异常。

 

强烈建议使用try/catch、try/finally子句

finally子句一定会被执行。

 

带资源的try语句

try(Resource res = ...)
{work with res;
}
//try块退出时,自动调用res.close()方法。
//此时,try块抛出的异常被抛出,close方法抛出的异常被抑制。

带资源的try语句自身也可以有catch子句和一个finally子句。这些子句会在关闭资源后执行。

在实际中,一个try语句中加入这么多内容可能不是一个好主意。

堆栈跟踪(stack trace):

是一个方法调用过程的列表,包含了程序执行过程中方法调用的特定位置。


Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement frame : frames)analyze frame;
//多线程堆栈跟踪
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Thread t : map.keySet())
{StachTraceElement[] frames = map.get(t);analyze frames
}

11.3 使用异常的技巧

1、异常处理不能代替简单的测试;

2、不要过分细化异常;

3、利用异常层次结构;

4、不要压制异常;对于不常发生的异常,catch语句块的内容可以先空着,待日后感觉需要处理时再填上。

5、不要羞于传递异常。

11.4 使用断言

断言机制允许在测试期间向代码中插入一些检查语句。

当代码发布时,这些插入的检测语句将会被自动移走。

assert 条件;

assert 条件:表达式;

这两种形式都会对条件进行检测,如果结果为false,则抛出一个AssertionError异常。

在第二种形式中,表达式将被传入AssertionError的构造器,并转换成一个消息字符串。

表达式的唯一目的就是产生一个消息字符串。

assert x >= 0;

assert x >=0 : x;

assert x >=0 “x >= 0”;

默认情况下,断言被禁用。

在运行时,用-enableassertions 或 -ea 选项启用它。

java -enableassertions MyApp

在启用或禁用断言时不必重新编译程序。启用或禁用断言时类加载器(class loader)的功能。

当断言被禁用时,类加载器将跳过断言代码,因此不会降低程序运行的速度。

也可选用-disableassertions或-da禁用某个特定类或包的断言。

在Java中,给出了三种处理系统错误的机制:

异常;

日志;

断言。

断言是致命的,不可恢复的错误。只用于开发和测试阶段。

断言是一种测试和调试阶段所使用的战术性工具;

日志记录是一种在程序的整个生命都可以使用的策略性工具。

11.5 记录日志

优点:

·可以取消全部日志记录,或仅取消某个级别的日志,而且打开和关闭这个操作也很容易;

·可以很简单的禁止日志记录的输出,因此,这些日志代码留在程序中的开销很小;

·日志记录可以在控制台中显示,也可以在文件中存储;

·可以对日志记录进行过滤,只保留重要日志;

·可以采用不同格式:纯文本或XML;

·可使用多个日志记录;

·默认情况下,日志系统的配置由配置文件控制;如果需要的话,应用程序可以替换这个配置。

基本日志:

日志系统管理着一个名为Logger.global的默认日志记录器,可通过info方法记录日志信息

Logger.getGlobal().info(“File->Open menu item selected”); //记录日志

Logger.getGlobal().setLevel(Level.OFF); //取消所有日志

高级日志:

在一个应用程序中,不要将所有的日志都记录到一个全局日志记录器中,而是自定义日志记录器。

调用getLogger方法可以创建或检索记录器:

private static final Logger myLogger = Logger.getLogger(“com.mycompany.myapp”);

日志记录具有层次结构,上下层之间将共享某些属性,例如对com.mycompany日志记录器设置了日志级别,它的子记录器也会继承这个级别。

SERVER  WARNING  INFO  CONFIG  FINE  FINER  FINEST

默认记录前三个级别。

级别设置:

logger.setLevel(Level.FINE); //Level.ALL开启所有级别 Level.OFF关闭所有级别

记录方法:

logger.waring(message);

logger.fine(message);

logger.log(level.FINE, message);

默认的日志配置记录了INFO或更高级别的所有记录。

应该使用CONFIG、FINE、FINEST级别来记录那些有助于诊断,但对于程序员又没有太大意义的调试信息。

默认的日志记录将显示包含日志调用的类名和方法名,如同堆栈所显示的那样。

但是,如果虚拟机对执行过程进行了优化,就得不到准确的调用信息。

此时可用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)

如:

int read(String file, String pattern)
{logger.entering(“com.mycompany.mylib.Reader”,”read”,new Object[]{file, pattern};...logger.exiting(“com.mycompany.mylib.Reader”,”read”,count);return count;
}

这些调用将产生FINER级别和以字符串ENTRY和RETURN开始的日志记录。

日志记录的常见用途是记录那些不可预料的异常。

两种方法:

void throwing(String className, String methodName, Throwable t)

void log(Level l, String message, Throwable t)

典型用法:

if(...)
{IOException exception = new IOException(“...”);logger.throwing(“com.mucompany.mylib.Reader”,”read”,exception);throw exception;
}
//调用throwing可以记录一条FINER级别的记录和一条以THROW开始的信息。try
{...
}
catch(IOException)
{Logger.getLogger(“com.mycompany.myapp”).log(Level.WARNING, “Reading image”, e);
}

修改日志管理器配置文件jre/lib/logging.properties

日志管理器在VM启动过程中被初始化,在main之前执行。

.level=INFO //修改默认的日志记录级别

com.mycompany.myapp.level=FINE 指定自己的日志记录级别

本地化:

本地化的应用程序包含资源包(resource bundle)中的本地特定信息。

资源包由各个地区的映射集合组成。如某个资源包可以将redingFile映射成英文的Reading file或者德文的Achtung! Datei wired eingelesen。

一个程序可以包含多个资源包,一个用于菜单;其他用于日志消息。

每个资源包都有一个名字,如com.mycompany.logmessages。

要想将映射添加到一个资源包中,需要为每个地区创建一个文件。

英文消息映射位于com/mycompany/logmessages_en.properties文件中;

德文消息映射位于com/mycompany/logmessages_de.properties文件中。

可以将这些文件与应用程序的类文件放在一起,以便ResourceBundle类自动对它们进行定位。

这些文件都是纯文本文件,内容如下:

readigFile=Achtung! Datei wird eingelesen

renamingFile=Datei wird umbenannt

在请求日志记录器时,可以指定一个资源包:

Logger logger= Logger.getLogger(loggerName, “com.mycompany.logmessages”);

然后,为日志消息指定资源包的关键字,而不是实际的日志消息字符串:

logger.info(“readingFile”);

通常需要在本地化的消息中增加一些参数,因此,消息应该包括占位符{0}、{1}等。

例如,在日志消息中包含文件名,需要使用下列方式包含占位符:

readingFile=Reading file {0}

renamingFile=Change name file {0} to {1}

logger.log(Level.INFO, “readingFile”, fileName);

logger.log(Level.INFO, “renamingFile”, new Object[]{oldName, newName};

处理器:

默认情况下,日志记录器将记录发送到ConsoleHandler中,并由它输出到System.err流中。

日志记录器还会将记录发送到父处理中,最终处理器(命名为””)有一个ConsoleHandler。

处理器也有一个日志记录级别。

安装自己的处理器:

Logger logger = Logger.getLogger(“com.mycompany.myapp”);

logger.setLevel(Level.FINE);

logger.setUseParentHandlers(false);

Handler handler = new ConsoleHandler();

handler.setLevel(Level.FINE);

logger.addHandler(handler);

其他处理器:

FileHandler 将记录发送到用户主目录的javan.log文件中,n是文件的唯一编号,默认XML格式。

SocketHandler 将记录发到特定的主机和端口。

过滤器:

默认情况下,过滤器根据日志记录的级别进行过滤。

每个日志记录器和处理器都可以有一个可选的过滤器来完成附加的过滤。

另外,可通过实现Filter接口的boolean isLoggable(LogRecord record)方法来自定义过滤器。

调用setFilter方法安装过滤器。

一个时刻最多只能有一个过滤器。

日志记录说明:日志记录器最好与主应用程序包名相同;

Java04异常、断言、日志和调试相关推荐

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

    2019独角兽企业重金招聘Python工程师标准>>> 第十一章 异常,日志,断言,调试 由于程序的错误或一些外部环境的影响造成用户数据的丢失,用户就有可能不再使用这个程序了.为了避 ...

  2. CoreJava 笔记总结-第七章 异常,断言和日志

    文章目录 第七章 异常,断言和日志 处理错误 异常的分类 声明检查型异常 如何抛出异常 创建异常类 捕获异常 捕获单个异常 捕获多个异常 再次抛出异常和异常链 `finally`子句 `try-wit ...

  3. .NET WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作)

    .NET WebAPI 用ExceptionFilterAttribute实现错误(异常)日志的记录(log4net做写库操作) 参考文章: (1).NET WebAPI 用ExceptionFilt ...

  4. java异常及日志注意事项

    java异常及日志注意事项 参考文章: (1)java异常及日志注意事项 (2)https://www.cnblogs.com/lq147760524/p/8475531.html 备忘一下.

  5. windows server 2008 如何查看异常重启日志

    下面蓝队网络为大家介绍下windows server 2008 如何查看异常重启日志 开始->管理工具->时间查看器 windows日志->系统 筛选当前日志 选择Kernel-Po ...

  6. exception日志 php_PHP中错误与异常的日志记录用法分析

    本文分析了PHP中错误与异常的日志记录用法.分享给大家供大家参考,具体如下: 提到 Nginx + PHP 服务的错误日志,我们通常能想到的有 Nginx 的 access 日志.error 日志以及 ...

  7. HTML页面加载异常,按F12调试后居然又好了的解决办法!

    HTML页面加载异常,按F12调试后居然又好了的解决办法! 参考文章: (1)HTML页面加载异常,按F12调试后居然又好了的解决办法! (2)https://www.cnblogs.com/Jaco ...

  8. Netbackup5230备份一体机重删率异常故障分析日志收集

    Netbackup5230备份一体机重删率异常故障分析日志收集 参考文章: (1)Netbackup5230备份一体机重删率异常故障分析日志收集 (2)https://www.cnblogs.com/ ...

  9. Android 异常崩溃日志,捕捉并保存到本地

    Android 异常崩溃日志,捕捉并保存到本地: 前几天因为在省公安厅做一个通讯类之类的应用:碰到个问题,就是download人员信息将信息保存到本地数据库完成的时候,菊花转还没有dismission ...

最新文章

  1. 丰度决定了细菌在复杂群落中的功能作用
  2. Hadoop配置环境变量
  3. 在一个3*4数组中查找最大最小值
  4. 案例:图书管理——补充知识(数组相关API)||补充知识(数组响应式变化)
  5. 008 Android之Service
  6. JS----javascript原型和原型链
  7. THINKPHP3.2视频教程
  8. 3d激光雷达开发(法向量预测)
  9. Linux链表list_head/hlist_head/hlist_nulls_head的并发性
  10. 层次分析法java_AHP层次分析法java实现
  11. Elasticsearch 父子关系
  12. jquery 基础教程[温故而知新二]
  13. 配置jdbc遇到的问题
  14. Nginx配置文件目录实现pdf文件预览及下载
  15. 《Hands-On Machine Learning with Scikit-Learn TensorFlow》读书笔记(三):分类
  16. 对比阿里云服务器和腾讯云服务器的不同之处
  17. 9 输入输出设备题库
  18. 谁能够最终实现超越普通计算机的“量子霸权”
  19. 18 岁清华毕业,95后博士生杨幻睿将深度学习 “单核” 变“多核”,显著提高 “对抗性攻击” 防御力-1
  20. 特来电最新财务数据曝光:年亏7770万,与星星充电争抢上市

热门文章

  1. Java IdentityHashMap isEmpty()方法与示例
  2. python的pass语句_适用于pass语句的Python程序
  3. 十八、对已经找到轮廓的图像进行测量
  4. php字符串连接符、三元运算符
  5. 分布式是写出来的(二)
  6. 函数无法识别_PostgreSQL找不到最佳函数问题解析
  7. 软件工程---18.面向服务的软件工程
  8. c++实现顺序表的相关操作
  9. 传输层:IP 地址解析 路由转发
  10. 顺序表实现栈相关操作