反射机制

通过Java的反射机制,我们能够实现在程序运行时动态的加载类、查看类的所有方法和字段定义,甚至修改private字段的值。有关反射的类和方法在JDK的包java.lang.reflect下。

理解“类类型”

“类类型”就是字面的意思,假设我们有一个User类,我们可以通过User.class获得该类的类类型。甚至,void也可以获得类类型。

Class userClass = User.class;
Class voidClass = void.class;

同理,方法(Method)、字段(Field)、构造函数(Constructor)都有一个类型来表示。为什么Java提供了这些信息的类型呢?就是为了实现反射,Java是纯面向对象的语言,所有的数据都是类的实例,因此JDK中提供了这些工具类,其中许多方法都是native的,是JVM实现的。

Class

Class是一个类的类型,我们可以通过以下2种方式获取一个类或一个类实例的类类型:

Class userClass1 = User.class;

User u = new User();
Class userClass2 = u.getClass();

实际上,userClass1userClass2指向同一个对象。

Class对象是支持泛型的:

Class<User> userClass = User.class;

通过类类型可以获得类全名等信息:

Class<User> userClass = User.class;
System.out.println(userClass.getName());

我们进行的所有反射操作都是从这个类类型开始的,我们从类类型可以获得这个类的方法定义,字段定义等等。

Method

Method是类的方法定义信息,下面代码可以打印一个类中的所有方法:

Class<User> userClass = User.class;
Method[] methods = userClass.getDeclaredMethods();
for(Method m : methods)
{
  System.out.println(m.getName());
}

注:getMethods()方法能返回所有public方法,getDeclaredMethods()能返回所有方法。

除此之外,Method对象还有许多工具方法,可以获得返回值类型等,参照文档都很好理解,这里就不多介绍了。

Field

Field是类的字段定义信息,下面代码可以打印一个类的所有字段:

Class<User> userClass = User.class;
Field[] fields = userClass.getDeclaredFields();
for(Field f : fields)
{
  System.out.println(f.getName());
}

我们还可以通过反射修改一个字段的值,下面代码尝试通过反射修改一个private字段的值:

package com.gacfox;

import java.lang.reflect.Field;

class A
{
    private String str;

    public A(String str)
    {
        this.str = str;
    }

    public void print()
    {
        System.out.println(str);
    }
}

public class Main
{

    public static void main(String[] args)
    {
        //实例化A
        A a = new A("hello");
        a.print();

        //获取Class对象
        Class aClass = a.getClass();
        try
        {
            //获取字段
            Field field = aClass.getDeclaredField("str");
            //设置访问属性,因为是private的,这样设置后可以忽视访问控制修饰符强制修改
            field.setAccessible(true);
            //设置字段的值
            field.set(a, "world");
            a.print();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

类的动态加载

下面代码中,我们实现通过类名字符串动态加载一个类。

public class Main
{
    public static void main(String[] args)
    {
        A a = null;
        try
        {
            Class aClass = Class.forName("com.gacfox.A");
            if(aClass != null)
            {
                // 这样写适用于有无参数构造函数的类
                // a = (A) aClass.newInstance();

                //调用Constructor对象实例化A类,并传入构造方法参数
                Constructor constructor = aClass.getDeclaredConstructor(String.class);
                a = (A) constructor.newInstance("hello");
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        a.print();
    }
}
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap