5.7 反射
反射(reflection library)提供了非常丰富的工具集,以便编写能够动态操纵Java代码的程序.这项功能被大量地应用于JavaBeans中,它是Java组件的体系结构.使用反射,Java可以支持Visual Basic用户习惯使用的工具,特别是在设计或运行中添加新类时,能够快速地应用开发工具动态地查询新添加类的能力.能够分析类能力的程序称为反射,反射机制可以用来:
在运行中分析类的能力
在运行中查看对象,例如,编写一个toString类方法供所有类使用
实现通用的数组操纵代码
利用Method对象,这个对象很像C++中的函数指针
5.7.1 Class类
在程序运行期间, Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识.这个信息跟踪着每个对象所属的类.虚拟利用运行时类型信息选择相应的方法执行.然而,可以 通过专门的Java类访问这些信息,保存这些信息的类称为Class,Object类中的 getClass()方法将返回一个Class类型的实例.
Employee e;
...
Class cl = e.getClass();
如同用一个Employee对象表示一个特定的雇员属性一样,一个Class对象将表示一个特定类的属性.
最常用的Class方法就是getName,这个方法将返回类的名字,例如:
System.out.println(e.getClass().getName() + " " + e.getName());
如果e是一个雇员,则会打印出
Employee Harry
如果e是经理,则会打印出
Manager Harry
获取Class类对象的第二种方法是调用静态方法forName:
String className = "java.util.Date";
Class cl = Class.forName(className);
获取Class类对象的第三种方法非常简单,如果T是任意的Java类型,T.class将代表匹配的类对象,例如:
Class cl1 = Date.class;
Class cl2 = int.class;
Class cl3 = Double[].class;
一个Class对象实际上表示的是一个类型.
虚拟机为每个类型管理一个Class对象,因此,可以利用==运算符实现两个类对象比较的操作.例如,
if (e.getClass() == Employee.class) ...
还有一个很有用的方法
newInstance(),可以用来快速地创建一个类的实例.例如
e.getClass().newInstance();
创建了一个与e具有相同类型的实例.newInstance方法调用默认的构造器(没有参数的构造器)初始化新创建的对象.
将forName与newInstance配合起来使用,可以根据存储在字符创中的类名创建一个对象.
String s = "java.util.Date";
Object m = Class.forName(s).newInstance();
注释:newInstance方法对应C++虚拟构造器的习惯用法.然而,C++的虚拟构造器不是一种语法特性.需要由专门的库支持,Class类与C++中的 type_info类相似,getClass方法与C++中的 typeid 运算符等价.但Java中的Class比C++中的type_info功能强.C++中的type_info只能以字符串的形式显示一个类型的名字,而不能创建那个类型的对象.
5.7.2 捕获异常
当程序运行过程中发生错误时,就会"抛出异常".抛出异常比终止程序要灵活得多,这是因为可以提供一个"捕获"异常的处理器对异常情况进行处理.如果没有提供处理器,程序就会终止,并在控制台上打印出一条信息,其中给出了异常的类型.
异常有两种类型:未检查异常和已检查异常,对于已检查异常,编译器将会检查是否提供了处理器.然而,还有很多常见的异常,例如,访问 null 引用,都属于未检查异常,编译器不会查看是否为这些错误提供了处理器.
Class.forName方法就是一个抛出已检查异常的例子.将可能抛出已检查异常的一个或多个方法调用代码放在 try 块中,然后在 catch 子句中提供处理器代码.
try
{
statement that might throw exceptions
}
catch (Exception e)
{
handler action
}
下面是一个示例:
try
{
String name = ...; // get Class name
Class cl = Class.forName(name); // might throw exception
do something with cl
}
catch (Exception e)
{
e.printStackTrace();
}
java.lang.Class方法如下所示:
static Class forName(String className);
返回描述类名为className的Class对象
Object newInstance();
返回这个类的一个新实例
5.7.3 利用反射分析类的能力
在java.lang.reflect包中有三个类 Field,Method,Constructor分别用于描述类的域,方法和构造器.这三个类都有一个叫做getName的方法,用来返回项目的名称.Field类有一个getType方法,用来返回描述域所属类型的Class对象.Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回返回类型的方法.这三个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述 public 和 static 这样的修饰符使用状况.Class类的getFields,getMethods,getConstrution方法将返回类提供的 public 域,方法和构造器数组.
5.7.4 在运行时使用反射分析对象
5.7.5 使用反射编写泛型数组代码
5.7.6 调用任意方法
5.8 继承设计的技巧
下面给出一些对设计继承关系很有帮助的建议.1.将公共操作和域放在超类
2.不要使用受保护的域
3.使用继承实现"is-a"关系
4.除非所有继承的方法都有意义,否则不要使用继承
5.在覆盖方法时,不要改变预期的行为
6.使用多态,而非类型信息
7.不要过多地使用反射