天天看点

深入理解Java反射机制

到底什么是反射?

很多初学者总是很迷惑一个问题,反射到底是个啥?包括我刚开始学习java的时候,对于反射这个词很迷,那时候老师和我们讲,反射就是“照镜子”,巴巴拉拉一大堆,但还是听不懂。后来慢慢深入了解以后,才开始慢慢拨云见日。

以我的理解,反射就是从内存的层面去操作代码。首先只要能运行的一切代码,都肯定会出现在内存里,不然肯定运行不了,这是人人都懂的废话。知道了这个,然后我们再了解一下java代码的运行机制。下面我画了一个小小的流程图来帮助理解。

深入理解Java反射机制

在这里我们很明显能发现,在计算机内存中,我们所有成员变量,成员方法,构造方法全部存在三个数组里。拿到这三个数组,不就拿到所有信息了?这就是反射。然后你只需要研究如何拿到这三个数组,和数组里的信息就OK了。

接下来我用代码,进行讲解。

反射的代码实现

首先我创建了三个类,父类Person,子类User,反射类Reflect

深入理解Java反射机制

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,"大米饭");
    }

           

运行结果:

深入理解Java反射机制

这里可以对比着代码看看,运行结果出自哪里。

获取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);
    }
           

运行结果:

深入理解Java反射机制

获取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);
    }
           

运行结果:

深入理解Java反射机制

这里我们发现,c1,c2,和c3的hashcode一样,说明无论我们创建多少个Clss对象,同一个类共用同一个Class对象

哪些类型可以有Class对象?

最后一个问题,哪些类型可以有Class对象呢?
 1:class类 
 2:interface接口 
 3:[]数组  
 4:enum枚举 
 5: annotation注解 
 6:primitive type基本数据类型 
 7:void 空类型 
 8:Class本身(它本身也是一个类)
           

哈哈,到这里反射的基本应用已经写完了,自我感觉已经是保姆级的演示了,对着敲一遍基本就能理解了,希望对大家有帮助!