Java和C++异
- 在C++中,throw说明符在运行时执行。Java在编译时执行。
处理错误
-
异常处理的任务就是将控制权从产生错误的地方转移到能够处理这种情况的错误处理器。
-
如果由于出现错误而使得某些操作没有完成,程序应该:返回到一种安全状态,并能够让用户执行其他的命令;或者允许用户保存所有工作的结果,并以妥善的方式终止程序。
-
程序中可能会出现的错误和问题
- 用户输入错误
- 设备错误。如打印机在打印过程中可能没有纸了。
- 物理限制。磁盘已满。
- 代码错误。数组越界。
- 某个抛出 (throw) 一个封装了错误信息的对象。需要注意的是,这个方法将会立刻退出,并不返回正常值(或任何值)。此外,也不会从调用这个方法的代码继续执行,取而代之的是,异常处理机制开始搜索能够处理这种异常状况的异常处理器(exception handler)。
异常分类
- 所有的异常都是由Throwable继承而来。
- 在下一层立即分解为两个分支:Error 和 Exception。Error类层次结构描述了 Java运行时系统的内部错误和资源耗尽错误。你的应用程序不应该抛出这种类型的对象。如果出现了这样的内部错误,除了通知用户,并尽力妥善地终止程序之外,你几乎无能为力。这种情况很少出现。
- Exception层次结构又分解为两个分支:一个分支派生于RuntimeException ;另一个分支包含其他异常。一般规则是:由编程错误导致的异常属于RuntimeException ;如果程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常。
派生于RuntimeException的异常包括以下问题:
- 错误的强制类型转换。
- 数组访问越界。
- 访问null指针。
不是派生于RuntimeException的异常包括:- 试图超越文件末尾继续读取数据。
- 试图打开一个不存在的文件
- 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在
- Java语言规范将派生于Error类或RuntimeException类的所有异常称为非检查型(unchecked)异常,所有其他的异常称为检查型(checked)异常。编译器将检查你是否为所有的检查型异常提供了异常处理器。
声明异常
一个方法必须声明所有可能抛出的检查型异常,不应该声明非检查型异常。如果你的方法没有声明所有可能发生的检查型异常,编译器就会发出一个错误消息。
如果在子类中覆盖了超类的一个方法,子类方法中声明的检查型异常不能比超类方法中声明的异常更通用(子类方法可以抛出更特定的异常,或者根本不抛出任何异常)。
例如,如果覆盖JComponent.paintComponent方法,由于超类中这个方法没有抛出任何检查型异常,所以,你的paintComponent也不能抛出任何检查型异常。
一个个方法抛出的异常可能属于这个异常类,也可能属于这个异常类的任意一个子类。
例如, FileinputStream构造器声明有可能抛出一个IOExcetion异常,在这种情况下,你并不知道具体是哪种IOExcetion异常。它既可能是IOException异常,也可能是其某个子类的对象,例如, FileNotFoundException。
捕获异常
捕获(try-catch)那些你知道如何处理的异常,而继续传播(throwable)那些你不知道怎样处理的异常。
再次抛出异常与异常链
为了改变异常的类型,可以在catch子句中抛出一个异常。
finally 子句
不管是否有异常被捕获,finally子句中的代码都会执行。
finally子句的体要用于清理资源。不要把改变控制流的语句(return,throw, break,continue)放在 finally 子句中。
场景
清理必须清理的资源
try-with-Resouroes 语句
分析堆栈轨迹元素
堆栈轨迹( stack trace)是程序执行过程中某个特定点上所有挂起的方法调用的一个列表。
访问堆栈轨迹的文本描述信息
- Throwable类的printStackTrace
var t = new Throwable();
var out = new StringWriterf);
t.printStackTrace(new PrintWriter(out));
String description = out.toStringl);
- StackWalker类
使用异常的技巧
- 异常处理不能代替简单的测试。
- 不要过分地细化异常。
- 充分利用异常层次结构。
- 不要压制异常。
- 在检测错误时,“苛刻”要比放任更好。
- 不要羞于传递异常。
注释:规则5、6可以归纳为“早抛出,晚捕获”。
使用断言
断言的概念
断言机制允许在测试期间向代码中插人一些检查,而在生产代码中会自动删除这些检查。
启用和禁用断言
- 不必重新编译程序来启用或禁用断言。启用或禁用断言是类加载器的功能。
- 可以使用开关有选择地启用或禁用某个特定类和包中的断言。
使用断言完成参数检查
Java中3种处理系统错误的机制:
- 抛异常
- 日志
- 断言
什么时候应该选择使用断言呢?
- 断言失败是致命的、不可恢复的错误。
- 断言检查只是在开发和测试阶段打开。
日志
日志API优点
基本日志-全局日志
Logger.getGlobal().setLevel(Level.OFF);
Logger.getGlobal().info("global");
String s = "jdiofjeijgfo;akjfoiej";
System.out.println("logging" + s);
高级日志
定义自己的日志记录器
private static final Logger myLogger = Logger.getLogger("com.ypp.corejava"); //工厂方法创建
日志记录器的层次结构
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
- 默认情况下,只记录前3个级别。
- 应该使用CONFIG、FINE、FINER和FINEST级别来记录那些有助于诊断但对用户意义不大的调试信息。
如果将记录级别设置为比INFO更低的级别,还需要修改日志处理器的配置才能正确打出日志。
跟踪执行流
- logp方法获得调用类和方法的确切位置.
默认的日志记录将显示根据调用堆栈得出的包含日志调用的类名和方法名。不过,如果
虚拟机对执行过程进行了优化,就得不到准确的调用信息。
- entering(), exiting() 生成FINER级别而且以字符串ENTRY和RETURN开头的日志记录。
public int read(String file, String pattern)
{
int count=1;
logger.entering("com.mycompany.mylib.Reader","read",new Object[]{file,pattern});
logger.exiting("com.mycompany.mylib.Reader","read",count);
return count;
}
记录异常
throwing(), log方法。
修改日志管理器配置
本地化
本地化的应用程序包含资源包(中的本地特定信息。资源包包括一组映射,分别对应各个本地化环境。这些文件都是纯文本文件,包含如下所示的条目:
readingFile=Achtung! Datei wird eingelesen
renamingFile=Datei wird umbenannt
处理器
- 在默认情况下,日志记录器将记录发送到ConsoleHandler ,并由它输出到System.err流。
- 与日志记录器一样,处理器也有日志级别。对于一个要记录的日志记录,它的日志级别
必须高于日志记录器和处理器二者的阈值。日志管理器配置文件将默认的控制台处理器的日
志级别设置为java.util.logging.ConsoleHandler.level=INFO - 要想记录FINE级别的日志,就必须修改配置文件中的默认日志记录器级别和处理器级别。另外,还可以绕过配置文件,安装你自己的处理器。
Logger logger = Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false); //默认会发到consoleHandler。不设为false,会看到两次消息记录
ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler); //安装自己的处理器
logger.fine("a fine message");
- 其它处理器:SocketHantRer将记录发送至l指定的主机和端口。FileHandler将记录收集到文件中。
- 过扩展Handler类或StreamHandler类自定义处理器。
过滤器
- 默认情况下,会根据日志记录的级别进行过滤。
- 每个日志记录器和处理器都有一个可选的过滤器来完成附加的过滤,调用setFilter方法就可以
了。注意,同一时刻最多只能有一个过滤器。 - 要定义一个过滤器,需要实现Filter接口并定义以下方法:
boolean isLoggable(LogRecord record)
格式化器
1.ConsoleHandler类和FileHandler类可以生成文本和XML格式的日志记录。不过,你也可
以自定义格式。这需要扩展Formatter类并覆盖下面这个方法:String format(LogRecord record)。
2. 在format方法中,可能会调用下面这个方法:String formatMessage(LogRecord record)
这个方法对记录中的消息部分进行格式化,将替换参数并应用本地化处理。
3. 调用setFormatter方法将格式化器安装到处理器中。
调试技巧