许吉友 - 运维

反射机制

java的反射机制使得程序获得在运行期获得类字节码中的任何信息。通过Class对象配合java.lang.reflect包中的类可以访问类的限定名,类定义,变量,方法,泛型等所有字节码中的信息。下面就来研究反射机制API级别的原理!

一,java.lang.Class

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就是定义注解的真面目:

img

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对比一下:

  1. 使用new时,jvm会自动加载class自己流,而newInstance()需要通过类加载器手动加载
  2. newInstance()只能调用无参构造器
  3. new更高效

二,java.lang.reflect包

该包下共有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对象的操纵。