- 反射允许程序在运行期间根据反射API获取任意类的内部信息,及操作类的属性和方法;
-
Java
类在加载完成之后,在堆内存的方法区会产生一个Class
对象,该对象包含类的完整信息; - 一个类只有一个
Class
对象;
Java反射提供的功能
- 运行时判断一个对象所属的类;
- 运行时创建任何类的对象;
- 运行时获取任意类的成员变量和方法;
- 运行时获取泛型信息;
- 运行时处理注解;
- 生成动态代理;
相关主要API
-
java.lang.Class
:类对象; -
java.lang.reflect.Method
:类的方法 -
java.lang.reflect.Field
:类的成员变量; -
java.lang.reflect.Constructor
:类的构造器;
简单使用
- 创建一个
Person
类; - 提供空参和带参构造方法;
- 属性一个为
private
,一个为public
;
public class Person {
private String name;
public Integer age;
public void sayHi(){
System.out.println("SayHi...");
}
*测试代码
@Test
public void test() throws Exception {
// 获取Class对象
Class clazz = Person.class;
// 获取带参构造器
Constructor constructor = clazz.getConstructor(String.class, Integer.class);
// 使用构造器创建对象
Person lc_666 = (Person) constructor.newInstance("lc_666", 20);
System.out.println(lc_666);//Person{name='lc_666', age=20}
// 通过反射获取指定 属性
Field age = clazz.getDeclaredField("age");
age.set(lc_666, 28);
System.out.println(lc_666);//Person{name='lc_666', age=28}
// 通过反射获取指定 方法
Method sayHi = clazz.getDeclaredMethod("sayHi");
sayHi.invoke(lc_666);//SayHi...
// 通过反射调用类的私有结构
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(lc_666, "大顺");
System.out.println(lc_666);//Person{name='大顺', age=28}
}
java.lang.Class
- 加载到内存中的
XXX.class
称为运行时类,此运行时类,就是一个Class
实例; - 所有数据类型都有
Class
实例,包含基本数据类型、数组、void
等; - 获取
Class
实例的方式:
@Test
public void test1() throws ClassNotFoundException {
// 方式1:调用运行时类属性
Class personClass = Person.class;
// 方式2:通过运行时类的对象
Person person = new Person();
Class personClass1 = person.getClass();
//方式3:调用Class的静态方法
Class<?> personClass2 = Class.forName("com.llds.entities.Person");
//方式4:使用类的加载器
ClassLoader classLoader = ReflectTest.class.getClassLoader();
Class<?> personClass3 = classLoader.loadClass("com.llds.entities.Person");
System.out.println(personClass == personClass1);//true
System.out.println(personClass == personClass2);//true
System.out.println(personClass == personClass3);//true
}
类的加载过程:
- 将类的
class
文件读取到内存,并创建一个Class
对象; - 类的链接:将类的二进制数据合并到JRE中;
- 验证:验证加载的类是否符合JVM规范;
- 准备:为
static
变量分配内存,设置初始值(方法区中); - 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程;
- 类初始化;
- 执行类构造器
<clinit>()
方法; - 如果有父类,需要先初始化父类;
- 虚拟机会保证一个类的
<clinit>()
方法在多线程环境中正确加锁和同步;
- 执行类构造器
ClassLoader
-
作用:将
.class
文件内容加载到内存当中,将这些静态数据转换称为方法区的运行时数据结构;然后在堆中生成一个java.lang.Class
对象,作为方法区中类数据的访问入口; - 类缓存:一旦某个类被加载到类加载器中,将缓存一段时间,但是可能会被垃圾回收;
- 类加载器类型:
-
BootStrap ClassLoader
:引导类加载器,负责加载Java核心类库如(String
),无法直接获取; -
Extension ClassLoader
:扩展类加载器,jre/lib/ext
下的jar包; -
System ClassLoader
:系统类加载器,常用加载器; - 自定义类加载器;
-
@Test
public void test3(){
// 系统类加载器
ClassLoader classLoader = Person.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
// 获取扩展类加载器
System.out.println(classLoader.getParent());//sun.misc.Launcher$ExtClassLoader@568db2f2
// 无法获取引导类加载器
System.out.println(classLoader.getParent().getParent());//null
}
网友评论