java_反射
什么是反射
概念
- 放射:将类的各个部分封装为其他对象,这就是反射机制。
java代码在内存中经历的三个阶段
1、 Source 源代码阶段
- 执行javac编译命令从.java文件到.class文件的过程都是在源代码阶段,.class字节码文件会将类分为多个部分,其中分为成员变量部分,成员方法部分,构造方法部分等。
2、 Class 类对象阶段
- 通过ClassLoader(类加载器)将字节码文件加载到内存中。
- 通过Class类对象来描述进入内存中的字节码文件的特征和行为。将成员变量、成员方法、构造方法等封装成单独的对象放入Class类对象中。
- 最后我们可以通过Class对象的一些行为创建具体的某个对象。
3、 runtime 运行时阶段
- new 类();
反射的好处
- 在程序的运行期间操作这些对象。
- 降低程序的耦合性,提高程序的可扩展性。
获取Class类对象的方法
- 获取class类对象的方式有三种,分别对应的java代码经历的三个阶段
1、 Class.forName("全类名");
- 将字节码文件加载进内存,返回class对象
- 多用于配置文件,将类名定义在配置文件中
2、 类名.class;
- 通过类名的属性class获取
- 多用于参数传递
3、 对象.getClass();
-
getClass()方法是定义在Object类中的
-
多用于对象获取字节码
-
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次。
package top.uaoie.day03;
import top.uaoie.domain.Person;
public class ReflectDome01 {
public static void main(String[] args) throws Exception {
//1. Class.forName("全类名");
Class clazz1 = Class.forName("top.uaoie.domain.Person");
//2. 类名.class;
Class clazz2 = Person.class;
//3. 对象.getClass();
Person p = new Person();
Class clazz3 = p.getClass();
System.out.println(clazz1);
System.out.println(clazz2);
System.out.println(clazz3);
//比较三个对象
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
}
}
反射的应用
1、 获取所有的成员变量
- Field[] getFields()
- Field getFied(String name)
- Field[] getDeclaredFields()
- Field getDeclaredFied(String name)
2、 获取所有的构造方法
- Constructor<?>[] getConstructors()
- Constructor
getConstructors(类<?>... parameterTypes) - Constructor<?>[] getDeclaredConstructors()
- Constructor
getDeclaredConstructors(类<?>... parameterTypes)
3、 获取所有的成员方法
- Method[] getMethods()
- Menthod getMethod(String name,类<?>...parameterTypes)
- Method[] getDeclaredMethods()
- Menthod getDeclaredMethod(String name,类<?>...parameterTypes)
4、 获取类名
- String getName()
以下为演示通过反射获取所有的成员变量
- 创建一个名为Person的类,里面的代码如下:
package top.uaoie.domain;
public class Person {
public String a;
protected String b;
String c;
private String d;
@Override
public String toString() {
return "Person{" +
"a=\'" + a + \'\\\'\' +
", b=\'" + b + \'\\\'\' +
", c=\'" + c + \'\\\'\' +
", d=\'" + d + \'\\\'\' +
\'}\';
}
}
- 再新建一个名为ReflectDome02的类,里面的代码如下:
package top.uaoie.day03;
import top.uaoie.domain.Person;
import java.lang.reflect.Field;
public class ReflectDome02 {
public static void main(String[] args) throws Exception {
//获取Person的Class对象,此处是在阶段二部分获取的
//也可在第一阶段获取,如:Class.forName("top.uaoie.domain.Person");
Class p = Person.class;
//获取成员变量
Field[] fields = p.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("---------------------");
Field a = p.getField("a");
System.out.println(a);
//创建一个对象
Person person = new Person();
//获取成员变量a的值
Object value = a.get(person);
System.out.println(value);
//设置成员变量a的值
a.set(person, "这是a的值");
System.out.println(person);
System.out.println("=================");
Field[] declaredFields = p.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
Field d = p.getDeclaredField("d");
//忽略访问权限修饰符
d.setAccessible(true);//暴力反射
Object value2 = d.get(person);
System.out.println(value2);
}
}
小结
- 获取类的class对象有多种方法,其中使用Class.forName可以通过读取配置文件获取全类名
- Class.forName和getField等类是代码会报错是因为传入的是字符串,所有可能不存在这个类获或者方法,所以需要处理异常
- 通过反射可以直接访问到任何成员变量名,不受访问修饰符的限制
- 可以setAccessible设置为true来暴力反射获取或者更改成员变量的值