1. 处理错误

1.2 异常分类

异常对象都是派生于 Throwable 类的一个实例。用户可以创建自己的异常类。

Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误。 应用程序不应该抛出这种类型的对象。 如果出现了这样的内部错误, 除了通告给用户,并尽力使程序安全地终止之外, 再也无能为力了。这种情况很少出现。

主要关注 Exception 层次结构。 这个层次结构又分解为两个分支:一个分支派生于 RuntimeException ; 另一个分支包含其他异常。划分两个分支的规则是:

  1. 由程序错误导致的异常属于 RuntimeException ; [•错误的类型转换 •数组访问越界 •访问 null 指针]
  2. 而程序本身没有问题, 但由于像 I/O 错误这类问题导致的异常属于其他异常。[•试图在文件尾部后面读取数据。
    •试图打开一个不存在的文件。•试图根据给定的字符串查找 Class 对象, 而这个字符串表示的类并不存在
    ]

Error 类 或 RuntimeException 类的所有异常称为非受查( unchecked ) 异常,所有其他的异常称为受查( checked) 异常

编译器将核查是否为所有的受査异常提供了异常处理器

RuntimeException 这个名字很容易让人混淆。 实际上,现在讨论的所有错误都发生在运行时。

C++ 有两个基本的异常类, 一个是 runtime_error ,另一个是 logic_error。logic_error 类相当于Java 中的 RuntimeException, 它表示程序中的逻辑错误;runtime_error 类是所有由于不可预测的原因所引发的异常的基类。它相当于 Java 中的非 RuntimeException 异常。

1.3 声明受查异常

方法应该在其首部声明所有可能抛出的异常。这样可以从首部反映出这个方法可能抛出哪类受査异常。

不需要声明 Java 的内部错误,即从 Error 继承的错误。任何程序代码都具有抛出那些异常的潜能, 而我们对其没有任何控制能力。
同样,也不应该声明从 RuntimeException 继承的那些非受查异常。

总之,一个方法必须声明所有可能抛出的受查异常, 而非受查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。

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

在 C++ 中,throw 说明符在运行时执行, 而不是在编译时执行。也就是说, C++编译器将不处理任何异常规范。
在 C++ 中, 如果没有给出 throw 说明, 函数可能会抛出任何异常。而在 Java中, 没有 throws 说明符的方法将不能抛出任何受查异常

1.4 如何抛出异常



对于一个已经存在的异常类, 将其抛出非常容易:
1 ) 找到一个合适的异常类。
2 ) 创建这个类的一个对象。
3 ) 将对象抛出。
一旦方法抛出了异常, 这个方法就不可能返回到调用者。也就是说, 不必为返回的默认值或错误代码担忧。

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

1.5 创建异常类

自定义异常类时,习惯上, 定义的类应该包含两个构造器, 一个是默认的构造器;另一个是带有详细描述信息的构造器(超类 Throwable 的 toString 方法将会打印出这些详细信息, 这在调试中非常有用)。

使用和标准异常类一样:

2. 捕获异常

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

通常, 应该捕获那些知道如何处理的异常, 而将那些不知道怎样处理
的异常继续进行传递。如果想传递一个异常, 就必须在方法的首部添加一个throws 说明符, 以便告知调用者这个方法可能会抛出异常。比如:

2.1 捕获多个异常


可用e.getMessage()获取异常对象e的更多信息。或者使用
e.getClass().getName()得到异常对象的实际类型

异常变量隐含为final

可对多个异常对象进行相同处理:

2.2 再次抛出异常和异常链

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

或者不改变异常类型,只是记录下异常,再次重新抛出该异常:

2.3 finally子句

不管是否有异常被捕获, finally 子句中的代码都被执行。然后才是后续的try-catch以外的代码。在下面的示例中, 程序将在所有情况下关闭文件。


try 语句可以只有finally 子句,而没有catch 子句。

假设利用return语句从try 语句块中退出。在方法返回前, finally 子句的内容将被执行。如果finally 子句中也有一个return 语句, 这个返回值将会覆盖原始的返回值

如果在finally语句块也抛出了异常,原始的异常将会丢失,转而抛出新的方法的异常。

2.4 带资源的try语句


try块退出时,会自动调用 res.close()。和finally效果一样。

可在try内指定多个资源:

不论这个块如何退出, in 和 out 都会关闭。

如果 try块抛出一个异常, 而且 close 方法也抛出一个异常,如前面所说,会导致新异常覆盖旧异常。带资源的 try 语句可以很好地处理这种情况。原来的异常会重新抛出,而 close方法抛出的异常会被抑制。 这些异常将自动捕获,并由 addSuppressed 方法增加到原来的异常。 如果对这些异常感兴趣, 可以调用 getSuppressed 方法,它会得到从 close 方法抛出并被抑制的异常列表

带资源的 try 语句自身也可以有 catch 子句和一个 finally 子句。 这些子句会在关闭资源之后执行。 不过在实际中, 一个 try 语句中加入这么多内容可能不是一个好主意。

2.5 分析堆栈轨迹元素

堆栈轨迹( stack trace ) 是一个方法调用过程的列表, 它包含了程序执行过程中方法调用的特定位置。

可以调用 Throwable 类的 printStackTrace 方法访问堆栈轨迹的文本描述信息

一种更灵活的方法是使用 getStackTrace 方法, 它会得到 StackTraceElement 对象的一个数组, 可以在你的程序中分析这个对象数组。例如:

StackTraceElement 类含有能够获得文件名和当前执行的代码行号的方法, 还含有能够获得类名和方法名的方法。

静态的 Thread.getAllStackTrace 方法, 它可以产生所有线程的堆栈轨迹 :

3. 断言

断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插人的检测语句将会被自动地移走

assert用法:
1.assert 条件;
2.assert 条件:表达式;
这两种形式都会对条件进行检测, 如果结果为 false, 则抛出一个 AssertionError 异常。在第二种形式中,表达式将被传人 AssertionError 的构造器, 并转换成一个消息字符串

C 语言中的 assert 宏将断言中的条件转换成一个字符串。 当断言失败时,这个字符串将会被打印出来。例如, 若 assert(x>=0) 失败, 那么将打印出失败条件"x>=0”。在 Java 中, 条件并不会自动地成为错误报告中的一部分。如果希望看到这个条件, 就必须将它以字符串的形式传递给 AssertionError 对象:assert x >= 0:“x >= 0”。

3.1 启用和禁用断言

在默认情况下, 断言被禁用。可以在运行程序时用 -enableassertions 或 -ea 选项启用:

java -enableassertions MyApp

需要注意的是, 在启用或禁用断言时不必重新编译程序。启用或禁用断言是类加载器( class loader) 的功能。当断言被禁用时, 类加载器将跳过断言代码, 因此,不会降低程序运行的速度。

也可以在某个类或整个包中使用断言, 例如:

java -ea:MyClass -ea:com.mycompany.mylib ...  MyApp

这条命令将开启 MyClass 类以及在 com.mycompany.mylib 包和它的子包中的所有类的断言。选项 -ea 将开启默认包中的所有类的断言。

也可以用选项 -disableassertions 或 -da 禁用某个特定类和包的断言:

java -ea:... -da:MyClass MyApp

上面提到的启用和禁用所有断言的 -ea 和 -da 开关不能应用到那些没有类加载器的“ 系统类”上。对于这些系统类来说, 需要使用 -enablesystemassertions/-esa 开关启用断言。

3.2 使用断言规则

在 Java 语言中, 给出了 3 种处理系统错误的机制:
•抛出一个异常
•日志
•使用断言

断言失败是致命的、 不可恢复的错误。断言检查只用于开发和测阶段。不应该使用断言向程序的其他部分通告发生了可恢复性的错误,或者,不应该作为程序向用户通告问题的手段。断言只应该用于在测试阶段确定程序内部的错误位置。

4. 记录日志

相比使用printf观察程序运行,使用日志要方便的多:
1.可以很容易地取消全部日志记录,或者仅仅取消某个级别的日志,而且打开和关闭这个操作也很容易。
2.日志记录可以被定向到不同的处理器(控制台、文件等等),并可以对记录进行过滤。
3.日志记录可以采用不同的方式格式化(txt,xml)。

4.1 基本日志

使用全局日志记录器(global logger) 并调用其 info 方法:

Logger.getClobal().info("File->Open menu item selected");

调用

Logger.getClobal ().setLevel (Level.OFF);

将会取消所有的日志。

4.2 高级日志

不要将所有的日志都记录到一个全局日志记录器中,而是可以自定义日志记录器。可以调用 getLogger 方法创建或获取记录器:

private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp"):

未被任何变量引用的日志记录器可能会被垃圾回收。 为了防止这种情况发生,要像上面的例子中一样, 用一个静态变量存储日志记录器的一个引用。

与包名类似,日志记录器名也具有层次结构。事实上, 与包名相比,日志记录器的层次性更强。 对于包来说,一个包的名字与其父包的名字之间没有语义关系,但是日志记录器的父与子之间将共享某些属性。例如, 如果对 com.mycompany 日志记录器设置了日志级别,它的子记录器也会继承这个级别。下面是七个日志级别:
• 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);

默认的日志记录将显示包含日志调用的类名和方法名, 如同堆栈所显示的那样。但是,如果虚拟机对执行过程进行了优化,就得不到准确的调用信息。此时,可以调用 logp 方法获得调用类和方法的确切位置, 这个方法的签名为:

void logp(Level 1, 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)
这些调用将生成 FINER 级别和以字符串 ENTRY 和 RETURN 开始的日志记录。

可用如下方法将异常写入日志中:

void throwing(String className , String methodName , Throwable t)
void log(Level 1 , String message , Throwable t)

4.3 修改日志管理器配置

可以通过编辑配置文件来修改日志系统的各种属性。在默认情况下, 配置文件存在于:

jre/lib/1ogging.properties

要想使用另一个配置文件, 就要将java.util.logging.config.file 特性设置为配置文件的存储位置, 并用下列命令启动应用程序:

java -Djava.util.logging.config. file-configFileMainClass

日志管理器在VM 启动过程中初始化, 这在main 执行之前完成.

要想修改默认的日志记录级别, 就需要编辑配置文件,并修改以下命令行

.level=INFO

可以通过添加以下内容来指定自己的日志记录级别

com.mycompany_myapp.level=FINE

也就是说,在日志记录器名后面添加后缀.level。

日志记录并不将消息发送到控制台上,这是处理器的任务。另外,处理器也有级别。要想在控制台上看到FINE 级别的消息, 就需要进行下列设置

java.util.logging.ConsoleHandler.level=FINE

在曰志管理器配置的属性设置不是系统属性,只影响当前程序的日志记录,对日志记录器没有任何修改。

4.4 本地化

本地化的应用程序包含资源包( resource bundle ) 中的本地特定信息。资源包由各个地区( 如美国或德国)的映射集合组成。例如, 某个资源包可能将字符串“ readingFile” 映射成英文的“ Reading file” 或者德文的“ Achtung! Datei wird eingelesen”

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

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

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

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

logger.info("readingFile");

通常需要在本地化的消息中增加一些参数,因此,消息应该包括占位符。例
如, 要想在日志消息中包含文件名,就应该用下列方式包括占位符:

Reading file {0}.
Achtung! Datei {0} wird eingelesen.

然后,通过调用下面的一个方法向占位符传递具体的值:

logger.log(Level .INFO, "readingFile", fileName) ;
logger,log(Level .INFO, "readingFile", new Object[] { oldName, newName }) ;

4.5 处理器

在默认情况下,日志记录器将记录发送到ConsoleHandler 中, 并由它输出到System.err流中。具体来说,日志记录器将记录发送到父处理器中,而最终的处理器有一个ConsoleHandler。

与日志记录器一样, 处理器也有日志记录级别。对于一个要被记录的日志记录,它的日志记录级别必须高于日志记录器和处理器的阈值
日志管理器配置文件设置的默认控制台处理器的日志记录级别为

java.util.logging.ConsoleHandler.level =INFO

要想记录FINE 级别的日志,就必须修改配置文件中的默认日志记录级别和处理器级别
另外,还可以绕过配置文件,安装自己的处理器。

Logger logger = Logger.getLogger("com.mycompany.myapp") ;
logger.setLevel (Level .FINE) ;
logger.setUseParentHandlers(false) ;
Handler handler = new ConsoleHandler() ;
handler,setLevel (Level .FINE) ;
logger.addHandler(hander):

在默认情况下, 日志记录器将记录发送到自己的处理器和父处理器。我们的日志记录器是原始日志记录器(命名为“ ”)的子类而原始日志记录器将会把所有等于或高于INFO级別的记录发送到控制台。然而, 我们并不想两次看到这些记录。鉴于这个原因, 应该将useParentHandlers属性设置为false。

要想将日志记录发送到其他地方, 就要添加其他的处理器。日志API为此提供了两个很有用的处理器, 一个是FileHandler ; 另一个是SocketHandler。SocketHandler 将记录发送到特定的主机和端口, 而更令人感兴趣的是FileHandler, 它可以收集文件中的记录。
可以像下面这样直接将记录发送到默认文件

FileHandler handler = new FileHandler() ;
logger.addHandler(handler) ;

这些记录被发送到用户主目录的javan.log 文件中, n 是文件名的唯一编号。

可以通过扩展Handler 类或StreamHandler 类自定义处理器。

4.6 过滤器

在默认情况下, 过滤器根据日志记录的级别进行过滤
每个日志记录器和处理器都可以有一个可选的过滤器来完成附加的过滤
另外, 可以通过实现Filter 接口并定义下列方法来自定义过滤器。

boolean isLoggable(LogRecord record)

要想将一个过滤器安装到一个日志记录器或处理器中, 只需要调用setFilter 方法就可以了。注意**,同一时刻最多只能有一个过滤器**。

4.6.1 格式化器

ConsoleHandler 类和FileHandler 类可以生成文本和XML 格式的日志记录。但是, 也可以自定义格式。这需要扩展Formatter 类并覆盖下面这个方法:

String format(LogRecord record)

调用setFormatter 方法将格式化器安装到处理器中

4.6.2 日志记录说明

最常用的操作:
1)为一个简单的应用程序, 选择一个日志记录器,并把日志记录器命名为与主应用程序包一样的名字:

Logger logger = Logger.getLogger("com.mycompany.myprog");

用一个静态变量存储日志记录器的一个引用,防止被回收:

private static final Logger logger =Logger.getLogger("com.mycompany.myprog"):

2)默认的日志配置将级别等于或高于INFO 级别的所有消息记录到控制台。用户可以覆盖默认的配置文件。但是正如前面所述, 改变配置需要做相当多的工作。因此,最好在应用程序中安装一个更加适宜的默认配置。

3)现在,可以记录自己想要的内容了。但需要牢记: 所有级别为INFO、WARNING 和SEVERE 的消息都将显示到控制台上。因此, 最好只将对程序用户有意义的消息设置为这几个级别。将程序员想要的日志记录,设定为FINE 是一个很好的选择

JAVA//异常、断言和日志相关推荐

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

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

  2. Java核心技术笔记 异常、断言和日志

    <Java核心技术 卷Ⅰ> 第7章 异常.断言和日志 处理错误 捕获异常 使用异常机制的技巧 记录日志 处理错误 如果由于出现错误而是的某些操作没有完成,程序应该: 返回到一种安全状态,并 ...

  3. Java异常处理学习笔记(抛出、捕获、finally、异常传播、NPE、断言、日志)

    Java中的异常是什么? Java异常本质上一种class,继承关系如下图所示,Error是严重的错误,程序无能为力,RuntimeException是在运行过程中发生的异常,其余的异常在编写程序的时 ...

  4. Java基础(四)——异常、断言、日志

    文章目录 异常.断言.日志 1 异常 2 断言 3 日志 异常.断言.日志 在Java语言中,给出了3种处理系统错误的机制 抛出一个异常 使用断言 日志 1 异常 Error:程序无法处理的错误,无法 ...

  5. Core Java 读后感 - 第七章 异常、断言和日志

    第七章 异常.断言和日志 7.1 处理异常 如果由于出现错误使得某些操作没有完成, 程序应该: 返回到一种安全状态,并能够让用户执行其他命令 允许用户保存所有工作的结果,并以妥善的方式终止程序 异常处 ...

  6. java 异常 日志_java中的异常、断言、日志(一)

    1.Java异常处理i.异常的概念和Java里面的异常体系结构1)基本概念:程序中的异常,一般成为例外情况,可以理解为是非正常情况,其他编程语言里面也有这样的情况,Java里面同样存在这样一个体系结构 ...

  7. easyloging 获取日志文件名字_愉快地学Java语言:第十五章 断言与日志

    导读 本文适合Java入门,不太适合Java中高级软件工程师.本文以<Java核心技术基础知识卷I>第10版为蓝本,采用不断提出问题,然后解答问题的方式来讲述.本篇文章只是这个系列中的一篇 ...

  8. Java04异常、断言、日志和调试

    11 异常.断言.日志和调试 异常处理(exception handing) 使用断言来启动检测 Java日志框架 调试技巧 11.1 处理错误 如果一个方法不能够采用正常的途径完成任务,就通过另外一 ...

  9. 5 异常、断言和日志

    title: 异常.断言和日志 tag: 标签名 categories: 分类 comment: 是否允许评论(true or false) description: 描述 top_img: http ...

  10. java 异常 日志_java(异常和日志)

    java异常看这篇就够了http://www.cnblogs.com/lulipro/p/7504267.html 1.类路径 所谓的类路径就是指程序运行时jvm要加载的类的.class文件所在地方 ...

最新文章

  1. 2022-2028年中国美瞳行业应用市场需求及开拓机会研究报告
  2. linux 编译opencl,OpenCL编译环境配置(VS+Nvidia)
  3. GIT之旅【第一篇】
  4. 自定义Lisp透明命令
  5. MySQL server has gone away报错原因分析及解决办法
  6. VC++ 进程间通信方法总结
  7. 算法学习-莫比乌斯反演
  8. Java当中迭代器的使用(遍历容器ArrayList, HashSet,HashMap)
  9. leetcode142. 环形链表 II
  10. 实战演练丨SCN太大引发ORA-600[2252]
  11. Unable to add window -- token null is not for an application
  12. GoogleTest测试框架介绍(一)
  13. day25-python之继承组合
  14. python学习手册笔记
  15. 从Sklearn Bunch对象到Pandas DataFrame对象的转换
  16. react 或者 vue,如何做 SEO 优化?
  17. 【python游戏】新的一年快来变身兔兔战士打败獾守护兔兔吧~
  18. POJ 2248【加法链】
  19. win10笔记本合盖无法睡眠(风扇依然在转动)(开盖后不是锁屏状态)
  20. NIPS2019:旷视提出DetNAS:首个搜索物体检测Backbone的方法

热门文章

  1. 11 【实操篇-定时任务 软件安装 克隆虚拟机】
  2. 计算机视觉,机器学习需要关注的网站和会议
  3. 麒麟系统在线安装docker(x86/arm)
  4. 2020年中国家居建材行业信息化发展论坛
  5. Matlab遇到内部问题,需要关闭Disabled - No sandbox or build area path
  6. 基于simulink平台的非线性模型预测控制算法实现代码,无人驾驶运动控制
  7. skydrive服务器位置,skydrive文件是什么 Win10系统修改skydrive文件默认储存位置路径方法...
  8. Qt 文件和文件夹操作
  9. android studio build variants,Android studio构建App的不同variants和types
  10. 我流泪了。。。。。。。。。