反射(Reflection)
① java反射机制的概述
-
Reflection (反射) : 是被视为 动态语言 的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
-
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。
这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为 : 反射。
Java反射机制提供的功能 : -
在运行时判断任意一个对象所属的类
-
在运行时构造任意一个类的对象
-
在运行时判断任意一个类所具有的成员变量和方法
-
在运行时获取泛型信息
-
在运行时调用任意一个对象的成员变量和方法
-
在运行时处理注解
-
生成动态代理
反射相关的API :
- java.lang.Class : 代表一个类
- java.lang.reflect.Methon : 代表类的方法
- java.lang.reflect.Field : 代表类的成员变量
- java.lang.reflect.Constructor : 代表类的成员变量
- …
② 关于 java.lang.Class类的理解
1.1 类的加载过程
程序经过 javac.exe 命令以后,会生成一个或多个字节码文件 ( .class 结尾 ), 接着我们使用 java.exe 命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为 类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
换句话说,Class 的实例就对应着一个 运行时类。
加载到内存中的运行时类,会缓存一段时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
1.2 如何获取Class的实例
public class ReflectionTest {
@Test
public void test1() throws ClassNotFoundException{
//方式一: 调用运行时类的属性 .class
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);
//方式二: 通过运行时类的对象, 调用getClass()
Person person = new Person();
Class clazz2 = person.getClass();
System.out.println(clazz2);
//方式三: 调用Class的静态方法,forName(String classPath)
Class clazz3 = Class.forName("com.cs.mall.Person");
System.out.println(clazz3);
//方式四: 使用类的加载器: ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.cs.mall.Person");
System.out.println(class4);
}
}
1.3 类的加载过程
-
加载 : 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
-
链接 : 将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
-
初始化:
- 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
③ 通过反射,创建运行时类的对象
public class NewInstanceTest {
@Test
public void test2(){
int number = new Random().nextInt(3);//0,1,2
String classPath = "";
switch (number) {
case 0 :
classPath = "java.util.Date";
break;
case 1 :
classPath = "java.lang.Object";
break;
case 2:
classPath = "com.cs.mall.Person";
break;
}
Object obj = null;
try {
obj = getInstance(classPath);
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建一个指定类的对象
* @param classPath 指定类的全类名
*/
private Object getInstance(String classPath) throws Exception{
Class clazz = Class.forName(classPath);
return clazz.newInstance();
}
}
④ 获取属性结构
-
**getFields(): **
获取当前运行时类及其父类中声明为public访问权限的 属性
-
getDeclaredFields() :
获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)