目录
1、反射概念:
将类的各个组成部分封装为其他对象(Class对象),这就是反射机制
要先看懂概念,先搞懂Java代码的加载过程:
2、Java代码在计算机中经历的三个阶段:
- Source源代码阶段: 编译过程:java代码编译为class文件,还没有进入内存,而是存储在硬盘上
- Class类对象阶段: 类加载过程:使用类加载器将字节码文件加载进内存 ,在内存中会生成一个对象(Class对象),这个对象用来描述被加载的这个字节码文件:
- 类属性:类名name、包名、继承的父类、实现的接口
- 成员变量:Field[] fields
- 构造方法:Constructor[] constructors
- 成员方法:Method[] methods
3.Runtime运行时阶段:
总结:反射机制将类的各个组成部分封装为其他对象:即将类的成员变量封装为Field[ ]对象,将类的构造方法封装为Constructor[ ]对象,将成员方法封装为Method[ ]对象,这些对象合起来就是一个Class对象,唯一的确定一个类的信息。
反射的一个应用实例:
在Eclipse中,当你用到某种对象的某个方法时,你不用全部敲出这个方法的全称,只要按下alt+/即可显示出这个类下边的所有字段,这其实就是利用反射实现的。eclipse在创建类对象时,已将这个类加载进了内存,并且在内存中有一个这个类对应的Class类对象,Class对象将这个类所有的方法都抽取放在了Method[ ]数组中了,所以在alt+/时,只需要将Method[ ]中的数据显示出来。
发射的好处:
1、在程序的运行过程中方便地操作对象
2、可以解耦,降低耦合度,提高程序的可扩展性
3、很多开发框架都是基于反射实现的
3、反射API的操作:
想要获取并操作封装的对象,就需要获取字节码Class对象,那么如何获取Class对象?
对应Java代码的三个阶段,有三种不同的获取Class对象的方式:
1.Source源代码阶段:这个阶段,字节码文件还存储在硬盘中,因此需要手动将字节码文件加载到内存中,然后生成对应的Class对象。
Class.forName("全类名");将字节码文件加载到内存中,返回的是Class对象
这种方式多用于配置文件,可以将类名写在配置文件中,读取文件后加载类
2、Class类对象阶段:如果已经将字节码文件加载进了内存(Class对象已经存在时),直接通过类名就可以直接获取。
类名.class
这种方式一般用于参数的传递
3、Runtime运行时阶段:已经有对象了
直接通过对象继承自Object的getClass()方法就可以获取Class对象
多用于对象的获取字节码
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
//1、Source阶段进行发射
Class clazz1 = Class.forName("java.lang.String");
System.out.println(clazz1);
//2、Class类对象阶段进行反射
Class clazz2 = String.class;
System.out.println(clazz2);
//3、直接通过类名,利用Object类的getClass方法进行发射
Integer i = 0;
Class clazz3 = i.getClass();
System.out.println(clazz3);
}
}
运行结果:
注意:对于同一个字节码文件,在一次程序运行过程中,只会被加载一次,因此也只能创建一个Class文件,因此这三种方式加载的同一个字节码文件的Class文件必然是同一个Class对象。
4、使用Class对象:
Class对象中包含了类的所有信息,因此我们可以通过操作Class对象来获得或者修改这些信息
- 类属性:类名name、包名、继承的父类、实现的接口
- 成员变量:Field[] fields
- 构造方法:Constructor[] constructors
- 成员方法:Method[] methods
获取功能:
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
2. 获取构造方法们
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
3. 获取成员方法们:
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
4. 获取全类名
* String getName()
修改信息:
Field:成员变量
* 操作:
1. 设置值
* void set(Object obj, Object value) , 指定一个该类的一个实例对象
2. 获取值
* get(Object obj) 获得一个该类的实例对象的参数值
3. 忽略访问权限修饰符的安全检查
* setAccessible(true):暴力反射
Constructor:构造方法
* 创建对象:
* T newInstance(Object... initargs)
* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法
Method:方法对象
* 执行方法:
* Object invoke(Object obj, Object... args)
* 获取方法名称:
* String getName:获取方法名
实践:
1、首先创建Person类
public class Person {
private String name;
private Integer age;
public String a;
protected String b;
String c;
private String d;
public Person() {
}
public Person(String name, Integer age, String a, String b, String c, String d) {
this.name = name;
this.age = age;
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", a=" + a + ", b=" + b + ", c=" + c + ", d=" + d + "]";
}
}
使用getFields()获得成员变量:
import java.lang.reflect.Field;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Class personClass = Person.class;
java.lang.reflect.Field[] fields = personClass.getFields();
for(Field field : fields) {
System.out.println(field);
}
}
}
运行结果发现,只能获得Person类中的public型的成员变量a,而不能获得其他权限的成员变量
对于Field变量,
可以使用Field提供的方法实现:1、设置值,set( ); 2、 获取值,get( );3、忽略访问权限修饰符的安全检查 setAccessible(true)
import java.lang.reflect.Field;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
//获取Person类的Class对象
Class personClass = Person.class;
//1、获取成员变量
Field a = personClass.getField("a");
//创建一个Person对象,用成员对象来操作这个Person对象对应的属性的值
Person person = new Person();
//获取Person对象person的成员变量a的值
Object o = a.get(person);
System.out.println(o);
//修改a的值
a.set(person, "章第三");
System.out.println(person);
}
}
输出结果:使用get获得Person对象实例成员变量a原值是null,使用set将其设置为“章第三”
使用getDeclaredFields()获得Person类的所有成员变量
import java.lang.reflect.Field;
public class ReflectDemo2 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
//获取Person类的Class对象
Class personClass = Person.class;
//使用getDeclaredFields()获得所有的成员变量
Field[] declaredFields = personClass.getDeclaredFields();
for(Field field : declaredFields) {
System.out.println(field);
}
}
}
结果:可以将Person类中的各种访问权限的成员变量都获取到
对于是private的成员变量,如果直接操作会报错,这个时候可以设置其忽略访问权限setAccessible(true)
import java.lang.reflect.Field;
public class ReflectDemo3 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
//获取Person类的Class对象
Class personClass = Person.class;
//尝试直接获得Person类的private成员变量name
Field d = personClass.getDeclaredField("name");
Person person = new Person();
Object o = d.get(person);
System.out.println(o);
}
}
系统抛出异常:
修改设置即可修复:
对于Method和Constractor的使用方式与之类似,不同的是需要传入方法参数,这里就不再赘述了。