一、异常
1、异常分类
异常对象都是派生于Throwable的一个实例。Java中异常层次结构如下图所示
1.1 Error
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。
1.2 Exception
在设计Java程序时,需要关注Exception层次结构:
1.2.1 RuntimeException
由程序错误导致的异常属于RuntimeException,派生于RuntimeException的异常包含下面几种情况:
- 错误的类型转换
- 数组访问越界
- 访问null指针
1.2.2 IOException
程序本身没有问题,像I/O错误这类问题导致的异常属于其他异常,派生于IOException的异常包含下面几种情况:
- 试图在文件尾部后面读取数据
- 试图打开一个不存在的文件
- 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在
1.3
java语言规范将派生于Error类或RuntimeException类的所有异常称为非受查(unchecked)异常,所有其他异常称为受查异常。编译器将核查是否为所有的受查异常提供了异常处理器。
应该抛出异常的情况
- 调用一个抛出受查异常的方法,例如,FileInputStream构造器
- 程序运行过程中发现错误,并且利用throw语句抛出一个受查异常
- 程序出现错误
- Java虚拟机和运行时库出现的内部错误
注意:如果在子类覆盖了超类的一个方法,子类方法中声明的受查异常不能比超类方法声明的异常更通用,也就是说,子类方法中可以抛出更特定的异常,或者根本不抛出异常。如果超类方法没有抛出受查异常,子类也不能抛出受查异常。
1. Throwable() // 构造一个新的Throwable对象
2. Throwable(String message) //构造一个新的Throwable对象,带有描述信息,
//习惯上,所有派生类都支持一个默认构造器和一个带有详细描述信息的构造器
3. String getMessage() //获得描述信息
2、捕获异常
try{
code;
}
catch(Exception e){
handler for this type;
}
finally{
close ;
}
2.1 捕获多个异常
try{
code one;
}
catch(FileNotFoundException e)
{
code1;
}
catch(UnknownHostException e)
//catch(UnknownHostException | FileNotFoundException e)当异常类型之间不存在子类关系时才可以
{
code2;
}
catch(IOException e)
{
code3;
}
e.getMessage() //获得异常信息
e.getClass().getName()// 获得异常类型的实际类型。
2.2 finally
- 无论代码的哪一个部分出现异常,包括catch内的语句,finally内的语句都会执行,但是finally仍然有可能抛出异常。
- 带资源的try语句,类似于python的with语句。
2.3 使用异常的技巧
- 异常处理不能代替简单的测试,例如isEmpty的检测比捕获异常要快。
- 不要过分地细化异常
- 利用异常层次结构
- 不要压制异常
- 在检测错误时,苛刻比放任更好
- 不要羞于传递异常
二、 使用断言
断言机制允许在测试期间插入检查语句,而在代码发检测语句将会自动移走。
assert 条件;
asert 条件:表达式;
以上两种形式都会对条件进行检测,如果结果为false,则抛出一个AssertionError异常。
在第二种形式中,表达式将被传入AssertionError的构造器,并转换成一个消息字符串。(toString())
禁用启动断言
默认情况下断言被禁用。可以用 -ea 选项启动。
Java使用3种处理系统错误的机制:
- 抛出一个异常
- 日志
- 使用断言
断言特点: - 断言失败是致命的、不可恢复的错误
- 断言检查只用于开发和测试阶段
三、记录日志
记录日志API的优点
- 可以容易地取消全部日志记录。
- 可以很简单地禁止日志记录的输出。
- 日志记录可以被定向到不同的处理器,用于在控制台显示,用于存储在文件中等。
- 日志记录器和处理器可以对记录进行过滤。
- 日志记录可以采用不同的方式格式化。
- 应用程序可以使用多个日志记录器
- 在默认情况在,日志系统的配置由配置文件控制。
1. 基本日志
Logger.getGlobal().info("File->Open menu item selected");//使用全局日志记录器
Logger.getGlobal().setLevel(Level.OFF);//取消所有日志
2. 高级日志
private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");//自定义日志记录器
7个日志记录器级别
- SEVERE
- WARNING
- INFO
- CONFIG
- FINE
- FINER
- FINEST
3. 处理器
默认情况,只记录前三个级别。也可以设置其他级别。但是真正要想记录其他级别的日志。就必须修改配置文件的默认日志记录级别和处理器级别。当然除了一个办法,还可以绕过配置文件,安装自己的处理器。
logger.setLevel(Level.FINE)//现在FINE和更高级别的记录都可以记录下来;
mylogger.setLevel(Level.CONFIG);
mylogger.setUseParentHandlers(false);
Handler handler = new ConsoleHandler();
FileHandler handler1 = new FileHandler();//还有一个SocketHandler,将记录发送到特定的主机和端口
FileHandler handler2 = new FileHandler("E:\\Work\\log.txt");//使用自定义文件
mylogger.addHandler(handler);//发送到控制台处理器
mylogger.addHandler(handler1);//发送到本地文件处理器
4. 过滤器,格式化器
默认情况下,过滤器根据日志记录的级别进行过滤。每个日志记录器和处理器都可以有一个可选的过滤器来完成附加的过滤。也可以通过实现Fliter接口并定义下列方法来自定义过滤器。
boolean isLoggable(LogRecord record);
格式化器同样也可以自定义。但需要覆盖下面的方法
String format(LogRecord record)
以及可能会覆盖的方法
String formatMessage(LogRecord record)
String getHead(Handler h)
String getTail(Handler h)
5.日志记录说明
面多如此繁杂的可选项,下面总结一些操作:
- 为一个简单的应用程序,选择一个日志记录器,并把日志记录器命名为与主应用程序包一样的名字。
- 默认的日志配置将级别等于或高于INFO级别的所有消息记录到控制台,用户可以覆盖默认的配置文件。