Java 反射

1、获取 Class 对象

一个Class对象可以表示一个类型,可能是类类型(包括数组类型)、接口类型,或者是基本数据类型和void

① 使用Object类中的getClass方法获取对应的 Class 对象:

class Employee {}

class Manager extends Employee {}

public class Main {
    public static void main(String[] args) {
        Employee e = new Manager();
        Class<? extends Employee> cl = e.getClass();
        String name = cl.getName();
        System.out.println(name); // com.company.Manager
    }
}

如上所示,因为 e 引用的对象的类型是 Manager,所以获取的 Class 对象保存了 Manager 类的信息。我们使用 getName 输出 cl 保存的类型名称,得到com.company.Manager

需要注意,虚拟机为每一个类型管理一个唯一的 Class 对象,我们可以利用==运算符实现两个 Class 对象的比较:

class Employee {}

class Manager extends Employee {}

public class Main {
    public static void main(String[] args) {
        Manager m1 = new Manager();
        Manager m2 = new Manager();
        System.out.println(m1.getClass() == m2.getClass()); // true
    }
}

② 使用静态方法forName获得类名对应的 Class 对象:

Class<?> cl = Class.forName("com.company.Manager");

我们需要传入一个类名或接口名的完全限定名,否则将抛出一个检查型异常。

③ 如果 T 是任意的 Java 类型或 void 关键字,T.class将代表对应的 Class 对象:

Class<Integer> cl1 = Integer.class;
Class<Integer> cl2 = int.class;
Class<Void> cl3 = void.class;

如上所示,它们对应的类型名分别是 java.lang.Integer、int 和 void。

需要注意的是,使用.class获取 Class 对象的引用时,不会自动初始化该 Class 对象表示的类,而前两个方法会进行初始化。(类加载的步骤:加载、链接和初始化)

如下所示:

class Init1 {
    static { System.out.println("Initializing Init1"); }
}

class Init2 {
    static { System.out.println("Initializing Init2"); }
}

class Init3 {
    static { System.out.println("Initializing Init3"); }
}

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Init1 init1 = new Init1();
        Class<? extends Init1> cl1 = init1.getClass();

        Class<?> cl2 = Class.forName("com.company.Init2");

        Class<Init3> cl3 = Init3.class;
    }
}

运行结果:

Initializing Init1
Initializing Init2

可以发现,输出结果中并没有Initializing Init3,说明Init3类并没有初始化。

2、利用反射分析类

主要使用java.lang.reflect包下的FieldMethodConstructorModifier类。

示例代码:

class Employee {
    private String name;
    private double salary;

    public Employee() {}

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public void foo(String s) {
        System.out.println(name + ":" + s);
    }
}

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> cl = Class.forName("com.company.Employee");
        // Field
        Field[] fields = cl.getDeclaredFields();
        for(Field f : fields) {
            System.out.println(f.getType().getName() + " " + f.getName());
        }
        // Constructor
        Constructor<?>[] constructors = cl.getDeclaredConstructors();
        for(Constructor<?> c : constructors) {
            Class<?>[] types = c.getParameterTypes();
            System.out.println(c.getName() + " " + Arrays.toString(types));
        }
        // Method
        Method[] methods = cl.getDeclaredMethods();
        for(Method m : methods) {
            Class<?>[] types = m.getParameterTypes();
            System.out.println(m.getReturnType().getName() + " " +
                               m.getName() + " " + Arrays.toString(types));
        }
        // Modifier
        int modifiers = fields[0].getModifiers();
        boolean aPrivate = Modifier.isPrivate(modifiers);
        boolean aStatic = Modifier.isStatic(modifiers);
        boolean aFinal = Modifier.isFinal(modifiers);
        System.out.println(aPrivate + " " + aStatic + " " + aFinal);
    }
}

运行结果:

java.lang.String name
double salary
com.company.Employee []
com.company.Employee [class java.lang.String, double]
void foo [class java.lang.String]
true false false

3、利用反射在运行时分析对象

示例代码:

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Employee e = new Employee("张三", 1800.0);
        Class<? extends Employee> cl = e.getClass();
        Field f1 = cl.getDeclaredField("name");
        Field f2 = cl.getDeclaredField("salary");
        // 将private字段设置为可访问
        f1.setAccessible(true);
        f2.setAccessible(true);
        // 获取对象e的字段值
        String name = (String)f1.get(e);
        double salary = (double) f2.get(e);
        System.out.println(name + " " + salary);
    }
}

运行结果:

张三 1800.0

其中setAccessible方法是AccessibleObject类中的一个方法,它是 Field、Method 和 Constructor 类的公共超类。

4、利用反射编写泛型数组代码

java.lang.reflect包中的Array类允许动态地创建数组,Arrays类中的copyOf方法就使用了这个类,我们分析一下:

// 拷贝数组,并指定长度,可能截断或者用null填充
public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    // 如果是原数组是Object数组,则创建Object数组
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

可以看到,copyOf 方法先使用了 Class 类的getComponentType方法获取数组的元素类型,然后使用 Array 类的newInstance方法创建数组。

5、利用反射调用任意的方法和构造器

示例代码:

public class Main {
    public static void main(String[] args) throws Exception {
        Employee e1 = new Employee("张三", 1800);
        Class<? extends Employee> cl = e1.getClass();
        // 调用方法
        Method foo = cl.getMethod("foo", String.class);
        foo.invoke(e1, "Hello World!");
        // 调用构造器
        Constructor<? extends Employee> constructor = cl.getConstructor(String.class, double.class);
        Employee e2 = constructor.newInstance("李四", 20000.0);
        e2.foo("Java is very good!");
    }
}

运行结果:

张三:Hello World!
李四:Java is very good!

如有错误,欢迎指正。.... .- ...- . .- -. .. -.-. . -.. .- -.-- -.-.--

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值