彻底理解Java反射

一、引入 - 反射就是这么强大

1.1、需求

现有接口Animal,接口中存在抽象方法void cry();void hi(),实现类Dog、Cat实现了Animal接口,现要求编写一个程序,编写完成后,可在不修改源码的情况下,动态控制程序创建的对象实例是Dog或是Cat,并且还可以动态指定调用的方法是void cry();还是void hi();

  • Animal接口
package reflect.example.pojo;

/**
 * @author IT00ZYQ
 * @date 2021/5/19 18:05
 **/
public interface Animal {
    /**
     * 动物叫声
     */
    void cry();

    /**
     * 打招呼
     */
    void hi();
}
  • Cat类
package reflect.example.pojo;
/**
 * @author IT00ZYQ
 * @date 2021/5/19 18:06
 **/
public class Cat implements Animal {
    @Override
    public void cry() {
        System.out.println("喵喵猫 ~~~ ");
    }

    @Override
    public void hi() {
        System.out.println("Hi! 喵 ~");
    }
}
  • Dog类
package reflect.example.pojo;

/**
 * @author IT00ZYQ
 * @date 2021/5/19 18:07
 **/
public class Dog implements Animal {
    @Override
    public void cry() {
        System.out.println("汪汪汪 ~~~");
    }

    @Override
    public void hi() {
        System.out.println("Hi! 汪 ~");
    }
}

1.2、尝试常规写法

package reflect.example;

import reflect.example.pojo.Animal;
import reflect.example.pojo.Cat;
import reflect.example.pojo.Dog;

/**
 * @author IT00ZYQ
 * @date 2021/5/19 18:20
 **/
public class TestClass_Common {
    public static void main(String[] args) {
        /**
         * 通过直接new的方式创建对象
         * 当想要修改创建Dog实例时,只能修改源码
         * 当想要修改调用的方法时,只能修改源码
         */
        Animal animal = new Cat();
        //Animal animal = new Dog();
        animal.cry();
        //animal.hi();
    }
}

使用常规写法发现,在不修改源码的情况下,根本无法动态指定创建的对象实例,也无法动态指定调用的方法。

1.3、使用反射机制完美解决

  • 首先,我们创建一个配置文件example.properties,用于动态指定创建的对象实例和动态指定调用的方法
# 要创建的对象
MyClassPath=reflect.example.pojo.Dog
#调用的的方法
MyMethodName=cry
  • 读取配置文件信息并通过反射创建对象实例并调用方法
package reflect.example;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * @author IT00ZYQ
 * @date 2021/5/19 18:07
 **/
public class TestClass_Reflect {
    public static void main(String[] args) {
        /**
         * 创建的类与调用的方法都是从配置文件中读取的
         * 可根据配置文件的信息动态创建对象
         * 可根据配置文件的信息动态调用方法
         * 无需修改源码
         */
        try {
            //1. 使用工具类Properties读取配置文件example.properties
            Properties properties = new Properties();
            properties.load(new FileInputStream("src/reflect/example/example.properties"));
            //2. 读取类的全路径
            String myClassPath = properties.getProperty("MyClassPath");
            //3. 读取要调用的方法名称
            String myMethodName = properties.getProperty("MyMethodName");
            //4. 通过反射获取要创建的对象的Class类
            Class<?> myClass = Class.forName(myClassPath);
            //5. 通过反射获取要调用的方法的Method类
            Method myMethod = myClass.getMethod(myMethodName);
            //6. 创建对象实例
            Object instance = myClass.newInstance();
            //7. 调用方法
            myMethod.invoke(instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这样,就能在不修改源码的情况下控制程序动态创建对象实例与调用方法了。如今盛行的Spring、Mybatis等框架也是大量使用了发射机制,也就是说,如果没有反射机制,那么这些框架也就无从说起,也许Java的价值也会大大降低。

二、Java反射是什么

  1. 反射机制允许程序在运行期借助于java.lang.reflect包下的Api取得类的内部信息,比如成员变量,成员方法,构造器等等,并能操作对象的属性以及方法。
  2. 当第一次使用到一个类时,会触发JVM进行类加载,加载完类之后,在堆中就产生了一个类型为Class的对象(每一个类只有一份),这个Class对象包含了类的完整结构信息,这个类就像一面镜子,通过这个类,可以看到类的结构,反射这个名字也是因此而来。
  3. 反射相关的类
java.lang.Class:代表一个类,Class对象表示某个类加载之后在堆中的对象
java.lang.reflect.Method:代表类的方法,一个Method对象表示类中的一个方法
java.lang.reflect.Field:代表类的属性,一个Filed对象表示类中的一个属性
java.lang.reflect.Constructor:代表类的构造器,一个Constructor对象表示类中的一个构造方法
  1. 反射的优缺点
优点:可以动态的创建和使用对象,是框架的底层核心
缺点:反射基本是解释执行方式,执行速度较慢

三、详解Class类

3.1、Class的类图

在这里插入图片描述

3.2、Class类的常用方法

在这里插入图片描述

3.3、基本介绍

  • Class也是类,也继承自超类Object
  • Class类的对象实例不是通过new关键字创建的,而是JVM在类加载时创建的
  • 每一个类只加载一次,每个类在堆中只会有一份Class类对象
  • 每个类的对象实例的对象头中会有指针指向Class类对象,用于标记自己由哪个Class类对象生成
  • 通过Class类对象可以完整得到一个类的完整结构
  • 所有Class类对象都存放在堆中

3.4、获取Class类对象的方法

package reflect;

import reflect.pojo.Person;

/**
 * 获取Class类对象的方法
 * @author IT00ZYQ
 * @date 2021/5/19 19:10
 **/
public class GetClassInstance {
    public static void main(String[] args) throws Exception {
        // 1. Class.forName(类的全路径)
        // 前提条件:已知类的全路径
        // 应用场景:多用于通过配置文件动态加载类
        Class<?> forName = Class.forName("reflect.pojo.Person");

        // 2. Person.class
        // 前提条件:已知具体的类
        // 应用场景:多用于参数传递
        Class<Person> personClass = Person.class;

        // 3. 对象.getClass()
        // 前提条件:已知类的实例
        Person p = new Person();
        Class<? extends Person> aClass = p.getClass();

        // 4. 基本数据类型.class
        Class<Integer> integerClass = int.class;
        Class<Float> floatClass = float.class;
        
        // 5. 包装类.Type
        Class<Integer> integerType = Integer.TYPE;
        Class<Float> floatType = Float.TYPE;
    }
}

3.5、有Class对象的类型

  • 外部内
  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类
  • 接口
  • 数组
  • 枚举
  • 注解
  • 基本数据类型
  • Void

四、用于测试反射的类

package reflect.pojo;

/**
 * @author IT00ZYQ
 * @date 2021/5/19 19:11
 **/
public class Person extends Father implements interface1, interface2{
    /**
     * 属性
     */
    private String name = "张三";
    int age = 18;
    protected float height = 183F;
    public String gender = "男";
    public final static int tt = 1;
    /**
     * 构造器
     */
    private Person(int age) {
        this.age = age;
    }
    protected Person(String name) {
        this.name = name;
    }
    Person(String name, float height) {
        this.name = name;
        this.height = height;
    }
    public Person() {}

    /**
     * 方法
     */
    private void m1() {
        System.out.println("m1方法 private");
    }
    protected void m2() {
        System.out.println("m2方法 protected");
    }
    void m3() {
        System.out.println("m3方法 缺省");
    }
    public void m4() {
        System.out.println("m4方法 public");
    }

    /**
     * 静态方法
     */
    public static void m5() {
        System.out.println("m5方法 public static");
    }
}

class Father{
    /**
     * 属性
     */
    private int year;
    int month;
    protected int day;
    public int hour;

    /**
     * 构造器
     */
    public Father() { }
    private Father(int year) {
        this.year = year;
    }
    protected Father(int year, int month) {
        this.year = year;
        this.month = month;
    }
    /**
     * 方法
     */
    private void year() {
        System.out.println("year方法 private");
    }
    public void hour() {
        System.out.println("hour方法 public");
    }
}

interface interface1 {

}
interface interface2 {

}

五、通过反射获取类信息

package reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @author IT00ZYQ
 * @date 2021/5/19 19:32
 **/
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        // 1. 获取Person类的Class对象
        Class<?> personClass = Class.forName("reflect.pojo.Person");

        // 2. 获取全类名 reflect.pojo.Person
        String name = personClass.getName();
        System.out.println("2. 获取全类名 -> " + name);

        // 3. 获取类名 Person
        String simpleName = personClass.getSimpleName();
        System.out.println("3. 获取类名 -> " + simpleName);

        // 4. 获取本类及父类中所有public修饰的属性
        Field[] fields = personClass.getFields();
        System.out.println("4. 获取本类及父类中所有public修饰的属性 -> " + Arrays.toString(fields));

        // 5. 获取本类所有属性
        Field[] declaredFields = personClass.getDeclaredFields();
        System.out.println("5. 获取本类所有属性 -> " + Arrays.toString(declaredFields));

        // 6. 获取本类及父类中所有public修饰的方法
        Method[] methods = personClass.getMethods();
        System.out.println("6. 获取本类及父类中所有public修饰的方法 -> " + Arrays.toString(methods));

        // 7. 获取本类所有方法
        Method[] declaredMethods = personClass.getDeclaredMethods();
        System.out.println("7. 获取本类所有方法 -> " + Arrays.toString(declaredMethods));

        // 8. 获取本类中所有public修饰的构造器
        Constructor<?>[] constructors = personClass.getConstructors();
        System.out.println("8. 获取本类中所有public修饰的构造器 -> " + Arrays.toString(constructors));

        // 9. 获取本类中所有的构造器
        Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
        System.out.println("9. 获取本类中所有的构造器 -> " + Arrays.toString(declaredConstructors));

        // 10. 获取包信息
        Package aPackage = personClass.getPackage();
        System.out.println("10. 获取包信息 -> " + aPackage);

        // 11. 获取父类信息
        Class<?> superclass = personClass.getSuperclass();
        System.out.println("11. 获取父类信息 -> " + superclass);

        // 12. 获取接口信息
        Class<?>[] interfaces = personClass.getInterfaces();
        System.out.println("12. 获取接口信息 -> " + Arrays.toString(interfaces));

        // 13. 获取注解信息
        Annotation[] annotations = personClass.getAnnotations();
        System.out.println("13. 获取注解信息 -> " + Arrays.toString(annotations));

    }
}

输出结果:

2. 获取全类名 -> reflect.pojo.Person
3. 获取类名 -> Person
4. 获取本类及父类中所有public修饰的属性 -> [public java.lang.String reflect.pojo.Person.gender, public int reflect.pojo.Father.hour]
5. 获取本类所有属性 -> [private java.lang.String reflect.pojo.Person.name, int reflect.pojo.Person.age, protected float reflect.pojo.Person.height, public java.lang.String reflect.pojo.Person.gender]
6. 获取本类及父类中所有public修饰的方法 -> [public void reflect.pojo.Person.m4(), public void reflect.pojo.Person.hour(), public final void java.lang.Object.wait() throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public java.lang.String java.lang.Object.toString(), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
7. 获取本类所有方法 -> [void reflect.pojo.Person.m3(), public void reflect.pojo.Person.m4(), public void reflect.pojo.Person.hour(), private void reflect.pojo.Person.m1(), protected void reflect.pojo.Person.m2()]
8. 获取本类中所有public修饰的方法 -> [public reflect.pojo.Person()]
9. 获取本类中所有的构造器 -> [public reflect.pojo.Person(), reflect.pojo.Person(java.lang.String,float), protected reflect.pojo.Person(java.lang.String), private reflect.pojo.Person(int)]
10. 获取包信息 -> package reflect.pojo
11. 获取父类信息 -> class reflect.pojo.Father
12. 获取接口信息 -> [interface reflect.pojo.interface1, interface reflect.pojo.interface2]
13. 获取注解信息 -> []
Process finished with exit code 0

六、通过反射创建对象实例

package reflect;

import java.lang.reflect.Constructor;

/**
 * @author IT00ZYQ
 * @date 2021/5/19 21:01
 **/
public class ReflectCreateInstance {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("reflect.pojo.Person");

        // 方式1;personClass.newInstance(),前提是该类必须有public修饰的无参构造器
        Object instance1 = personClass.newInstance();
        System.out.println("instance1 -> " + instance1);

        // 方式2:通过构造器创建对象实例
        // 获取无参构造器
        Constructor<?> constructor1 = personClass.getDeclaredConstructor();
        // 创建对象
        if (!constructor1.isAccessible()) {
            constructor1.setAccessible(true);
        }
        Object instance2 = constructor1.newInstance();
        System.out.println("instance2 -> " + instance2);

        // 获取参数类型为(int.class)的构造器
        // 该构造器为私有构造器,无法通过getConstructor(int.class)获取
        // 报错 java.lang.NoSuchMethodException: reflect.pojo.Person.<init>(int)
        //Constructor<?> constructor2 = personClass.getConstructor(int.class);
        Constructor<?> constructor2 = personClass.getDeclaredConstructor(int.class);
        // 虽然Person确实有参数列表为(int.class)的构造器
        // 但是并不是public修饰的构造器,而是private
        // 需要设置accessible属性为true才能正常使用
        // 否则会抛出异常java.lang.IllegalAccessException: Class reflect.ReflectCreateInstance can not access a member
        if (!constructor2.isAccessible()) {
            constructor2.setAccessible(true);
        }
        // 创建对象
        Object instance3 = constructor2.newInstance(18);
        System.out.println("instance3 -> " + instance3);

        // 尝试获取参数列表为(int.class, int.class)的构造器
        // 该构造器并不存在
        // 报错java.lang.NoSuchMethodException: reflect.pojo.Person.<init>(int, int)
//        Constructor<?> constructor3 = personClass.getDeclaredConstructor(int.class, int.class);
//        // 创建对象
//        if (!constructor2.isAccessible()) {
//            constructor2.setAccessible(true);
//        }
//        Object instance4 = constructor3.newInstance(18, 18);
//        System.out.println("instance4 -> " + instance4);

    }
}

输出结果:

instance1 -> reflect.pojo.Person@4554617c
instance2 -> reflect.pojo.Person@74a14482
instance3 -> reflect.pojo.Person@1540e19d

Process finished with exit code 0

七、通过反射操作属性

package reflect;

import java.lang.reflect.Field;

/**
 * 修饰符值计算方法:
 * 默认修饰符 0,public 1,private 2,protected 4,static 8,final 16
 * 有多个时进行相加
 * @author IT00ZYQ
 * @date 2021/5/19 21:15
 **/
public class ReflectOperateField {
    public static void main(String[] args) throws Exception {
        Class<?> personClass = Class.forName("reflect.pojo.Person");
        Object instance = personClass.newInstance();

        // 1. 获取public修饰的属性gender
        Field genderField = personClass.getField("gender");
        // 2. 获取gender的值
        Object genderValue = genderField.get(instance);
        System.out.println("gender属性的值为:" + genderValue);
        // 3. 修改gender的值
        genderField.set(instance, "女");
        genderValue = genderField.get(instance);
        System.out.println("gender属性的值为:" + genderValue);
        // 4. 获取修饰符值 public 1
        System.out.println("修饰符值:" + genderField.getModifiers());


        System.out.println("=========================================");

        // 1. 获取非public修饰的属性age
        // 使用personClass.getField("age")报错java.lang.NoSuchFieldException: age
        //Field ageField = personClass.getField("age");
        Field ageField = personClass.getDeclaredField("age");

        // 没有设置Accessible属性为true,报错
        // java.lang.IllegalAccessException: Class reflect.ReflectOperateField can not access a member of
        if (!ageField.isAccessible()) {
            ageField.setAccessible(true);
        }
        // 2. 获取age的值
        Object ageValue = ageField.get(instance);
        System.out.println("age属性的值为:" + ageValue);
        // 3. 修改age的值
        ageField.set(instance, 20);
        ageValue = ageField.get(instance);
        System.out.println("age属性的值为:" + ageValue);
        // 4. 获取修饰符值 默认 0
        System.out.println("修饰符值:" + ageField.getModifiers());

        System.out.println("=========================================");
        // 1. 获取public final static修饰的属性tt
        Field ttField = personClass.getField("tt");

        // 2. 获取tt的值,static属性,传null即可
        Object ttValue = ttField.get(null);
        System.out.println("tt属性的值为:" + ttValue);
        // 3. 尝试修改tt的值
        // 报错 java.lang.IllegalAccessException: Can not set static final int field reflect.pojo.Person.tt to java.lang.Integer
        // final修饰的值无法修改
//        ttField.set(instance, 20);
//        ttValue = ttField.get(null);
//        System.out.println("tt属性的值为:" + ttValue);
        // 4. 获取修饰符值 public 1 + final 16 + static 8 = 25
        System.out.println("修饰符值:" + ttField.getModifiers());


    }
}


输出结果:

gender属性的值为:男
gender属性的值为:女
修饰符值:1
=========================================
age属性的值为:18
age属性的值为:20
修饰符值:0
=========================================
tt属性的值为:1
修饰符值:25

Process finished with exit code 0

八、通过反射操作方法

package reflect;

import java.lang.reflect.Method;

/**
 * 修饰符值计算方法:
 * 默认修饰符 0,public 1,private 2,protected 4,static 8,final 16
 * 有多个时进行相加
 * @author IT00ZYQ
 * @date 2021/5/19 21:34
 **/
public class ReflectOperateMethod {
    public static void main(String[] args) throws Exception{
        Class<?> personClass = Class.forName("reflect.pojo.Person");
        Object instance = personClass.newInstance();

        // 1. 获取public修饰的普通方法
        Method m4Method = personClass.getMethod("m4");
        // 2. 调用方法
        m4Method.invoke(instance);
        // 3. 修饰符值 public 1
        System.out.println("修饰符值:" + m4Method.getModifiers());

        System.out.println("====================================");

        // 1. 获取非public修饰的方法
        // 非public修饰的方法无法使用getMethod("m2")获取,报错
        // java.lang.NoSuchMethodException: reflect.pojo.Person.m2()
        Method m2Method = personClass.getDeclaredMethod("m2");
        // 2. 调用方法
        // 非public修饰的方法调用前必须先设置accessible属性为true
        // 否则报错:ava.lang.IllegalAccessException: Class reflect.ReflectOperateMethod can not access a member of
        if (!m2Method.isAccessible()) {
            m2Method.setAccessible(true);
        }
        m2Method.invoke(instance);
        // 3. 修饰符值 protected 4
        System.out.println("修饰符值:" + m2Method.getModifiers());


        System.out.println("=====================================");
        // 1. 获取public static修饰的方法
        Method m5Method = personClass.getMethod("m5");
        // 调用静态方法无需传入对象实例,传null即可
        m5Method.invoke(null);
        // 3. 修饰符值 public 1 + static 8 = 9
        System.out.println("修饰符值:" + m5Method.getModifiers());


    }
}


输出结果:

m4方法 public
修饰符值:1
====================================
m2方法 protected
修饰符值:4
=====================================
m5方法 public static
修饰符值:9

Process finished with exit code 0
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

it00zyq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值