“在反射之下,一段Java程序也变得无所遁形。探索框架的精髓——反射”
什么是反射
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
之前学习Java基础时候没注意到反射,一直到最近在研究 Spring动态代理和 IOC底层的时候才发现,原来有这么一个强大的机制都被我给忘了。
先来了解一下Java程序的三个阶段:
这三个阶段分别是:源代码阶段,Class类对象阶段,程序运行阶段。
- 第一阶段发生在磁盘内:javac 将 Java 代码编译成字节码(.class),类的属性成员们,构造方法们,成员方法们分别被管理到三个区域。
- 第二阶段发生在JVM内存中,这里并非堆内存:由ClassLoader类加载器将字节码加载到Class类对象中,三个区域仍被分区管辖,反射的一些操作在这一阶段引入。
- 第三阶段:对象在运行时被调用运行。
到这里就可以联想到IDE编写程序时的一些快捷操作了,实际上就是反射机制下,在JVM内存里找到对象对应的属性或方法们,然后放到一个表格里供快速选择。
Java反射中的一些操作
Java中的反射相关类封装在 import java.lang.reflect 包中。常用的有操作Class对象、成员属性、构造方法、成员方法。
获取Class对象有三种实现方式,在一次运行中同一字节码文件只会被加载一次,所以这三种方式获取到的时同一个对象,用“==”比较结果毫无疑问时 true
@Test //测试获取class对象
public void testClass() throws Exception {
//获取Class对象的方式一
Class<?> clazz1 = Class.forName("com.duebass.reflect.Person");
System.out.println(clazz1);
//方式二
Class<Person> clazz2 = Person.class;
System.out.println(clazz2);
//方式三
Person person = new Person();
Class<? extends Person> clazz3 = person.getClass();
System.out.println(clazz3);
//在一次运行中同一字节码文件只会被加载一次,结果毫无疑问是 true
System.out.println(clazz1==clazz2);
System.out.println(clazz1==clazz3);
}
在获取成员属性、构造器方法、成员方法时参考命名规范可以发现——
get***(参数): 是获取某一成员,参数时成员名,如果是构造器参数则是构造器的传参类型.class(例如String.class)
get***s(参数):是获取某一全部成员,这个和上面那个都找不到原类中的私有成员。
getDeclared***():这种方式对应着get***() 方法,不过这种情况下可以让返回对象调用 setAccessible(true) 方法设置成暴力反射,即便是私有属性也能访问。
getDeclared***s():带s,毫无疑问啊获取全部某一成员,这种和上面那位一样,可以设置暴力反射,如果不设置暴力反射而贸然访问私有属性运行时会被一个 java.lang.IllegalAccessException 异常中断程序。
获取成员属性:返回 Field 对象 有一个get() 方法传入一个真实对象可获取一个Object对象,同时也可以调用set() 方法传入一个有该属性的对象和一个值间接给对象赋值。
@Test //测试获取属性
public void testField() throws Exception {
Class<?> clazz = Class.forName("com.duebass.reflect.Person");
// Field name = clazz.getField("name");//贸然访问私有属性会抛出异常
Field name = clazz.getDeclaredField("name");
name.setAccessible(true); //暴力反射开关
Field[] fields = clazz.getFields();
Field[] declaredFields = clazz.getDeclaredFields();
Person person = new Person();
Object result = name.get(person);
System.out.println(result);
name.set(person,"yang"); //给Person赋值
System.out.println(person);
}
获取构造方法: 构造方法返回的一个Constructor 对象 有一个 newInstance() 方法可用于创建对象,传参时有参构造,不传参无参构造,要保证原类有对应方法。
@Test //测试获取方法
public void testConstructor() throws Exception {
Class<?> clazz = Class.forName("com.duebass.reflect.Person");
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object result = constructor.newInstance("yang", 20);
System.out.println(result);
}
获取成员方法:成员方法返回的一个Method 对象 有一个invoke() 方法可以用于调用类成员方法,真实对象当作参数传入就可以调用,这个传入得对象也可以是继承而来。
@Test
public void testMethod() throws Exception {
Class<Person> clazz = Person.class;
Method method = clazz.getMethod("speak");
test tes = new test(); //test是继承Person得一个类
method.invoke(tes);
}
研究到这里不由得联想到学习Spring框架时的动态代理的底层实现,哦呦可以,有内味了