反射学习笔记

本文详细介绍了Java中的类加载流程、JVM内存结构以及反射机制。通过实例展示了如何在运行时动态加载类、创建对象,以及如何访问私有属性和方法。此外,还探讨了反射在破坏单例模式中的应用,并提供了多个测试用例来演示反射的常见操作,如通过Class对象创建实例、获取和修改字段值等。
摘要由CSDN通过智能技术生成

JVM类加载流程和内存结构

类编译器将java源文件编译成class文件,类加载器ClassLoader将class文件加载到jvm

反射类图
在这里插入图片描述
生产对象步骤

// 编译期加载Person.class文件->查找Person()构造函数->通过构造函数创建对象
Person person = new Person();

// 运行期加载Person.class文件->创建构造函数Person()->通过构造函数创建对象
Class personClazz = Class.forName("com.tqq.Person");
// 初始化无参构造方法
Constructor constructor = personClazz.getConstructor();
Person person = (Person) constructor.newInstance();

// 初始化有参构造方法对象
Constructor constructor2 = personClazz.getConstructor(String.class, Integer.class, Byte.class, Boolean.class); 
// 调用有参数构造方法创建Person对象
Person person2 = (Person) constructor2.newInstance("muse2", 10, (byte) 1, true);

创建Class对象的3种方式

// 方式一  类.class
Class personClazz = Person.class;

// 方式二  实例.getClass()
Person person = new Person();
Class personClazz1 = person.getClass();

// 方式三  Class.forName("类的全路径")
Class personClazz2 = Class.forName("com.tqq.reflect.Person");

Class文件包含的内容
类的属性、类的方法、类的字段、实现的接口、父类
魔数、小版本号、大版本号、常量池、访问标记、当前类
在这里插入图片描述
反射的应用
BeanUtils工具可以将一个对象属性相同的值赋值给另一个对象。

public static void convertor(Object originObj, Object targetObj) throws Throwable {
    // 第一步,获得class对象
    Class orginClazz = originObj.getClass();
    Class targetClazz = targetObj.getClass();

    // 第二步,获得Field
    Field[] orginFields = orginClazz.getDeclaredFields();
    Field[] targetFields = targetClazz.getDeclaredFields();

    // 第三步:赋值
    for (Field originField : orginFields) {
        for (Field targetField : targetFields) {
            if (originField.getName().equals(targetField.getName())) {
                originField.setAccessible(true);
                targetField.setAccessible(true);
                targetField.set(targetObj, originField.get(originObj));
            }
        }
    }
}

反射通过私有构造方法创建对象,破坏单例模式

Class singletonPersonClazz = SingletonPerson.class;
// Constructor constructor3 = singletonPersonClazz.getConstructor();
Constructor constructor3 = singletonPersonClazz.getDeclaredConstructor();
constructor3.setAccessible(true);
SingletonPerson singletonPerson = (SingletonPerson) constructor3.newInstance();
SingletonPerson singletonPerson1 = SingletonPerson.getInstance();
SingletonPerson singletonPerson2 = SingletonPerson.getInstance();
System.out.println("singletonPerson==singletonPerson1 is " + (singletonPerson==singletonPerson1));
System.out.println("singletonPerson==singletonPerson2 is " + (singletonPerson==singletonPerson2));
System.out.println("singletonPerson1==singletonPerson2 is " + (singletonPerson1==singletonPerson2));

反射的使用

public class ReflectionTest {

    /**
     * 示例:创建Class对象的3种方式
     */
    @Test
    public void test() throws Throwable{
        // 方式一  类.class
        Class personClazz = Person.class;

        // 方式二  实例.getClass()
        Person person = new Person();
        Class personClazz1 = person.getClass();

        // 方式三  Class.forName("类的全路径")
        Class personClazz2 = Class.forName("com.muse.reflect.Person");
        
        System.out.println(personClazz == personClazz1);
        System.out.println(personClazz == personClazz2);

    }

    /**
     * 示例:通过Class创建实例对象
     *
     * // 无参数
     * <bean id="person" class="com.muse.reflect.Person" />
     *
     * // 有参数
     * <bean id="person" class="com.muse.reflect.Person" >
     *    <constructor-arg index="0" type="java.lang.String" value="muse"/>
     * </bean>
     */
    @Test
    public void test2() throws Throwable{
        /** 首先:获得Person的字节码 */
        Class personClazz = Class.forName("com.muse.reflect.Person");

        /** 其次:通过Class对象,创建构造方法对象 */
        Constructor constructor1 = personClazz.getConstructor(); // 初始化无参构造方法
        Constructor constructor2 = personClazz.getConstructor(String.class, Integer.class, Byte.class,
                Boolean.class); // 初始化有参构造方法对象

        /** 最后:通过构造方法创建对象 */
        // 调用无参数构造方法创建Person对象
        Person person1 = (Person) constructor1.newInstance();
        person1.setName("muse1");
        System.out.println("person1=" + person1);

        // 调用有参数构造方法创建Person对象
        Person person2 = (Person) constructor2.newInstance("muse2", 10, (byte) 1, true);
        System.out.println("person2=" + person2);


        /** 补充内容:反射通过私有构造方法创建对象,破坏单例模式 */
        Class singletonPersonClazz = SingletonPerson.class;
        // Constructor constructor3 = singletonPersonClazz.getConstructor();
        Constructor constructor3 = singletonPersonClazz.getDeclaredConstructor();
        constructor3.setAccessible(true);
        SingletonPerson singletonPerson = (SingletonPerson) constructor3.newInstance();
        SingletonPerson singletonPerson1 = SingletonPerson.getInstance();
        SingletonPerson singletonPerson2 = SingletonPerson.getInstance();
        System.out.println("singletonPerson==singletonPerson1 is " + (singletonPerson==singletonPerson1));
        System.out.println("singletonPerson==singletonPerson2 is " + (singletonPerson==singletonPerson2));
        System.out.println("singletonPerson1==singletonPerson2 is " + (singletonPerson1==singletonPerson2));
    }

    /**
     * public属性的Field
     */
    @Test
    public void test3() throws Throwable{
        // 第一步:获得Class
        Class personClazz = Person.class;

        // 第二步:获得构造方法
        Constructor<Person> constructor = personClazz.getConstructor();
        Person person = constructor.newInstance(); // 初始化生成Person对象

        // 第三步:通过Class对象,获得Field对象  Person[类]的name属性。
        Field nameField = personClazz.getField("name");

        // 第四步:操作Field,获得属性值
        String name = String.valueOf(nameField.get(person));

        System.out.println(name);
    }

    /**
     * private属性Field
     */
    @Test
    public void test4() throws Throwable{
        // 第一步:获得Class
        Class personClazz = Person.class;

        // 第二步:获得构造方法
        Constructor<Person> constructor = personClazz.getConstructor();
        Person person = constructor.newInstance();

        // 第三步:通过Class对象,获得Field对象
        Field sexField = personClazz.getDeclaredField("sex");
        sexField.setAccessible(true);

        // 第四步:操作Field,获得属性值
        System.out.println(sexField.get(person));
    }

    /**
     * 通过反射获得类的public属性值
     * <p>
     * getField 只能获取public的,包括从父类继承来的字段。
     * getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,
     * 除非加上setAccessible(true))
     */
    @Test
    public void getPublicField() throws Throwable {
        /** 首先:获得Person的字节码 */
        Class personClazz = Person.class;

        /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
        Person person = (Person) personClazz.getConstructor().newInstance();

        /** 第三:通过Class对象,获得Field对象 */
        // Field nameField = personClazz.getField("name"); // 不报错
        Field nameField = personClazz.getDeclaredField("name");

        /**
         * Field.toString();
         * --------------------------------------------------------------------
         * public String toString() {
         *         int mod = getModifiers();
         *         return (((mod == 0) ? "" : (Modifier.toString(mod) + " "))
         *             + getType().getTypeName() + " "
         *             + getDeclaringClass().getTypeName() + "."
         *             + getName());
         * }
         *
         * 【Field的重要方法】
         * System.out.println("获取字段的类型:" + nameField.getType());
         * System.out.println("获取字段的名字:" + nameField.getName());
         * System.out.println("获取字段的访问修饰符:" + Modifier.toString(nameField.getModifiers()));
         * System.out.println("获取字段所在类的全路径:" + nameField.getDeclaringClass().getName());
         */
        System.out.println(nameField); // protected java.lang.Integer com.muse.Person.name

        /** 最后:获取字段的类型 */
        String name = String.valueOf(nameField.get(person));
        System.out.println(name);
    }

    /**
     * 获得private属性和方法
     *
     * @throws Throwable
     */
    @Test
    public void getPrivateField() throws Throwable {
        /** 首先:获得Person的字节码 */
        Class personClazz = Person.class;

        /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
        Person person = (Person) personClazz.getConstructor().newInstance();

        /** 第三:通过Class对象,获得Field对象 */
        // Field sexField = personClazz.getField("sex");  // 不能使用getField,否则报错:java.lang.NoSuchFieldException: sex
        Field sexField = personClazz.getDeclaredField("sex");
        sexField.setAccessible(true); // 必须设置为true

        /** 最后:获取字段的类型 */
        Byte sex = (Byte) sexField.get(person);
        System.out.println("private属性:sex=" + sex);

        /** 补充内容:获取private类型的方法 */
        // Method method = personClazz.getMethod("privateMethod"); // 不能使用getMethod,否则报错:java.lang.NoSuchMethodException: com.muse.reflect.Person.privateMethod()
        Method method = personClazz.getDeclaredMethod("privateMethod");
        method.setAccessible(true); // 必须设置为true
        System.out.println("private方法:privateMethod()=" + method.invoke(person));
    }

    /**
     * 获得protected属性
     *
     * @throws Throwable
     */
    @Test
    public void getProtectedField() throws Throwable {
        /** 首先:获得Person的字节码 */
        Class personClazz = Person.class;

        /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
        Person person = (Person) personClazz.getConstructor().newInstance();

        /** 第三:通过Class对象,获得Field对象 */
        // Field ageField = personClazz.getField("age"); // java.lang.NoSuchFieldException: age
        Field ageField = personClazz.getDeclaredField("age");
        ageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers "protected"

        /** 最后:获取字段的类型 */
        Integer age = (Integer) ageField.get(person);
        System.out.println(age);
    }

    /**
     * 获得default属性
     */
    @Test
    public void getDefaultField() throws Throwable {
        /** 首先:获得Person的字节码 */
        Class personClazz = Person.class;

        /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
        Person person = (Person) personClazz.getConstructor().newInstance();

        /** 第三:通过Class对象,获得Field对象 */
        // Field isMarriageField = personClazz.getField("isMarriage"); // java.lang.NoSuchFieldException: isMarriage
        Field isMarriageField = personClazz.getDeclaredField("isMarriage");
        isMarriageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers ""

        /** 最后:获取字段的类型 */
        Boolean isMarriage = (Boolean) isMarriageField.get(person);
        System.out.println(isMarriage);
    }
}

public class Person {

public String name = "com/muse";

protected Integer age = 1;

private Byte sex = (byte) 1;

Boolean isMarriage = true;

// 无参数
public Person() {
}

// 有参数
public Person(String name, Integer age, Byte sex, Boolean isMarriage) {
    this.name = name;
    this.age = age;
    this.sex = sex;
    this.isMarriage = isMarriage;
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值