Java之反射机制深入理解

反射机制是Java中的一个难点,反射机制也是各个框架的核心,个人认为正是反射机制的存在,才显得Java的OOP特性的强大,这篇博客中,笔者会基于自己的理解和阅读的书籍以及博客,做一个比较详细的介绍,剖析底层实现原理(仅供参考),如有存在错误的地方,欢迎指正。

Java反射机制

Java反射机制是在运行状态中,对任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取信息以及动态调用对象的方法的功能称为Java语言的反射机制。

反射机制主要提供的功能

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行时判断任意一个类所具有的成员变量和方法;
  4. 在运行时调用任意一个对象的方法。

反射机制原理大致流程如下

在这里插入图片描述
现在对反射机制的有一个大致的了解后,我们现在要深入剖析其内部实现。
反射的一切基础都是围绕着Class类对象展开的,而Class对象就是用过.class文件获得的。Java中使用Class类表示某个.class文件并且任何一个.class文件都是Class这个类的一个实例对象。
接上面的原理图后,我们现在以.class文件开始,展开介绍底层实现反射的剖析
在这里插入图片描述
接下来我们将按照上面的流程,分别展开具体的介绍

获取Class对象

三种方式获取
(1)通过类名.class获取

public class Test {

    public static void main(String[] args) {
        // 获取Person的Class对象
        Class demo = Person.class;
        // 输出:class com.llm.test.Person
        System.out.println(demo);
    }
}

(2)Object类的成员方法getClass()方法获取

public class Test {

    public static void main(String[] args) {
    	// 实例化Person对象
        Person person = new Person();
        // 获取Person的Class对象
        Class demo = person.getClass();
        // 输出:class com.llm.test.Person
        System.out.println(demo);
    }
}

(3)通过Class.forName(“全限定类名”)方法获取

public class Test {

    public static void main(String[] args) throws ClassNotFoundException {
        Class demo = Class.forName("com.llm.test.Person");
        // 输出:class com.llm.test.Person
        System.out.println(demo);
    }
}

Class对象的常用方法:

// 获得类名
public String getSimpleName();

// 获取完整类名,包名+类名
public String getName();

// 获取Class的一个实例
// 必须要有public的无参构造函数,否则无法实例化
// 但是java9中已经不推荐使用该方法了
// 使用 类对象.getDeclaredConstructor().newInstance()代替
public T newInstance();

测试如下:

public class Test {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class demo = Class.forName("com.llm.test.Person");

        System.out.println(demo.getSimpleName());    // 输出:Person
        System.out.println(demo.getName());         // 输出:com.llm.test.Person
        System.out.println(demo.newInstance());    // 输出:com.llm.test.Person@b4c966a
        System.out.println(demo.getConstructor().newInstance());    // 输出:com.llm.test.Person@b4c966a
    }
}

Constructor类的获取

该类是构造方法的类,类中的每一个构造方法都是Constructor的对象,通过Constructor对象可以实例化读反射类的对象,利用Class对象的方法获取。
主要有以下四中方式获取:

// 根据参数获取构造方法对象,只获取public修饰的
public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 根据参数获取构造方法对象,包括private修饰的
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
// 获取所有public方法
public Constructor<?>[] getConstructors() 
// 获取所有构造方法包含private
public Constructor<?>[] getDeclaredConstructors() 

举例代码:

public class Test {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class demo = Class.forName("com.llm.test.Person");
        // 输出 : public com.llm.test.Person(java.lang.String,int)
        System.out.println(demo.getConstructor(String.class,int.class));
        // 输出:private com.llm.test.Person(java.lang.String)
        System.out.println(demo.getDeclaredConstructor(String.class));
        // 输出:
        // public com.llm.test.Person(java.lang.String,int)
        //public com.llm.test.Person()
        for (Constructor constructor : demo.getConstructors()) {
            System.out.println(constructor);
        }

        // 输出:
        // private com.llm.test.Person(java.lang.String)
        //public com.llm.test.Person(java.lang.String,int)
        //public com.llm.test.Person()
        for (Constructor declaredConstructor : demo.getDeclaredConstructors()) {
            System.out.println(declaredConstructor);
        }
    }
}

Constructor类常用方法
比较简单就不举例了

// 创建对象实例
public T newInstance(Object ... initargs)
// flag为true可以直接访问私有类型的构造方法,反之则不可
public void setAccessible(boolean flag)

Method类

Method类是反射类的所有成员方法的,与Constructor类同样的原理使用。也是利用Class对象获取。
获取方法:

 // 根据方法名和参数类型获取一个方法对象,只有public方法
 public Method getMethod(String name, Class<?>... parameterTypes)
 // 根据方法名和参数类型获取所有public修饰的方法,包括父类的public方法
 public Method[] getMethods()
 // 根据方法名和参数类型获取一个方法对象,可以获取private修饰的方法
 public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 // 根据方法名和参数类型获取所有的方法,包括私有,但是不包括父类中所有方法,即获取不到父类的方法
 public Method[] getDeclaredMethods()

Method类中的常用方法

	// 根据args参数调用对象obj的该成员方法
    public Object invoke(Object obj, Object... args)
    //设置私有成员方法的访问权限
    public void setAccessible(boolean flag)

Field类

Field类是属性类,是类中所有属性的对象,通过Field对象可以给对应的属性赋值和取值。也是用Class对象获取。

	// 根据属性名获取属性对象,只能获取public
    public Field getField(String name)
    // 获取所有public属性对象
    public Field[] getFields()
    // 根据属性名获取属性对象,包括private
    public Field getDeclaredField(String name)
    // 获取所有属性对象,包括private的
    public Field[] getDeclaredFields()

Field的常用方法比较多,都比较简单,这里介绍两个

// 同上一样,设置私有访问权限
public void setAccessible(boolean flag)
// 获取属性类型,返回的是Class对象
public Class<?> getType()

此外,Field的其他各种setXxx和getXxx方法都是根据实际的类型,然后进行具体调用


Java反射机制大致就是这些,底层是通过一些类的封装和调用完成的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值