一、反射(reflection)的定义

Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

二、Java如何实现反射

类加载的第一阶段,即“加载”阶段,Java虚拟机需完成三件事情

1、通过一个类的全限定名来获取此类的二进制字节流

2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构

3、在内存中生成一个代表这个类的java.lang.Class对象

这个java.lang.Class对象,就是当前加载的类的各种数据的访问入口。Java的反射机制就是通过这个Class对象来实现。

三、Class对象的获取方式

private static native void registerNatives();
static {
    registerNatives();
}
/*
 * Private constructor. Only the Java Virtual Machine creates Class objects.
 * This constructor is not used and prevents the default constructor being
 * generated.
 */
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类的构造函数是Java虚拟机来调用的。也就是在类加载的时候生成类的Class对象。

在JDK7及之前,Class对象存储在永久代中。JDK8及之后,类变量和Class对象都存放在Java堆中。

获取Class对象的方式有三种

1.通过类名.class获取

2.实例对象调用getClass()方法获取

3.调用Class类静态方法forName()得到

关于Java反射机制_java关于Java反射机制_类加载器_02
 1 class A{
 2 
 3 }
 4 
 5 public class Test {
 6     public static void main(String[] args) throws ClassNotFoundException {
 7         Class t1=A.class;
 8         Class t2=new A().getClass();
 9         Class t3=Class.forName("com.reflection.A");
10         System.out.println(t1);
11         System.out.println(t2);
12         System.out.println(t3);
13         System.out.println(t1==t2);
14         System.out.println(t1==t3);
15         System.out.println(t1==t3);
16     }
17 }
View Code

第一种方式:类似于访问类变量

第二种方式:getClass()是一个Object类中的native方法。主要通过对象头中存储的类型指针来找到当前对象的类,接着找到该类的Class对象。

public final native Class<?> getClass();

 第三种方式:forName()是Class类的静态方法,可以用于加载指定的类,也会返回Class对象有两种重载,最后都是去调用forNmae0()。

三个参数分别是

  • className 当前类的全限定名
  • initialize 若此类没进行过类初始化是否要进行类初始化(默认为true)
  • loader 用什么类来进行类加载
@CallerSensitive
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);
}

这个地方如果没有指定类加载器,就会使用加载caller的类加载器进行加载

那么caller类又是什么呢

Class<?> caller = Reflection.getCallerClass();

getCallerClass()方法是Reflection类中的静态本地方法

getCallerClass()方法调用所在的方法必须用@CallerSensitive进行注解,通过此方法获取class时会跳过链路上所有的有@CallerSensitive注解的方法的类,直到遇到第一个未使用该注解的类

@CallerSensitive注解是JVM提供的,只有被启动类加载器和扩展类加载器加载的类的方法才能使用此注解。

最后调用的forName0也是一个native方法,通过指定的类加载器去加载指定的类,返回class对象

四、反射操作属性,方法,构造器

先写一个类A

关于Java反射机制_java关于Java反射机制_类加载器_02
 1 class A{
 2     private String a;
 3     public String b;
 4     public int c;
 5 
 6     public A() {
 7     }
 8 
 9     public A(String a) {
10         this.a = a;
11     }
12 
13     public String getA() {
14         return a;
15     }
16 
17     public void setA(String a) {
18         this.a = a;
19     }
20 
21     @Override
22     public String toString() {
23         return "A{" +
24                 "a='" + a + '\'' +
25                 '}';
26     }
27 }
View Code

getMethods()和getDeclaredMethods()都是Class类中的成员方法,看名字就可以知道是获取一个方法集合

Method[] methods = clazz.getMethods();
for (Method method1 : methods) {
    System.out.print(method1.getName()+' ');
}
System.out.println();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.print(declaredMethod.getName()+' ');
}

输出结果

toString setA getA wait wait wait equals hashCode getClass notify notifyAll 
toString setA getA 

从运行结果上就能看出他们的区别

getMethods()用于获取类中所有(包括继承的)public访问权限的方法

getDeclaredMethods()用于获取当前类声明的(不包括继承的)任何访问权限的方法

那么类似的getFields(),getDeclaredFields(),getConstructors(),getDeclaredConstructors()都很好理解了

反射操作实例

Class clazz=A.class;
//构造器
Constructor constructor = clazz.getConstructor();//获取默认午餐构造器
Object o = constructor.newInstance();//newInstance创建对象实例
//字段
Field a = clazz.getDeclaredField("a");//通过属性名获取属性,这里获取的a是private修饰,所以不能用getField获取,会报NoSuchFieldException
a.setAccessible(true);//修改访问权限,由于属性a为私有,直接修改会报IllegalAccessException
a.set(o,"200");//setAccessible(true)之后就可以修改值了
//方法
Method method = clazz.getMethod("getA");//通过方法名获取方法
System.out.println(method.invoke(o));//调用方法