Java RTTI 与反射基础
引子
考虑如下问题
Animal为Duck,Fish,Zebra对象的父类
假设创建了一个Animal的List,其中有Duck,Fish,Zebra的实例
public class CountAnimal{
public static void main(String args[]){
List<Animal> animals = Arrays.aslist(new Duck(),new Fish(),new Zebra);
}
}
程序员的视角看来,Duck对象,Fish对象,Zebra对象会向上转型成Animal对象,会失去特有的方法。
实际上,容器会将所有的元素都当做Object对象持有。此时,RTTI机制会将ArrayList中的元素由Object自动转型成Animal。这是由泛型机制和类型转换机制保证的。
而在实际应用的过程中,我们可能会实际的需要某种特定的对象,比如,可能需要把Animal列表(List<Animal>)中的Duck对象取出来,并且进行操作,或者要删除所有的Fish对象,这时就需要使用RTTI或反射。
1.RTTI
RTTI(Run-time Type Infornation):运行时类型信息。
通过运行时类型信息,程序能够使用基类的引用来检查这些引用所指的对象的实际派生类型
用途:RTTI可用于查询对象的确切类型,或剔除特例。
下面介绍通过class对象执行RTTI机制的方法。
2.Class对象
(1)Class类与Class对象
Class类与class关键字相似,但是Class类与class并无关联。类似于Object类是一种具体的类,Class类也是一种具体的存在的类,只是名字和class相似。它不是用户自己定义的,而是java体系中的一种类。也就是说,Class类可以直接使用
class Main(){
public static void main(String[] args){
Class cc = null; //直接使用Class类的引用
}
}
Class对象,就是用来创建类的对象,比如定义了一个Shape类
public class Shape(){
}
java会在一个合适的时候生成一个(内容是Shape的)Class类的对象,以记录Shape类的信息,包括字段、方法、父类、实现的接口等等。
然而,用户不可以用类似于new Class()的方法创建Class类的对象,这个对象只能由JVM(java虚拟机)创建。因为这个类没有public的构造方法。
通过获取Class对象可以获取某个特定对象的类型信息。如果某个不知道具体类型的对象和与它对应的Class对象关联,只要知道这个Class对象是什么,就可以知道这个对象的具体类型了。
这样就解决了引子中提到的问题。我们不知道Animal列表里的具体某一个对象是哪一个子类,如果我们可以得到这个对象对应的Class对象,就可以判断其具体类型。
(2)Class对象与RTTI
可通过Class对象执行其RTTI
获得Class对象的方法
- forname()
- .class
- getClass()
class Candy{
}
class Gum{
}
class Cookie{
}
为了说话方便起见,创建以上三个空类
public class SweetShop(){
public static void main(String args[]){
Class candyclass = null;
candyclass = Candy.class;
//将得到Candy的class对象
Class gumclass = null;
try{
gumclass = Class.forName("Gum");
}catch(ClassNotFoundException e){
throw new RuntimeException();//forName()方法会抛出ClassNotFoundException
}
//将得到gum的class对象
Class cookieclass = null;
Cookie cookie = new cookie();
cookieclass = cookie.getClass();
//将得到cookie的class对象
}
}
这里,.class又叫类字面常量。
3.用instanceof,isInstance(),和Class对象的==,equals进行比较
instanceof isInstance 考虑继承,如果A的类继承自B的类,A instance of B 也为true
== equals 对Class对象进行比较,不考虑继承,如果不是确切的相等,就会返回false
4.动态加载
考虑如下情景
现有接口I,A与B同时对接口I进行实现,A为系统提供的,功能上存在一些欠缺,B为网络库中的,功能相对完整但是不一定存在(或不一定找得到)。现在打算实现如下功能:如果B存在优先用B的实现,如果B不存在只好委屈一下用A的实现。
具体一点的例子如下,对于Logging,如果有Log4j首先使用Log4j,没有Log4j使用java类库中的log
利用forName()方法可以实现以上功能,下边是伪代码
boolean isClassPresent(String name){
try{
Class.forName(name)
return true;
}catch(ClassNotFoundException e){
return false;
}
}
//maybe in some function
LogFactory factory;
if(isClassPresent("org.apahe.logging.log4j.Logger")){
factory = createLog4j();
}else{
factory = createJdkLog();
}
5.Class对象的实用方法
(1)获取该类信息
方法 | 功能 |
---|---|
getName() | 获取该类的全名 |
getSimpleName() | 获取该类的名字简写(不包括具体包名) |
getPackage() | 返回package对象 |
方法 | 功能 |
---|---|
isInterface() | 是否是接口 |
isEnum() | 是否是枚举类型 |
isArray() | 是否是Array类型 |
isPrimitive() | 是否是基本类型 |
方法 | 功能 |
---|---|
newInstance() | 可以创建新的对象,调用默认构造方法 |
(2)获取字段信息
方法 | 功能 |
---|---|
getField(name) | 某个public字段,包括父类的 |
getDeclaredField(name) | 某个任意字段,不包括父类 |
getFields() | 所有public字段,包括父类 |
getDeclaredFields() | 所有字段,不包括父类 |
返回的是field对象,有如下实用方法。
值得注意的是,这个field对象不是和具体某个对象相关联的,而是和一个类相关联的。
方法 | 功能 |
---|---|
getName() | field的名字 |
getType() | field的类型 |
getModifiers() | field的修饰符,public/private/… |
get(Object) | 获取一个实例字段的值,Object为传入的实例,如果是静态字段,传入null |
set(Object,Object) | 设置Object字段的值为Object。如果是静态字段,第一个从参数传入null |
setAccessible(boolean) | 传入true获得private的访问权限 |
(3)获取方法信息
方法 | 功能 |
---|---|
getMethod(name, Class…) | 某个public字段,包括父类的 |
getDeclaredMethod(name, Class…) | 某个任意字段,不包括父类 |
getMethods() | 所有public字段,包括父类 |
getDeclaredMethods() | 所有字段,不包括父类 |
返回的是method对象,有如下实用方法。
方法 | 功能 |
---|---|
getName() | 方法的名字 |
getReturnType() | 方法返回值的类型 |
getParameterTypes() | 方法参数的类型 |
getModifiers() | 方法的修饰符,public/private/… |
invoke(Object obj,Object… args) | 调用方法 |
setAccessible(boolean) | 传入true获得private的访问权限 |
关于invoke的举例
static void calltoString(){
Animal animal = new Duck();
Class animalclass = animal.getClass();
Method m = animalclass.getMethod("toString");
m.invoke(animal);//执行animal的m方法
}
(4)构造方法
newInstnce() 自动调用无参构造方法
方法 | 功能 |
---|---|
getMethod(Class…) | 某个public Constructor |
getDeclaredMethod(Class…) | 某个任意Constructor |
getMethods() | 所有public Constructor |
getDeclaredMethods() | 所有Constructor |
setAccessible(boolean) | 传入true获得private的访问权限 |
(5)获取继承关系
方法 | 功能 |
---|---|
getSuperClass() | 返回父类的class对象 |
getInterfaces() | 返回该类实现的Interface的class对象数组 |
isAssignableFrom(Class) | 括号内对象能否赋值给括号外调用者,返回boolean |
参考文献
[1] https://www.cnblogs.com/bethunebtj/p/4680532.html