7.1处理错误假若因为出现错误而致使个别操作没有完成, 程序应当:返回到一种安全状态,并才能让用户执行一些其他的命令;或者容许用户保存所有操作的结果,并以妥善的形式中止程序异常处理的任务就是将控制权从错误形成的地方转移给才能处理这些情况的错误处理器异常出现情况:用户输入错误设备错误化学限制代码错误某个方式不能够采用正常的途径完整它的 任务,就可以通过另外一个路径退出方式。在这些情况下,方法并不返回任何值, 而是抛出 ( throw) 一个封装了错误信息的对象。需要注意的是,这个方式将会立即退出,并不返回任 何值。此外,调用这个方式的代码也将难以继续执行,取而代之的是,异常处理机制开始搜 索才能处理这些异常状况的异常处理器异常具有自己的句型和特定的承继结构
异常对象都是派生于 Throwable 类的一个实例,如果 Java 中外置的异常类不能否满足需求,用户可以创建自己的异常类异常层次结构简化示意图 所有的异常都是由 Throwable 继承而至,但在下一层立刻分解为两个分 支:Error 和 ExceptionError 类层次结构描述了 Java 运行时系统的内部错误和资源用尽错误在设计 Java 程序时, 需要关注 Exception 层次结构。 这个层次结构又分解为两个分支: 一个分支派生于 RuntimeException ; 另一个分支包含其他异常。划分两个分支的规则是由程序错误引起的异常属于 RuntimeException ; 而程序本身没有问题, 由于像 I/O 错误这类 问题引起的异常属于其他异常:派生于 RuntimeException 的异常包含下边几种情况错误的类型转换字段访问越界访问 null 指针不是派生于 RuntimeException 的异常包括:试图在文件尾部前面读取数据企图打开一个不存在的文件企图按照给定的字符串查找 Class 对象,而这个字符串表示的类并不存在应当通过测量链表下标是否越界来防止 ArraylndexOutOfBoundsException 异常;应该通过在 使用变量之前检查是否为 null 来防止 NullPointerException 异常的发生Java语言规范将派生于Error 类 或 RuntimeException 类的所有异常称为非检查型异常,所有其他的异常称为检查型异常编译器将核查是否为所有的受査异常提供了异常处理器
异常层次结构简化示意图方式除了须要告诉编译器即将返回哪些值,还要告诉编译器有可能发生哪些错误要在技巧的首部强调这个方式可能抛出一个异常,所以要更改方式首部,以反映这个方式可能抛出的检查型异常遇见下边这四种情况是应当抛出异常:
调用一个抛出检查型异常的方式测量到一个错误,并且借助throw句子抛出一个检查型异常程序出现错误java虚拟机或运行时库出现内部错误有些java方式包含在对外提供的类中,对于那些方式,应该通过方式首部的异常规范申明这个方式可能抛出异常假如一个方式有可能抛出多个检查型异常类型,那么就必须在技巧的首部列下来所有的异常类。每个异常类之间用冒号隔开不需要申明java的内部错误,即从Error承继的异常同样,也不应当申明从RuntimeException承继的这些非检查型异常一个方式必须申明所有可能抛出的检查型异常,而非检查型异常要么在你控制之外(Error)要么就应当避开发生( RuntimeException)。如果方式没有申明所有可能发生的受查异常, 编 译器都会发出一个错误消息
对于一个早已存在的异常类, 将其抛出十分容易。在这些情况下:找到一个合适的异常类创建这个类的一个对象将对象抛出。一旦方式抛出了异常, 这个方式就不可能返回到调用者
自定义的类应当包含两个构造器, 一个是默认的构造器;另一个是包含详尽描述信息的构造器
7.2捕获异常要想捕获一个异常, 必须设置 try/catch语句块若果在 try语句块中的任何代码抛出了在 catch 子句中指定的的一个异常类, 那么程序将跳过 try语句块的其余代码程序将执行 catch 子句中的处理器代码假如在 try 语句块中的代码没有拋出任何异常,那么程序将跳过 catch 子句假如方式中的任何代码拋出了一个在 catch 子句中没有申明的异常类型,那么这个方式 就会立即退出
在一个 try 语句块中为每位异常类型使用一个单独的 catch 子句,可以捕获多个异常类型捕获多个异常时, 异常变量蕴涵为 final 变量捕获多个异常除了会让你的代码看起来更简单, 还会更高效。 生成的字节码只包 含一个对应公共 catch 子句的代码块
可以在catch谓词中抛出一个异常可以把原始异常设置为新异常的“原因”,捕获到这个异常时,可以使用getCause()语句获取原始异常,强烈使用这些包装技术。这样可以在子系统中抛出高层异常,而不会遗失原始异常的细节
不管是否有异常被捕获,finally谓词中的代码就会执行
带资源的 try 语句(try-with-resources) 的最简方式为:
2. try块退出时,会手动调用 res.close()。
3. 这个块正常退出时, 或者存在一个异常时, 都会调用 inxloseO 方法, 就好象使用了 finally块一样;还可以指定多个资源
4. 不论这个块怎样退出, in 和 out 都会关掉。如果你用常规方法自动编程,就须要两个嵌 套的 try/finally句子
5. 可以在try首部中提供之前申明的事实最终变量
6.只要须要关掉资源, 就要尽可能使用try-with-Resources 语句
7. try-with-Resources句子自身也可以有 catch 子句和一个 finally 子句。 这些原语会在关掉资源以后执行
堆栈轨迹时程序执行过程中某个特定点上所有挂起的方式调用的一个列表可以调用 Throwable 类的 printStackTrace 方法访问堆栈轨迹的文本描述信息一种更灵活的方式是使用StackWalker类,它会生成一个StackWalker.StackFrame实例流,其中每位实例分别描述一个栈帧
7.3使用异常的方法异常处理不能取代简单的测试
使用异常的基本规则是:旨在异常i情况下使用异常不要过于细化异常
不要只抛出 RuntimeException 异常。应该找寻愈发适当的泛型或创建自己的异常类不要只捕获 Thowable 异常, 否则,会使程序代码更难读、 更难维护考虑检查型异常与非检查型异常的区别假如才能将一种异常转换成另一种愈发适宜的异常,那么不要迟疑充分利用异常层次结构不要压制异常在检查错误时,“ 苛刻 ” 要比纵容更好不要羞于传递异常
7.4使用断定断定机制准许再测试期间向代码中插入一些检测,而再生产代码中会手动删掉那些检测Java 语言引人了关键字 assert。这个关键字有两种方式: assert 条件和 assert 条件:表达式;要想断定x 是一个非负数值, 只须要简单地使用下边这条句子 assert x >= 0; 或者将 x 的实际值传递给 AssertionError 对象, 从而可以在前面显示下来。 assert x >= 0 : x;
在默认情况下,断言是禁用的。可以在运行程序时用-enableassertions 或 -ea 选项启用启用或禁用断定是类加载器的功能。禁用断定时,类加载器会消除断定代码,因此,不会增加程序运行的速率。也可以在某个类或整个包中启用断定可以用选项 -disableassertions 或 -da 禁用某个特定类和包的断定
3 种处理系统错误的机制
抛出一个异常日志使用断定哪些时侯应当选择使用断定呢? 请记住下边几点
断言失败是致命的、 不可恢复的错误。断言检测只是在开发和测阶段打开不应当使用断定向程序的其他部份通告发生了可恢复性的错误,或者,不应当作 为程序向用户通告问题的手段。断言只应当用于在测试阶段确定程序内部的错误位置
断言是一种测试和调试阶段所使用的战术性工具,而日志记录是一种在程序的整个生命周期都可以使用的策略性工具
7.5日志可以很容易地取消全部日志记录,或者仅仅取消某个级别的日志,而且打开和关掉这 个操作也很容易。
可以很简单地严禁日志记录的输出, 因此,将这种日志代码留在程序中的开支很小。
日志记录可以被定向到不同的处理器, 用于在控制台北显示, 用于储存在文件中等。
日志记录器和处理器都可以对记录进行过滤。过滤器可以按照过滤实现器制订的标准 丢弃这些无用的记录项。
日志记录可以采用不同的形式低格,例如,纯文本或 XML。
应用程序可以使用多个日志记录器, 它们使用类似包名的这些具有层次结构的名子, 例如, com.mycompany.myapp0
日志系统的配置由配置文件控制要生成简单的日志记录,可以使用全局日志记录器(global logger) 并调用其 info 方法:
Logger.getClobal 0,info("File->Open menu item selected");如果在适当的地方(如 main 开始)调用Logger.getClobal ().setLevel (Level .OFF); 将会取消所有的日志。
可以调用 getLogger 方法创建或获取记录器:
private static final Logger myLogger=
Logger.getLogger("com.mycompany.myapp");未被任何变量引用的日志记录器可能会被垃圾回收。 为了避免这些情况发生,要 像前面的事例中一样, 用一个静态变量储存日志记录器的一个引用与包名类似,日志记录器名也具有层次结构。事实上, 与包名相比,日志记录器的层次 性更强。 对于包来说,一个包的名子与其父包的名子之间没有语义关系,但是日志记录器 的父与子之间将共享个别属性一般, 有以下 7 个日志记录器级别:
5. 在默认情况下,实际上只记录前3个级别。也可以设置一个不同的级别,例如:
logger,setLevel (Level .FINE);
6. 现在, FINE 和更高级别的记录都可以记录出来。 另外, 还可以使用 Level.ALL 开启所有级别的记录, 或者使用 Level.OFF 关闭所有级别 的记录。
7. 对于所有的级别有下边几种记录方式:
logger.warning(message);
logger,fine(message);
同时, 还可以使用 log 方法指定级别, 例如:
logger.log(Level .FINE, message);
8. 默认的日志记录将显示依据调用堆栈得出的包含日志调用的类名和技巧名
9. 使用logp方式获得调用类和技巧的准确位置
void logp(Level 1, String className, String methodName, String message)
可以通过编辑配置文件来更改日志系统的各类属性。在默认情况下,配置文件存在于
conf/logging.propertiesjava9之前,位于jre/lib/1ogging.properties
本地化的应用程序包含资源包( resource bundle ) 中的本地特定信息。资源包由各个地区 ( 如英国或法国)的映射集合组成。 例如, 某个资源包可能将字符串“ readingFile” 映射成英 文的 “ Reading file” 或者法文的“ Achtung! Datei wird eingelesen”
在默认情况下,日志记录器将记录发送到 ConsoleHandler 中, 并由它输出到 System.err 流中与日志记录器一样,处理器也有日志记录级别。对于一个要被记录的日志记录,它的日 志记录级别必须低于日志记录器和处理器的阀值
在默认情况下, 过滤器按照日志记录的级别进行过滤
ConsoleHandler 类和 FileHandler 类可以生成文本和 XML 格式的日志记录,也可以自定义格式
为一个简单的应用程序, 选择一个日志记录器默认的日志配置将级别等于或低于 INFO 级别的所有消息记录到控制台所有级别为 INFO、 WARNING 和 SEVERE 的消息都将显示到控制台上
7.6调试方法可以用下边的方式复印或记录任意变量的值:
System.out.println( "x=" + x);
//或
Logger.getClobal0- info(nx=" + x);在每一个类中放置一个单独的 main方式,就可以对每一个类进行单元测试使用JUiiit,这是一个十分常见的单元测试框架,利用它可以很容易地组织测试用例套件日志代理是一个泛型的对象, 它可以查获方式调用, 并进行日志记 录,然后调用超类中的方式利 用 Throwable 类提供的 printStackTmce 方法,可以从任何一个异常对象中获得堆栈 情况—般来说, 堆栈轨迹显示在 System.err 上。将一个程序中的错误信息保存在一个文件中是十分有用的,错误信息 被发送到 System.err 中,而不是 System.out 中采用下边的方法捕获错误流:
java MyProgram 2> errors.txt让非捕获异常的堆栈轨迹那些内容记录到一个文件中。可以调用静态的 Thread.setDefaultUncaughtExceptionHan dler 方法改变非捕获异常的处理器要想观察类的加载过程, 可以用 -verbose 标志启动 Java 虚拟机-Xlint 选项告诉编译器找出常见的代码问题java 虚拟机降低了对 Java 应用程序进行监控和管理的 支持java任务控制器是一个专业级性能剖析和确诊工具,包含在OracleJDK中,可以免费用于开发