异常、断言和日志

现实世界充满了不良数据和代码,由此会造成程序的错误和异常。假设在一个 Java 程序运行期间出现了一个错误, 用户期望在出现错误时 ,程序能够采用一些理智的行为。如果由于出现错误而使得某些操作没有完成,程序应该:

  • 返回到一种安全状态,并能够让用户执行一些其他命令;
  • 允许用户妥善的保存操作结果,并以妥善的方式结束程序;
  • 向用户通告错误。

1. 处理错误

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

1.1 程序中可能出现的错误和问题

1. 用户输入错误

除了那些不可避免的键盘输人错误外,有些用户喜欢各行其是,
不遵守程序的要求。在程序代码中应该对此进行检查,如果没有检査,网络层就会给出警告.

2. 设备错误

硬件并不总是让它做什么,它就做什么。在一个任务的处理过程中,硬件经常出现问题。

3. 物理限制

磁盘满了,可用存储空间已被用完。

4. 代码错误

程序方法有可能无法正确执行。对于方法中的一个错误,传统的做法是返回一个特殊的错误码,由调用方法分析。遗憾的是,并不是在任何情况下都能够返回一个错误码。
在 Java 中,如果某个方法不能够采用正常的途径完整它的
任务,就可以通过另外一个路径退出方法。在这种情况下,方法并不返回任何值,而是抛出(throw) 一个封装了错误信息的对象。调用这个方法的代码也将无法继续执行,取而代之的是,异常处理机制开始搜索能够处理这种异常状况的异常处理器( exception handler)。

1.2 异常的分类

在Java程序设计语言中,异常对象都是派生于Throwable 类的一个实例。
Java异常层次结构

Java语言将派生于Error类和RuntimeException类的所有异常称为非受查异常(unchecked),所有其他的异常称为受查异常(checked)。
是否受查,指的是编译器是否检查,编译器不能检查的异常称为非受查异常,主要依赖我们自己检查;编译期间就可以检查的异常称为受查异常,必须提供异常处理器,如果没有提供,可会编译报错。

1. error

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

2. Exception

在设计Java程序时,需要关注Exception层次结构。这个层次结构又分解为两个分支:
一个分支派生于 RuntimeException;另一个分支包含其他异常。划分两个分支的规则是:由程序错误导致的异常属于 RuntimeException;而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常。
如果出现 RuntimeException 异常,那么就一定是你的问题
编译器将检查所有的受查异常是否已经提供了异常处理器。

1.3 声明受查异常

在自己编写方法时,不必将所有可能抛出的异常都进行声明。遇到下面 4 种情况时应该抛出异常:

  • 调用一个抛出受查异常的方法,例如FileInputStream构造器;
  • 程序运行过程中发现错误,并且利用 throw 语句抛出一个受查异常;
  • 程序出现错误;
  • java 虚拟机和运行时库出现的内部错误。

如果一个方法有可能抛出多个受查异常类型,那么就必须在方法的首部列出所有的异常类。
一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。如果方法没有声明所有可能发生的受查异常,编译器就会发出一个错误消息。
如果类中的一个方法声明将会抛出一个异常,而这个异常是某个特定类的实例时,则这个方法就有可能抛出一个这个类的异常,或者这个类的任意一个子类的异常。

1.4 创建异常类

在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。在这种情况下,创建自己的异常类就是一件顺理成章的事情了。我们需要做的只是定义一个派生于Exception的类,或者派生于Exception子类的类 。
习惯上,定义的类应该包含两个构造器, 一个是默认的构造器;另一个是带有详细描述信息的构造器(超类Throwable的toString方法将会打印出这些详细信息,这在调试中非常有用)。

package exception;

import java.io.IOException;

/**
 * @author feng
 * @create 2021-04-06 21:20
 */
public class FileFormatException extends IOException {

    private String grip;

    public FileFormatException() {
    }

    public FileFormatException(String grip) {
        this.grip = grip;
    }
}

2. 捕获异常

通常,应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常继续进行传递。
如果编写一个覆盖超类的方法,而这个方法又没有抛出异常,那么这个方法就必须捕获方法代码中出现的每一个受查异常。不允许在子类的throws说明符中出现超过超类方法所列出的异常类范围。

2.1 捕获异常

如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈的内容。
了解每个方法可能会抛出哪种异常,然后再决定是自己处理,还是添加到throws列表中。对于后一种情况,也不必犹豫。将异常直接交给能够胜任的处理器进行处理要比压制对它的处理更好。

2.2 可以捕获多个异常

在一个 try 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。
在 Java SE 7 中,同一个catch子句中可以捕获多个异常类型。只有当捕获的异常类型彼此之间不存在子类关系时才需要这个特性。捕获多个异常时,异常变量隐含为final变量。

2.3 再次抛出异常或者异常链

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

3. 使用异常机制的技巧

3.1 异常处理不能当作是简单的测试

与执行简单的测试相比,捕获异常所花费的时间大大超过了代码的执行时间,因此使用异常的基本规则是:只在异常情况下使用异常机制。

3.2 不要过分的细化异常

建议将整个任务包装到try块中, 这样,当任何一个操作出现问题时,整个任务都可以取消。

3.3 利用异常层次结构

不要只抛出 RuntimeException 异常。应该寻找更加适当的子类或创建自己的异常类 。
将一种异常转换成另一种更加适合的异常时不要犹豫。

3.4 不要压制异常

在 Java 中,往往强烈地倾向关闭异常。

3.5 在检测错误时,“苛刻”要比放任更好

在出错的地方抛出一个异常,要比在后面才抛出异常要好。

3.6 不要羞于传递异常

传递异常要比捕获异常更好。

4. 使用断言

断言是JDK1.4中引入的一个新的关键字,是一种错误处理机制,是在程序的开发和测试阶段使用的工具。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;(程序员认为这个状态是true。)如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告并且退出。

4.1 断言的概念

断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插人的检测语句将会被自动地移走。
一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。通常,在整个开发阶段都会启用断言。一旦完全测试了系统并将它移送到产品环境时,则希望禁用断言,因为这样做会略微改善性能。使用assert,只需一行代码,并且不必从发布的代码中删除assert语句。不管怎样,为了编制文档的目的,断言也应保留在代码中。这样,当以后更改代码时,会提醒程序员要保持所有假设都是有效的,并且这也是可测试的。
Java 语言引人了关键字assert。这个关键字有两种形式 :

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

4.2 启用和禁用断言

在运行时,如果关闭了assertion功能,这些语句将不起任何作用。如果打开了assertion功能,那么expression1的值将被计算,如果它的值为false,该语句强抛出一个AssertionError对象。如果assertion语句包括expression2参数,程序将计算出expression2的结果,然后将这个结果作为AssertionError的构造函数的参数,来创建AssertionError对象,并抛出该对象,expression2可以在发生断言失败时获得更多的细节信息。如果expression1值为true,expression2将不被计算。AssertionError继承于Error对象,而Error继承于Throwable,Error是和Exception并列的一个错误对象,通常用于表达系统级运行错误,也就是说是一个不可控制异常(unchecked Exception)。AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。

  • 参数 -ea和-da:
    含义为开启(关闭)用户类的assertion功能:通过这个参数,用户可以打开某些类或包的assertion功能,同样用户也可以关闭某些类和包的assertion功能。打开assertion功能参数为-ea;如果不带任何参数,表示打开所有用户类;如果带有包名称或者类名称,表示打开这些类或包;如果包名称后面跟有三个点,代表这个包及其子包;如果只有三个点,代表无名包。关闭assertion功能参数为-da,使用方法与-ea类似。-ea和-da的全名为-enableassertions和-disenableassertions,全名和缩写名有同样的功能。
  • 参数 -esa 和 -dsa:
    它们含义为开启(关闭)系统类的assertion功能。由于新版本的Java的系统类中,也使了assertion语句,因此如果用户需要观察它们的运行情况,就需要打开系统类的assertion功能,我们可使用-esa参数打开,使用 -dsa参数关闭。-esa和-dsa的全名为-enablesystemassertions和-disenablesystemassertions,全名和缩写名有同样的功能。

5. 记录日志

记录日志API的优点:

  • 可以很容易地取消全部日志记录 , 或者仅仅取消某个级别的日志 , 而且打开和关闭这个操作也很容易 。
  • 可以很简单地禁止日志记录的输出 , 因此, 将这些日志代码留在程序中的开销很小 。
  • 日志记录可以被定向到不同的处理器 , 用于在控制台中显示 , 用于存储在文件中等 。
  • 日志记录器和处理器都可以对记录进行过滤 。 过滤器可以根据过滤实现器制定的标准丢弃那些无用的记录项 。
  • 日志记录可以采用不同的方式格式化 , 例如 , 纯文本或XML 。
  • 应用程序可以使用多个日志记录器 ,它们使用类似包名的这种具有层次结构的名字,例如 , com . mycompany . myapp
  • 在默认情况下 , 日志系统的配置由配置文件控制 。 如果需要的话 ,应用程序可以替换这个配置 。

5.1 日志的概念

5.1.1 日志文件

日志文件是用于记录系统操作事件的文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
在计算机中,日志文件是记录在操作系统或其他软件运行中发生的事件或在通信软件的不同用户之间的消息的文件。记录是保持日志的行为。在最简单的情况下,消息被写入单个日志文件 。

  1. 调试日志
    软件开发中,我们经常需要去调试程序,做一些信息,状态的输出便于我们查询程序的运行状况。为了让我们能够更加灵活和方便的控制这些调试的信息,所有我们需要专业的日志技术。java中寻找bug会需要重现。调试也就是debug 可以在程序运行中暂停程序运行,可以查看程序在运行中的情况。日志主要是为了更方便的去重现问题。
  2. 系统日志
    系统日志是记录系统中硬件、软件和系统问题的信息,同时还可以监视系统中发生的事件。用户可以通过它来检查错误发生的原因,或者寻找受到攻击时攻击者留下的痕迹。系统日志包括系统日志、应用程序日志和安全日志。
    系统日志策略可以在故障刚刚发生时就向你发送警告信息,系统日志帮助你在最短的时间内发现问题。
    系统日志可以让你了解故障或者袭击发生之前的所有事件。良好的系统日志可以防止你从错误的角度分析问题,避免浪费宝贵的排错时间。另外一种原因是借助于系统日志,管理员很有可能会发现一些之前从未意识到的问题,在几乎所有刚刚部署系统日志的环境当中。

    系统日志可以让你了解故障或者袭击发生之前的所有事件。良好的系统日志可以防止你从错误的角度分析问题,避免浪费宝贵的排错时间。另外一种原因是借助于系统日志,管理员很有可能会发现一些之前从未意识到的问题,在几乎所有刚刚部署系统日志的环境当中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值