到底什么是反射?
很多初学者总是很迷惑一个问题,反射到底是个啥?包括我刚开始学习java的时候,对于反射这个词很迷,那时候老师和我们讲,反射就是“照镜子”,巴巴拉拉一大堆,但还是听不懂。后来慢慢深入了解以后,才开始慢慢拨云见日。
以我的理解,反射就是从内存的层面去操作代码。首先只要能运行的一切代码,都肯定会出现在内存里,不然肯定运行不了,这是人人都懂的废话。知道了这个,然后我们再了解一下java代码的运行机制。下面我画了一个小小的流程图来帮助理解。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL0Z0ViBnVyQmb1clWv5kMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4gjM2UjNxcTM4EzMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
在这里我们很明显能发现,在计算机内存中,我们所有成员变量,成员方法,构造方法全部存在三个数组里。拿到这三个数组,不就拿到所有信息了?这就是反射。然后你只需要研究如何拿到这三个数组,和数组里的信息就OK了。
接下来我用代码,进行讲解。
反射的代码实现
首先我创建了三个类,父类Person,子类User,反射类Reflect
Person类:
/**
* @作者 zhx
* @创建日期:2020-03-18
*/
public class Person {
public String name;
public int age;
public String sex;
}
User类:
/**
* @作者 zhx
* @创建日期:2020-03-18
*/
public class User extends Person{
private String name;
private int age;
public String sex;
public void eat(String food){
System.out.println("一个人在吃"+food);
}
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 User(String name, int age) {
this.name = name;
this.age = age;
}
public User(){
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
这里我们通过Reflect类实现反射,创建User对象,获取User的属性,调用User里的方法:
共有三种实现反射的方法,我们在第一种方法中进行所有操作,第二种和第三种只做获取对象操作。
获取Class的第一种方法(最常用):已知一个类的全类名,用forName(“类的全类名”)方法获取
/**
* @作者 zhx
* @创建日期:2020-03-18
*/
public class Reflect {
//获取Class的第一种方法(最常用):已知一个类的全类名,用forName(“类的全类名”)方法获取
@Test
public void test01() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
Class c1=Class.forName("cn.kgc.zhx.User");
//这里输出一下c1的hashCode,便于和下面的对比
System.out.println("c1的hashCode:"+c1.hashCode());
//用获取到的类,通过newInstance方法,生成一个类对象,因为这里程序还不知道生成的对象是什么类型,所以生成一个Object对象
Object instance = c1.newInstance();
//将生成的对象强转成我们获取的类的类型
User user=(User)instance;
user.setAge(20);
user.setName("张三");
System.out.println("设置好姓名和年龄的user:"+user);
//获取class的父类
Class superclass = c1.getSuperclass();
String name = superclass.getName();
System.out.println("父类名:"+name);
//获取c1类,以及其父类中的所有public修饰的成员变量
Field[] fields = c1.getFields();
for (int i=0;i<fields.length;i++){
System.out.println("public修饰的成员变量:::"+fields[i]);
}
//获取c1中的sex属性
Field sex = c1.getField("sex");
//给user的sex属性赋值为“男生”
sex.set(user,"男生");
System.out.println("设置sex属性后:"+user);
//获取user中的sex属性
Object o = sex.get(user);
System.out.println("user的sex属性:"+o);
//获取c1类中的所有的成员变量,包括private修饰的
Field[] declaredFields = c1.getDeclaredFields();
for (int i=0;i<declaredFields.length;i++){
System.out.println("所有成员变量:::"+declaredFields[i]);
}
//获取一个私有成员变量
Field name1 = c1.getDeclaredField("name");
//这里如果需要操作name1,需要开启访问权限
name1.setAccessible(true);
Object o1 = name1.get(user);
//暴力反射获取user的私有成员变量name
System.out.println("user私有的成员变量name"+o1);
// 获取c1类的构造器
//获取无参构造,
Constructor constructor = c1.getConstructor();
Object o2 = constructor.newInstance();//这里简化后,就相当于 c1.newInstance();
//获取有参构造器,传入参数类型
Constructor constructor2 = c1.getConstructor(String.class,int.class);
Object o3 = constructor2.newInstance("小明", 16);
System.out.println("有参构造创建的对象:"+o3);
//获取c1类的所有public方法,方法操作参考获取成员变量,这不再一一展示,
Method[] methods = c1.getMethods();
//获取c1的eat方法 (方法名,参数类型)
Method eat = c1.getMethod("eat", String.class);
//执行user的eat方法 (具体对象,具体参数)
eat.invoke(user,"大米饭");
}
运行结果:
这里可以对比着代码看看,运行结果出自哪里。
获取Class的第二种方法:已知一个具体的类,用 类.class 方法获取
@Test
//获取Class的第二种方法:已知一个具体的类,用 类.class 方法获取
public void test02() throws IllegalAccessException, InstantiationException {
Class c2=User.class;
System.out.println("c2的hashCode:"+c2.hashCode());
User user = (User)c2.newInstance();
user.setName("李四");
user.setAge(22);
System.out.println(user);
}
运行结果:
获取Class的第三种方法:已有一个具体的实列对象,用 对象.getClass() 方法获取
@Test
//获取Class的第三种方法:已有一个具体的实列对象,用 对象.getClass() 方法获取
public void test03() throws IllegalAccessException, InstantiationException {
User user=new User();
Class c3 = user.getClass();
System.out.println("c3的hashCode:"+c3.hashCode());
User instance =(User) c3.newInstance();
instance.setAge(18);
instance.setName("王五");
System.out.println(instance);
}
运行结果:
这里我们发现,c1,c2,和c3的hashcode一样,说明无论我们创建多少个Clss对象,同一个类共用同一个Class对象
哪些类型可以有Class对象?
最后一个问题,哪些类型可以有Class对象呢?
1:class类
2:interface接口
3:[]数组
4:enum枚举
5: annotation注解
6:primitive type基本数据类型
7:void 空类型
8:Class本身(它本身也是一个类)
哈哈,到这里反射的基本应用已经写完了,自我感觉已经是保姆级的演示了,对着敲一遍基本就能理解了,希望对大家有帮助!