一、异常
用户期望在出现错误时,程序能够采取合理的行为。如果由于出现错误而使得某些操作没有完成,程序应该:
- 返回一种安全状态,并能够让用户执行其他的命令
- 允许用户保存所有工作的结果,并以妥善的方式终结程序
异常分类
所有的异常都是由Throwable继承而来,但在下一层立即分解为两个分支:Error和Exception。Error类层次结构描述了Java运行时系统的内部错误或资源耗尽错误。你的程序不应该抛出这种类型的错误。
在程序设计时,要重点关注Exception层次结构。这个层次结构又分为两个分支:一个分支派生于RuntimeException,另一个分支包含其他异常。
派生于RuntimeException的异常包括以下问题:
- 错误的强制类型转换
- 数组访问越界
- 访问null指针
Java语言规范将派生自Error类或RuntimeException类的所有异常称为非检查型异常,所有其他异常称为检查型异常。
声明检查型异常
如果遇到无法处理的情况,Java方法可以抛出一个异常。要在方法首部指出这个方法可能抛出一个异常,所以要修改方法首部,以反映这个方法可能抛出的检查型异常。至于什么时候需要在方法中用throws语句声明异常。需要记住在下面四种情况时会抛出异常:
- 调用了一个抛出检查型异常的方法,例如,FileInputStream构造器
- 检测到一个错误,并且利用throw语句抛出一个检查型异常
- 程序出现错误
- Java虚拟机或运行时库出现内部错误
如果子类中覆盖了超类的一个方法,子类方法中声明的检查型异常不能比超类方法中声明的异常更通用。
创建异常类
你的代码可能会遇到任何标准异常类都无法描述清楚的问题,这种情况下,创建自己的异常类就是一件顺理成章的事情。我们需要做的只是定义一个派生于Exception的类,或者派生于Exception的某个子类。
习惯做法是,自定义的这个类应该包含两个构造器,一个是默认构造器,另一个是包含描述信息的构造器。
public class FileFormatException extends IOException {
public FileFormatException() {
}
public FileFormatException(String cause) {
super(cause);
}
}
二、断言
在Java语言中,给出了3种处理系统错误的机制
- 抛出一个异常
- 日志
- 使用断言
什么时候应该选择使用断言呢?请记住下面几点:
- 断言失败是致命的,不可恢复的错误
- 断言检查知识在开发和测试阶段打开
因此,不应该使用断言向其他程序的其他部分通知发生了可恢复性错误,或者不应该利用断言与程序用户沟通问题。断言只应该用于在测试阶段确定程序内部错误的位置。
三、泛型 todo-完善
虚拟机中的泛型类型信息
Java泛型的突出特征之一是在虚拟机中擦除泛型类型。令人奇怪的是,擦除的类仍然保留原先泛型的微弱记忆。
可以使用反射API来确定:
- 这个泛型方法有一个名为T的类型参数
- 这个类型参数有一个子类型限定, 其自身又是一个泛型类型
- 这个限定类型有一个通配符参数
- 这个通配符参数有一个超类型限定
- 这个泛型方法有一个泛型数组参数
为了表述泛型类型声明,可以使用java.lang.reflect包中的接口Type。这个接口包含以下子类型:
- Class类,描述具体类型
- TypeVariable接口,描述类型变量(如T extend Comparable)
- WildcardType接口,描述通配符(如 ?extend T)
- ParameterizedType接口,描述泛型类或接口类型(Comparable<? super T>)
- GenericArrayType接口,描述泛型数组(如 T[])
WildcardType
Type[] getUpperBounds();
获得这个类型变量的子类(extends)限定,否则,如果没有子类限定,则返回长度为0的数组
Type[] getLowerBounds();
获得这个类型变量的超类(super)限定,否则,如果没有超类限定,则返回长度为0的数组。
ParameterizedType
Type getRawType();
获得这个参数化类型的原始类型
Type[] getActualTypeArguments();
获得这个参数化类型声明的类型参数
Type getOwnerType();
如果是内部类型,则返回其外部类类型,如果是一个顶级类型,则返回null。
GenericArrayType
Type getGenericComponentType();
获得这个数组类型声明的泛型类型元素。