目录
定义
java的反射进制是在运行过程中对于任意类,我们都能知道它的所有属性和方法,对于任意一个对象,我们都能调用它的所有属性和方法,这样动态的修改信息和调用信息的操作称为反射。
为什么要用到反射
下面我们通过Oracle官方文档的一些话来回答这个问题:
- 反射让开发人员可以通过外部类的全路径名创建对象,并使用这些类,实现一些扩展的功能
- 反射让开发人员可以枚举出类的全部成员,包括构造函数、属性、方法。以帮助开发者写出正确的代码
- 测试时可以利用反射 API 访问类的私有成员,以保证测试代码覆盖率
反射的使用
总览图
这是我们要用到的Person类
public class Person {
public String name;
public int age;
private int money;
public Person(String name, int age, int money) {
this.name = name;
this.age = age;
this.money = money;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", money=" + money +
'}';
}
}
Class类
什么是Class类
-
java文件被编译后,会生成.class字节码文件,而字节码文件又会被加载到JVM中,之后JVM会将要使用的类解析为一个对象,这个对象就是Class类的对象,它存储在JVM的堆中。在反射机制中,我们就可以通过这个Class对象创建类的实例,获取类的属性、修改类的属性、调用类的方法等等。
-
对于每一种类,JVM都会创建一个Class类的实例,每当我们编写并且创建一个新类时,都会创建一个Class对象,并且这个Class对象会保存在.class文件里
-
无论创建多少个类(比如下文里的Person类)的实例,在JVM里都只对应于一个Class对象
-
Class是反射的基础,要想获得类(比如下文的Person类)的实例,都必须创建一个Class对象获取才可以
Class类相关方法
方法 | 说明 |
---|---|
forName(String className) | 根据类名返回类的对象 |
newInstance() | 创建类的实例 |
getName() | 获得类的完整路径名字 |
获取Class类的对象的三种方式
-
通过调用Class的静态方法forName(“全路径名”)使用(这种方法需要抛出异常,因为路径名可能写错)
只需要知道全路径名就可以知道,这种方法如果类还没有加载就会进行加载,如果类已经加载就会返回Class对象
Class cls1 = Class.forName("Java高级特性.反射.Person");
-
通过调用Person类的class属性创建Class对象
这种方法比较适合于此类已经知道的情况
Class cls2 = Person.class;
-
通过调用Person类对象的getClass()方法创建
需要创建Person类对象才可以使用
Person person = new Person("1", 1, 1); Class cls3 = person.getClass();
public static void main(String[] args) throws Exception {
//1. 调用Class类的forName("全路径名")方法创建
Class cls1 = Class.forName("Java高级特性.反射.Person");
//2. 调用Person类的class属性创建Class对象
Class cls2 = Person.class;
//3. 调用Person类的对象的getClass()方法进行创建(这种方法需要一个Person类的对象)
Person person = new Person("1", 1, 1);
Class cls3 = person.getClass();
//比较三种创建方法是否相等
System.out.println(cls1 == cls2); //true
System.out.println(cls1 == cls3); //true
/*
从上面我们可以看出三种创建方式是等价的,Person类在JVM中只会加载一次
*/
}
Field类
对成员变量进行操作
Field类相关方法
方法 | 说明 |
---|---|
Field getFiled(String name) | 获得某个公有的属性对象 |
Field[] getFileds() | 获得所有公有的属性对象 |
Field getDeclaredFiled(String name) | 获得某个属性对象 |
Field[] getDeclaredFileds() | 获得所有属性对象 |
void set(Object obj, Object value) | 设置对象的对应的成员变量的值 |
Object get(Object obj) | 获取对象的对应的成员变量的值 |
void setAccessible(boolean flag) | 当访问私有成员变量是需要设置为true,暴力访问(该方法需要抛出异常) |
package Java高级特性.反射;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Class cls = Person.class;
//访问成员变量们
/*
Class类对象.getFields():获取以public修饰的成员变量
Class类对象.getDeclaredFields():获取全部的成员变量
*/
Field[] fields = cls.getFields();
System.out.println("公有成员变量:");
for (Field field : fields) {
System.out.println(field);
}
System.out.println("全部的成员变量:");
fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
Person person = new Person("张三", 18, 100);
//获取特定的成员变量
Field field = cls.getField("name");
System.out.println(field);
System.out.println(field.getName());
System.out.println(field.get(person));
//修改特定的成员变量
field.set(person, "李四");
System.out.println(field.get(person));
//访问private修饰的成员变量
field = cls.getDeclaredField("money");
field.setAccessible(true);
System.out.println(field.get(person));
field.set(person, 200);
System.out.println(field.get(person));
}
}
Constructor类
对构造方法进行操作
Constructor类相关方法
方法 | 说明 |
---|---|
getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructor() | 获得该类型所有的构造方法 |
T newInstance(Object ... initargs) | 通过Constructor对象创建Person对象,需要传Person对象和方法的参数 |
package Java高级特性.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
Class cls = Person.class;
//获取以public修饰的构造方法
Constructor[] constructors = cls.getConstructors();
System.out.println("以public修饰的构造方法:");
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("以private修饰的构造方法:");
constructors = cls.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
Constructor constructor = cls.getConstructor(String.class, int.class, int.class);
//使用构造器创建对象(可以使用所有的构造器)
Person person = (Person)constructor.newInstance("张三", 18, 100);
System.out.println(person.getName());
//使用Class对象创建对象(只能使用无参的构造方法)
person = (Person)cls.newInstance();
System.out.println(person.getName());
}
}
Method类
对成员方法进行操作
Method类相关方法
方法 | 说明 |
---|---|
getMethod(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有方法 |
getMethods() | 获得该类的所有公有方法 |
getDeclaredMethod(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的方法 |
getDeclaredMethods() | 获得该类型所有的方法 |
Object invoke(Object obj, Object... args) | 调用Person对象的方法,需要传Person对象和方法的参数 |
package Java高级特性.反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
Class cls = Person.class;
//获取以public修饰的成员方法(包含父类的)
System.out.println("以public修饰的成员方法:");
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//获取所有的成员方法(不包含父类的)
methods = cls.getDeclaredMethods();
System.out.println("所有的成员方法:");
for (Method method : methods) {
System.out.println(method);
}
System.out.println("调用方法:");
Person person = new Person("张三", 18, 100);
Method method = cls.getMethod("getName");
System.out.println(method);
System.out.println(method.invoke(person));
}
}
创建Person类的对象
通过Class对象创建
Class cls = Class.forName("Java高级特性.反射.Person");
Person person = (Person) cls.newInstance(); //必须强转,因为Class类.newInstance()方法返回的类型是Object类型
通过Constructor对象创建
Constructor constructor = cls.getConstructor(String.class, int.class, int.class);
person = (Person) constructor.newInstance("1", 1, 1);//强转,因为Constructor类.newInstance()方法返回的类型是Object类型
反射的优点和缺点
优点:
- 对于任意一个类,能够知道该类的所有属性和方法;对于任意一个对象,能够修改对象的所有属性和调用对象的所有方法和属性
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
缺点:
- 反射技术绕过源代码,会带来维护的问题
- 反射代码相比于直接的代码更为复杂
文章学习与:
https://blog.csdn.net/weixin_51367845/article/details/122014096
https://www.zhihu.com/question/377483107
https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
https://www.jianshu.com/p/10c29883eac1