# Class 源码解析 JDK8

# 概述

  • Class类实现了Serializable GenericDeclaration Type AnnotatedElement 接口。

    • GenericDeclaration 是接口继承了AnnotatedElement接口,是所有“可以声明(定义)范型变量”的实体(如Class,Constructor,Method)的公共接口。也就是说只有实现了这个接口的才能在对应“实体”上声明“范型变量”。

    • Type是Java语言中所有类型的父接口。它并不是我们平常工作中经常使用的 int、String、List、Map等数据类型,而是从Java语言角度来说,对基本类型、引用类型向上的抽象;

    • AnnotatedElement 表示目前正在此 VM 中运行的程序的一个已注释元素。该接口允许反射性地读取注释。由此接口中的方法返回的所有注释都是不可变并且可序列化的。调用者可以修改已赋值数组枚举成员的访问器返回的数组;这不会对其他调用者返回的数组产生任何影响。

  • java反射是围绕Class类展开的,jvm将字节码文件加载到方法区内存中并返回一个Class对象。

  • java反射使得我们可以在程序运行时动态加载一个类,动态获取类的基本信息和定义的方法,构造函数,域等。还可以动态创建类的实例,执行类实例的方法,获取类实例的域值。反射使java这种静态语言有了动态的特性。

  • 反射是通过反编译将.class文件转换成.java,从而操纵类的属性方法。

# 变量

//构造器缓存
private volatile transient Constructor<T> cachedConstructor;
//实例缓存
private volatile transient Class<?>       newInstanceCallerCache;

private transient String name;

private final ClassLoader classLoader;

# 构造器

  • Class类构造器私有的,不允许手动创建
private Class(ClassLoader loader) {
    // Initialize final field for classLoader.  The initialization value of non-null
    // prevents future JIT optimizations from assuming this final field is null.
    classLoader = loader;
}

# 方法

  • Class类中方法主要有三类:生成实例、获取类信息、类型转换。
    • 生成实例 一类方法主要有两个:forName方法和newInstance方法。
    • 获取类信息 一类方法非常多,作用包括获取类的构造方法,已经声明的字段、方法,获取类或者方法的注解,获取类的包名、父类,以及判断类是否是数组、是否是枚举、是否是接口。
    • 类型转换 该类方法主要有两个: asSubclass方法和 cast

# 生成实例

  • 此方法主要是根据给定的类名,JVM将类加载、链接、初始化;生成类的实例。

  • 初始化信息和生成实例一般包含两个方法 forNamenewInstance:

    • forName有两种声明
    public static Class<?> forName(String className)
                  throws ClassNotFoundException {
          Class<?> caller = Reflection.getCallerClass();
          return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
      }
    
    public static Class<?> forName(String name, boolean initialize,
                                     ClassLoader loader)
          throws ClassNotFoundException
      {
          Class<?> caller = null;
          SecurityManager sm = System.getSecurityManager();
          if (sm != null) {
              // Reflective call to get caller class is only needed if a security manager
              // is present.  Avoid the overhead of making this call otherwise.
              caller = Reflection.getCallerClass();
              if (sun.misc.VM.isSystemDomainLoader(loader)) {
                  ClassLoader ccl = ClassLoader.getClassLoader(caller);
                  if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                      sm.checkPermission(
                          SecurityConstants.GET_CLASSLOADER_PERMISSION);
                  }
              }
          }
          return forName0(name, initialize, loader, caller);
      }
    
    • 最终两个forName方法,都调用了forName0方法,这是一个本地方法。
    • forName方法实现中,出现了两个类,一个是SecurityManager,另一个是ClassLoader
      • 其中SecurityManager是jvm提供的在应用层进行安全检查的机制,应用程序可以根据策略文件被赋予一定的权限,例如是否可以读写文件,是否可以读写网络端口,是否可以读写内存,是否可以获取类加载器……。在进行特殊操作时,需要进行安全检查,从而给程序的运行安全提供一定保障。
      • ClassLoader则涉及到另一个大的话题:类加载,我们程序运行中使用的类都需要由类加载器来完成加载,并执行一定的初始化,随后才可以被我们使用。
  • newInstance:类加载完成后就需要实例化了,需要用到此方法。

public T newInstance()
    throws InstantiationException, IllegalAccessException
{   // 进行安全检查
    if (System.getSecurityManager() != null) {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
    }

  

  // 每次先检查是否有已经缓存过的构造器,如果没有,则重新获取
    if (cachedConstructor == null) {
        if (this == Class.class) {
            throw new IllegalAccessException(
                "Can not call newInstance() on the Class for java.lang.Class"
            );
        }
        try {
            Class<?>[] empty = {};
            // 获取该类已经声明的的无参构造方法
            final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
           // 此处是为了使无参构造方法可以被访问,因为有时,构造方法被声明为private的
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                            c.setAccessible(true);
                            return null;
                        }
                    });
             // 将获取的无参构造方法缓存        
            cachedConstructor = c;
        } catch (NoSuchMethodException e) {
            throw (InstantiationException)
                new InstantiationException(getName()).initCause(e);
        }
    }
    Constructor<T> tmpConstructor = cachedConstructor;
   // 获取构造方法的语言修饰符,诸如public,private,static,final等
    int modifiers = tmpConstructor.getModifiers();
     // 根据已经获取的语言修饰符判断是否具有访问权限,如果没有,则执行以下操作
    if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
        Class<?> caller = Reflection.getCallerClass();
        if (newInstanceCallerCache != caller) {
            Reflection.ensureMemberAccess(caller, this, null, modifiers);
            newInstanceCallerCache = caller;
        }
    }
    // 整个方法的核心,使用类的构造方法来生成实例
    try {
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
}
  • newInstance的具体实现,本质上是获取的类的无参构造方法,然后执行无参构造方法来生成实例。
  • 所以说反射必须依赖类的无参构造器。否则会抛出InstantiationException

# 获取类信息

  • 这一类方法非常多,作用包括获取类的构造方法,已经声明的字段、方法,获取类或者方法的注解,获取类的包名、父类,以及判断类是否是数组、是否是枚举、是否是接口。
  • 获取类信息之前必须先生成实例,即调用froName newInstance方法。
  • 以获取方法举例:
public class ReflectDemo {

    private String reflectString = "abc";
    //因为有 *有参构造器* 所有需要显示的定义一个无参构造器用于反射生成实例
    ReflectDemo() {
    }

    ReflectDemo(String reflectString) {
        this.reflectString = reflectString;
    }
    //1.私有方法
    private void priMethod(int a) {
        System.out.println("this is [private] method");
    }
    //2.公有方法
    public void pubMethod() {
        System.out.println("this is [public] method");
    }
    //3.有参方法
    public String getMath(int a, int b) {
        BigDecimal bigDecimalA = BigDecimal.valueOf(a);
        BigDecimal bigDecimalB = BigDecimal.valueOf(b);
        BigDecimal bigDecimal = bigDecimalA.divide(bigDecimalB, 2, RoundingMode.HALF_UP);
        return String.valueOf(bigDecimal);
    }
    //4.可变参数方法
    public String addString(String... args) {
        StringBuilder sb = new StringBuilder();
        for (String arg : args) {
            sb.append(arg).append("|");
        }
        return sb.substring(0, sb.length() - 1);
    }

}

public class App {
    public static void main(String[] args) throws Exception {
        Class<?> reflectDemo = Class.forName("com.project.ReflectDemo");
        Object o1 = reflectDemo.newInstance();
        //获取所有方法
        Method[] methods = reflectDemo.getMethods();

        //1
        Method priMethod = reflectDemo.getDeclaredMethod("priMethod");
        //功能是启用或禁用安全检查
        priMethod.setAccessible(true);
        priMethod.invoke(o1);

        //2
        Method pubMethod = reflectDemo.getMethod("pubMethod");
        pubMethod.invoke(o1);

        //3
        //因为入参是int 基本数据类型,这里也写int.class
        Method pubMethod3 = reflectDemo.getMethod("getMath", int.class, int.class);
        String invoke = (String) pubMethod3.invoke(o1, 10, 3);
        System.out.println(invoke);

        //4
        //可变参数写法,可变参数本质是一个数组,外层需要包一个new Class[]{}, invoke时候包一个new Object[]{}
        //否则报错
        Method pubMethod4 = reflectDemo.getMethod("addString", new Class[]{String[].class});
        Object invoke1 = pubMethod4.invoke(o1,new Object[]{new String[]{"123", "345"}});
        System.out.println(invoke1.toString());

    }
}
  • 获取私有方法调用,需要调用setAccessible(true);方法,关闭安全检查,这时程序不会检查访问修饰符

# 类型转换

  • 类型转换方法有两个 asSubclass cast
/**
 * 将类转换为它的子类Class
 * @param clazz 父类的Class
 * @param <U> 父类
 * @return U的子类Class
 */
public <U> Class<? extends U> asSubclass(Class<U> clazz) {
    // 判断clazz是否是当前类,或者是当前类的父类
    if (clazz.isAssignableFrom(this))
        return (Class<? extends U>) this;
    else
        throw new ClassCastException(this.toString());
}

/**
 * 将给定的类转换为当前Class所代表的类
 * @param obj 需要转换的类
 * @return 当前Class所代表的类
 */
public T cast(Object obj) {
    // obj不为null,并且可以被转换为当前Class代表的类
    // isInstance为native方法,类似于instanceOf的作用
    if (obj != null && !isInstance(obj))
        throw new ClassCastException();
    return (T) obj;
}

# 总结

  • Class.forName("") 与 ClassLoader.getSystemClassLoader().loadClass("")

    • Class.forName加载类是将类进了初始化(相当于调用无参构造函数,会调用static静态代码来初始化配置)
    • ClassLoader的loadClass并没有对类进行初始化,只是把类加载到了虚拟机中
  • 反射作用是把.class文件反编译,生成实例,动态获取实例的属性方法等。

  • spring 依赖注入也是这种反射机制获取对象