java的反射机制使得程序获得在运行期获得类字节码中的任何信息。通过Class对象配合java.lang.reflect包中的类可以访问类的限定名,类定义,变量,方法,泛型等所有字节码中的信息。下面就来研究反射机制API级别的原理!
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement
这里可以看出Class是带有泛型的,可以被序列化,然后实现的后面三个接口都是java.lang.reflect包中的接口,这些后面研究。下面根据Class开放的公共方法来探寻Class的原理
1,forName(String className)
这个方法是常用的,根据类的全限定名获取Class对象
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
这里面使用的两个方法都是native方法,不看实现了,暂且认为这个方法是能成功的。
2,forName(Module module, String name)
这个方法就厉害了,jdk9中新加的方法,module模块的意思,这方法一眼就看出来是根据模块中的类名获取类!但是其实现肯定是看不懂了。
3,asSubclass(Class clazz)
public <U> Class<? extends U> asSubclass(Class<U> clazz) {
if (clazz.isAssignableFrom(this))
return (Class<? extends U>) this;
else
throw new ClassCastException(this.toString());
}
就是将父类型强制转换为子类
4,cast(Object obj)
public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj));
return (T) obj;
}
将obj强制转换为Class中保存的类型,若obj没有继承T,则抛出异常。
5,public boolean desiredAssertionStatus()
public boolean desiredAssertionStatus() {
ClassLoader loader = getClassLoader0();
// If the loader is null this is a system class, so ask the VM
if (loader == null)
return desiredAssertionStatus0(this);
// If the classloader has been initialized with the assertion
// directives, ask it. Otherwise, ask the VM.
synchronized(loader.assertionLock) {
if (loader.classAssertionStatus != null) {
return loader.desiredAssertionStatus(getName());
}
}
return desiredAssertionStatus0(this);
}
每次使用断言时,断言状态将保存到desiredAssertionStatus中,这是一个Map实例。然后这个方法就把对象返回到断言时候的状态!
6,public AnnotatedType[] getAnnotatedInterfaces()
获取所有的注解类型,AnnotatedType是表示注解的接口
7,public Annotation[] getAnnotations()
返回注解数组,java.lang.annotation.Annotation就是定义注解的真面目:
8,getCanonicalName()
输出类的全限定名,看例子:
public class Clazz {
public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
Class<HelloWorld> cla = (Class<HelloWorld>) hello.getClass();
System.out.println(cla.getCanonicalName());
int[] nums = {1,2,3,4,5,6};
Class<? extends int[]> intClass = nums.getClass();
System.out.println(intClass.getCanonicalName());
}
}
输出:
wo.HelloWorld int[]
看其实现:
public String getCanonicalName() {
if (isArray()) {
String canonicalName = getComponentType().getCanonicalName();
if (canonicalName != null)
return canonicalName + "[]";
else
return null;
}
if (isLocalOrAnonymousClass())
return null;
Class<?> enclosingClass = getEnclosingClass();
if (enclosingClass == null) { // top level class
return getName();
} else {
String enclosingName = enclosingClass.getCanonicalName();
if (enclosingName == null)
return null;
return enclosingName + "." + getSimpleName();
}
}
若是数组的话,先获取组件类型 的名字,然后后面加上[]
若是匿名类,直接返回null
正常类的话先获取其包名,然后通过getSimpleName()方法获取其简单名。
public String getSimpleName() {
if (isArray())
return getComponentType().getSimpleName()+"[]";
String simpleName = getSimpleBinaryName();
if (simpleName == null) { // top level class
simpleName = getName();
return simpleName.substring(simpleName.lastIndexOf('.')+1); // strip the package name
}
return simpleName;
}
public String getName() {
String name = this.name;
if (name == null)
this.name = name = getName0();
return name;
}
// cache the name to reduce the number of calls into the VM
private transient String name;
private native String getName0();
可以看到,最终还是调用的native方法,这是用C语言去方法区从Class字节流中获取到了类名。
9,ClassLoader getClassLoader()
获取加载该类的ClassLoader,
10,getComponentType()
获取数组类型的Class对象
public class Test {
public static void main(String[] args) {
Test[] te = new Test[3];
Class cla = te.getClass();
System.out.println(cla.getComponentType().getCanonicalName());
Class test = Test.class;
//报错,因为test.getComponentType()返回null,test不是数组
//System.out.println(test.getComponentType().getCanonicalName());
}
}
11,Constructor getConstructor(Class<?>... parameterTypes)
根据特定的参数类型及数量返回一个构造器对象!一会学习反射包时看看这个构造器对象能干什么!
12,getDeclaredConstructor()
与getConstructor()的区别是getConstructor只能返回public的构造器,getDeclaredConstructor能返回所有内类型的构造器!其他的相似的方法与这个类似
13,getDeclaredClasses()
返回内部类级别的Class对象!Declared在这里是内部的意思
14,getDeclaredField(String name)
根据名字返回类变量或实例变量信息
15,getDeclaringClass()
与getDeclaredClasses()相反,这个方法是根据内部类找外部类
16,getEnclosingClass()
与getDeclaringClass()有相同的功能,不同的是他有两个兄弟方法:getEnclosingConstructor()与getEnclosingMethod()方法,这两个方法找在构造器或方法中定义的内部类!
17,getEnumConstants()
返回枚举对象数组
public class Test{
enum A{
one,two,three;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
A[] a= A.class.getEnumConstants();
for(A aa : a) {
System.out.println(aa);
}
}
}
输出:
one two three
18,getGenericInterfaces()与getInterfaces()
一个栗子看出区别:
import java.lang.reflect.Type;
public class Test implements A<Test>,B{
public static void main(String[] args) throws NoSuchMethodException, SecurityException {
Class<?>[] a = Test.class.getInterfaces();
for(Class aa : a)
System.out.println(aa);
System.out.println("-----------");
Type[] tes = Test.class.getGenericInterfaces();
for(Type te : tes)
System.out.println(te);
}
}
interface A<T>{}
interface B{}
interface C extends A{}
输出:
interface one.A interface one.B ----------- one.A<one.Test> interface one.B
19,getModifiers
JAVA 反射机制中,getModifiers()方法返回int类型值表示该字段的修饰符。
其中,该修饰符是java.lang.reflect.Modifier的静态属性。
对应表如下:
PUBLIC: 1 PRIVATE: 2 PROTECTED: 4 STATIC: 8 FINAL: 16 SYNCHRONIZED: 32 VOLATILE: 64 TRANSIENT: 128 NATIVE: 256 INTERFACE: 512 ABSTRACT: 1024 STRICT: 2048
20,Package getPackage()
返回包名称
21,getResource()
返回类在文件系统中的路径
22,boolean isSynthetic()
这个方法有意思,很好理解,就是如果这个类或方法或字段是编译器自动生成的,那返回值就是true,比如枚举中的values()方法!
23,isPrimitive()
此方法主要用来判断Class是否为原始类型(boolean、char、byte、short、int、long、float、double)
24,isMemberClass()
判断类是不是成员类(内部类,除了方法内部类)
25,isLocalClass()
判断类是不是局部类(方法内部类)
26,isAnonymousClass()
判断shi不是匿名类
27,isInstance(Object obj);
相当于instanceof
28,isAssignableFrom(Class<?> cls)
判断当前Class对象和cla对象是不是同一个类的子类,跟instanceof相似
29,newInstance()
灵魂方法,用于创建一个对象,和new对比一下:
- 使用new时,jvm会自动加载class自己流,而newInstance()需要通过类加载器手动加载
- newInstance()只能调用无参构造器
- new更高效
该包下共有30个左右的接口和类
1,Type接口
这个接口主要是用来表示泛型的!像上面介绍的getGenericInterfaces()的方法就返回Type数组,打印之后就是带有泛型的接口!
Type只有一个默认方法:getTypeName()
其下的子接口
WildcardType:用于带有通配符的泛型,它有两个方法getUpperBounds()和getLowerBounds()分别返回泛型的上界和下界。
TypeVariable:这个接口集成了注解和泛型的功能
ParameterizedType:这个接口用于参数泛型
GenericArrayType:用于代表数组中的泛型
2,Constructor与Field与Method
这三个类分别表示构造器,域,方法,类中都有一些基本方法,像获取类型,参数,泛型,注解的信息。Constructor与Method这两个类都继承于Executable,Executable定义了一些通用的方法。
Executable实现了Member接口,Member接口就规定了PUBLIC与DECLARED,分别代表可见不可见。当反射获取私有的方法时,就是以DECLARED为标志!
3,Array
反射包中的Array不允许实例化,只能通过Class中特殊的方法生成,用来表示数组的种种信息。
反射包中大部分的方法都是native方法,这些native方法就是用来从jvm中的方法区获取数据,然后用这些信息来组成新类(Constructor与Field与Method等)。然后根据这些新类做到对原Class对象的操纵。