总论:java中所有对象(object)不是引用类型(reference)就是基本类型(primitive)。不管什么类型对象,java虚拟机都会为之实例化一个
java.lang.Class的不可变实例(Class类的实例就是运行的java应用里的classes和interfaces),这个实例会提供方法来检测对象的成员(members )和类型(type)信息。Class也有能力创建新的classes和objects。最重要的是Class是所有反射(Reflection )APIs的入口。tip:java.lang.Class<T>继承自java.lang.Object第一部分:获得类除了
java.lang.reflect.ReflectPermission,
java.lang.reflect里的其他类都没有public构造器。为了得到
java.lang.reflect的其他类(如Method,
Array,
Field),就得调用
Class中的合适的方法。依据我们的代码是否可以访问object,类的名字,类型,存在的类
Class(anobject, the name of class, a type, or an existing
Class),我们有几种得到
Class的方式。下面依次介绍:Object.getClass()
这种方式只对引用类型有效。1. Class c = "a string".getClass();// 返回String的类。2.import java.util.HashSet;import java.util.Set;Set<String> s = new HashSet<String>(); Class c = s.getClass();// 返回
java.util.HashSet类。The .class Syntax 如果无法获得实例但是可以获得类型,我们就可以通过在Class后面加".class"来得到此类的Class。这也是基础类型(primitive type)获得相应Class最容易的方式。boolean b;Class c = b.getClass(); // 编译时错误 Class c = boolean.class; // 正确Class c = int[][][].class;// 正确Class.forName() 如果知道一个类的完整路径,那么也可以通过静态方法
Class.forName()获得相应的
Class。但这不能用于基础类型。Class c = Class.forName("com.duke.MyLocaleServiceProvider");Class cDoubleArray = Class.forName("[D"); // 返回的类型与 double[].class 一致。Class cStringArray = Class.forName("[[Ljava.lang.String;"); // String[][].classTYPE Field for Primitive Type Wrappers
基本类型可以通过".class"语法获得相应的类,但也可以通过其包裹类的TYPE属性(field)获得。Class c = Double.TYPE; // 即 double.classClass c = Void.TYPE; // void.classMethods that Return Classes
Class.getSuperclass()Class.getClasses() 返回此类中的公开的classes,interfaces 和enums成员。Class<?>[] c = Character.class.getClasses();还有其他的等等。第二部分:检测类(Class)的修饰符和类型访问修饰符:public,protected,private需要重写的修饰符: abstract限制只能有一个实例的修饰符:static防止值被修改的修饰符:final强制严格的浮点行为的修饰符:strictfp注释Annotations
java.lang.reflect.Modifier包含了所有修饰符的声明。也包含了由
Class.getModifiers()返回的修饰符集合的 解码的方法。Modifier.toString(c.getModifiers()); // c 是一个类TypeVariable[] tv = c.getTypeParameters();Type[] intfs = c.getGenericInterfaces();打印结果如下:$ java ClassDeclarationSpy java.util.concurrent.ConcurrentNavigableMapClass: java.util.concurrent.ConcurrentNavigableMapModifiers: public abstract interfaceType Parameters: K VImplemented Interfaces: java.util.concurrent.ConcurrentMap<K, V> java.util.NavigableMap<K, V>Inheritance Path: -- No Super Classes --Annotations: -- No Annotations --第三部分:发现类成员(Members)Class 提供的访问fields,methods和constructors的方法(methods)可以分成两种类型:枚举所有成员的方法(methods)和寻找特殊成员的方法(methods)。Class Methods for Locating Fields
Class API | List of members? | Inherited members? | Private members? |
getDeclaredField() | no | no | yes |
getField() | no | yes | no |
getDeclaredFields() | yes | no | yes |
getFields() | yes | yes | no |
Class Methods for Locating Methods
Class API | List of members? | Inherited members? | Private members? |
getDeclaredMethod() | no | no | yes |
getMethod() | no | yes | no |
getDeclaredMethods() | yes | no | yes |
getMethods() | yes | yes | no |
Class Methods for Locating Constructors
Class API | List of members? | Inherited members? | Private members? |
getDeclaredConstructor() | no | N/A1 | yes |
getConstructor() | no | N/A1 | no |
getDeclaredConstructors() | yes | N/A1 | yes |
getConstructors() | yes | N/A1 | no |
1 Constructors are not inherited.c.getPackage();c.getConstructors();c.getFields();c.getMethods();打印结果如下:$ java ClassSpy java.lang.ClassCastException CONSTRUCTORClass: java.lang.ClassCastExceptionPackage: java.langConstructor: public java.lang.ClassCastException() public java.lang.ClassCastException(java.lang.String)或者如下:$ java ClassSpy java.nio.channels.ReadableByteChannel METHODClass: java.nio.channels.ReadableByteChannelPackage: java.nio.channelsMethods: public abstract int java.nio.channels.ReadableByteChannel.read (java.nio.ByteBuffer) throws java.io.IOException public abstract void java.nio.channels.Channel.close() throws java.io.IOException public abstract boolean java.nio.channels.Channel.isOpen()《Think in java》中写道“Class 类支持反射的概念,Java附带的库java.lang.reflect包含了Field,Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行期创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的属性,用invoke()方法调用与Method对象关联的方法。另外,你还可以调用getFields(),getMethods(),getConstructors()等等很便利的方法,以返回表示属性、方法以及构造器的对象数组,这些对象(在JDK文档中,可找到与Class类相关的更多的资料)。这样,匿名对象的类信息就能在运行期被完全确定下来,而在编译期不需要知道任何事情。重要的是,反射机制并没有什么魔法。当你通过反射与一个未知类型的对象打交道时,JVM只是简单地检查这个对象,看它属于哪个特定的类(就象RTTI那样)。但在这之后,在做其它事情之前,必须加载那个类的Class对象。因此,那个类的.class文件对于JVM来说必须是可获取的,要么在本地机器上,要么可以通过网络取得。所以RTTI和反射之间真正的区别只在于,对RTTI来说,编译器在编译期打开和检查.class文件。(换句话说,我们可以用“普通”方式调用一个对象的所有方法。)而对于反射机制来说.class文件在编译期间是不可获取的,所以是在运行期打开和检查.class文件。 ”应用:使用内省器(Introspector)来抽取出BeanInfo JavaBean模型最关键部分之一,发生在当你从选用区拖动一个Bean,然后把它放置到窗体上的时候。应用程序构建工具必须能够创建这个Bean(如果有缺省构造器就可以创建),然后在不访问Bean的源代码的情况下,抽取出所有必要信息,以创建属性和事件处理器的列表。部分解决方案在第 10 章就出现了:Java的反射机制能发现未知类的所有方法。对于解决JavaBean的这个问题,这是个完美的方案,你不用像其它可视化编程语言那样使用任何语言附加的关键字。实际上,Java语言里加入反射机制的主要原因之一就是为了支持JavaBean(尽管反射也支持对象序列化和远程方法调用)。所以,你也许会认为应用程序构建工具的编写者将使用反射来抽取Bean的方法,然后在方法里面查找出Bean的属性和事件。这当然是可行的,不过Java的设计者希望提供一个标准工具,不仅要使Bean用起来简单,而且对于创建更复杂的Bean,能够提供一个标准方法。这个工具就是Introspector类,这个类最重要的就是静态的getBeanInfo( )方法。你向这个方法传递一个Class对象引用,它能够完全侦测这个类,然后返回一个BeanInfo对象,你可以通过这个对象得到Bean的属性、方法和事件。
代码片段:BeanInfo bi = null;
bi = Introspector.getBeanInfo(bean, Object.class);
PropertyDescriptor[] properties =
bi.getPropertyDescriptors();
for(int i = 0; i < properties.length; i++) {
Class p = properties[i].getPropertyType();
if(p == null) continue;
print("Property type:\n " + p.getName() +
"Property name:\n " + properties[i].getName());
Method readMethod = properties[i].getReadMethod();
if(readMethod != null)
print("Read method:\n " + readMethod);
Method writeMethod = properties[i].getWriteMethod();
if(writeMethod != null)
print("Write method:\n " + writeMethod);
print("====================");
}
print("Public methods:");
MethodDescriptor[] methods = bi.getMethodDescriptors();
for(int i = 0; i < methods.length; i++)
print(methods[i].getMethod().toString());
print("======================");
打印结果:
class name: Frog
Property type:
Color
Property name:
color
Read method:
public Color getColor()
Write method:
public void setColor(Color)
====================
Property type:
Spots
Property name:
spots
Read method:
public Spots getSpots()
Write method:
public void setSpots(Spots)
====================
创建一个BeanInfo对象,成功的话,就调用BeanInfo的方法得到有关其属性、方法和事件的信息。你会发现Introspector.getBeanInfo()方法有第二个参数,它用来告诉Introspector在哪个继承层次上停止查询。因为我们不关心来自Object的方法,所以这里的参数让Introspector在解析来自Object的所有方法前停止查询。对于属性来说,getPropertyDescriptors( )返回类型为PropertyDescriptor的数组,你可以针对每一个PropertyDescriptor都调用getPropertyType()来得到“通过属性方法设置和返回的对象”类型。然后,针对每个属性,你可以通过getName( )方法得到它的别名(从方法名中抽取),通过getReadMethod( )方法得到读方法,通过getWriteMethod()方法得到写方法。后两个方法返回Method对象,它们能够用来在对象上调用相应的方法(这是反射的一部分)。对于公共方法(包括属性方法),getMethodDescriptors( )方法返回类型为MethodDescriptor的数组。对于数组的每个元素,你可以得到相关联的Method对象,并显示它们的名称。