Java反射之获取类的信息

0. 前言

反射技术是Java的一大特色,它可以让Java程序运行过程中,获得一个类的所有字段和方法,也可以创建一个类的实例并调用任意一个方法。简而言之,就是解剖一个类,获得它的心肝脾肺肾,再进行操作。反射技术主要应用在框架的编写上,可能以后我们要做一个框架或者工具就会使用到反射技术。反射技术经常有用到的操作便是获取一个类的构造方法、字段、方法还有一些其它信息,并初始化一个类的对象并调用其方法。下面我就介绍一下反射技术的基本操作。

1. 获取Class对象

要想解剖一个类,得先获得它,一个类是用类Class描述的,所以一个类就是一个Class对象,下面有三种方法可以获取一个类的Class对象。此处获取Person类的Class对象,假设Person的全限定类名为edu.jyu.reflect.Person。

  1. Class类的forName()静态方法,Class clazz = Class.forName(“edu.jyu.reflect.Person”);
  2. 调用某个类的class属性,Class clazz = Person.class;
  3. 调用每个类的实例对象的getClass()方法,Class clazz = new Person().getClass();

2. 获取构造函数

类的构造方法,是用java.lang.reflect包中的类Constructor表示的,获取一个类的构造函数,有以下几个方法

  1. Constructor[] getDeclaredConstructors();返回已加载类声明的所有的构造方法(包括private修饰)的Constructor 对象数组.
  2. Constructor getDeclaredConstructor(Class[] paramTypes);返回已加载类声明的指定构造方法的Constructor 对象,paramTypes指定了参数类型.
  3. Constructor[] getConstructors();返回已加载类声明的所有的 public类型的构
    造方法的Constructor 对象数组.
  4. Constructor getConstructor(Class[] paramTypes);返回已加载类声明的指定
    的public类型的构造方法的Constructor对象,paramTypes指定了参数类型

这四个方法可以分成两类,一类(1、2方法)是可以获得private修饰的构造方法,一类(3、4方法)不可以,只能获取public的。而这两类中又可以分成两类,一类(方法名最后带s的,1、3方法)是获得所有可以获得的构造方法,一类(2、4)是获得指定参数列表对应的构造方法。说到这里可能会有点晕,通过下面这些代码大家可以看的更清晰。

先写一个Person类作为小白鼠,提供3个构造方法,其中有两个是public一个是private的,又有两个是带参数。

package edu.jyu.reflect;

class Person{
    public Person(){}

    public Person(Integer age){}

    private Person(String name){}
}

下面就是测试类,大家要对应着上面的Person类观察一下,这样比较清晰地看出各个方法之间的区别。

package edu.jyu.reflect;

import java.lang.reflect.Constructor;

class ReflectConstructorDemo{
    public static void main(String[] args) throws Exception{
        //获取Person类的Class对象
        Class pClazz = Class.forName("edu.jyu.reflect.Person");

        //=======可以获得private修饰的构造方法=========
        Constructor[] arrA = pClazz.getDeclaredConstructors();
        Constructor  conA1 = pClazz.getDeclaredConstructor(Integer.class);
        Constructor  conA2 = pClazz.getDeclaredConstructor(String.class);
        System.out.println("结果一:arrA length:"+arrA.length);
        System.out.println("结果二:"+conA1);
        System.out.println("结果三:"+conA2);

        //=======不能获得private修饰的构造方法=========
        Constructor[] arrB = pClazz.getConstructors();
        Constructor  conB1 = pClazz.getConstructor(Integer.class);
        //Constructor  conB2 = pClazz.getConstructor(String.class);
        System.out.println("结果四:arrB length:"+arrB.length);
        for (Constructor constructor : arrB) {
            System.out.println(constructor);
        }
        System.out.println("结果五:"+conB1);

    }

}

上面代码输出的结果

结果一:arrA length:3
结果二:public edu.jyu.reflect.Person(java.lang.Integer)
结果三:private edu.jyu.reflect.Person(java.lang.String)
结果四:arrB length:2
public edu.jyu.reflect.Person()
public edu.jyu.reflect.Person(java.lang.Integer)
结果五:public edu.jyu.reflect.Person(java.lang.Integer)

结果分析:

  1. 第一个结果可以看出arrA的长度为3,可以看出getDeclaredConstructors方法可以获取一个类所有的构造方法。
  2. 第二个结果是Person类的带有Integer参数的公有构造方法,说明getDeclaredConstructor方法可以获取一个类指定参数列表的公有构造方法。
  3. 第三个结果是Person类的带有String参数的私有构造方法,说明getDeclaredConstructor方法可以获取一个类指定参数列表的私有构造方法。
  4. 第四个结果说明getConstructors方法只能获取一个类所有的public修饰的构造函数。
  5. 第五个结果说明getConstructor方法可以获取一个类指定参数列表的公有构造方法。那么问题来了,怎么知道这个方法不能获取私有构造方法呢?把上面的Constructor conB2 = pClazz.getConstructor(String.class);这行代码的注释去掉,程序就会抛一个NoSuchMethodException异常,说明getConstructor根本就不能获得私有的构造方法。

3. 获取字段

类的字段,是用java.lang.reflect包中的类Field表示的,获取一个类的字段,有以下几个方法

  1. Field[] getDeclaredFields():返回已加载类声明的所有字段的Field对象数
    组,不包括从父类继承的字段.
  2. Field getDeclaredField(String name):返回已加载类声明的所有字段的
    Field对象,不包括从父类继承的字段,参数name指定字段的名称.
  3. Field[] getFields():返回已加载类声明的所有public型的字段的Field对象
    数组,包括从父类继承的字段
  4. Field getField(String name):返回已加载类声明的所有字段的 Field对象,
    包括从父类继承的字段,参数name指定字段的名称

这四个方法可以分成两类,一类(1、2方法)是可以获得private修饰的字段但是不包括从父类继承的字段,一类(3、4方法)不可以获得private修饰的字段但是包括从父类继承的字段。而这两类中又可以分成两类,一类(方法名最后带s的,1、3方法)是获得所有可以获得的字段,一类(2、4)是获得指定字段名称的字段。通过下面这些代码测试一下。

先写两个类,一个是Employee,一个是Manager,其中Manager继承Employee,这两个类都有一个公有字段和一个私有字段。

Employee类

package edu.jyu.reflect;

public class Employee {
    private String name;
    public Double salary;
}

Manager类

package edu.jyu.reflect;

public class Manager extends Employee{
    private Double bonus;
    public String Department;
}

下面是测试类

package edu.jyu.reflect;

import java.lang.reflect.Field;

public class ReflectFieldDemo {
    public static void main(String[] args) throws Exception{
        Class mClazz = Class.forName("edu.jyu.reflect.Manager");
        System.out.println("====getDeclaredFields结果====");
        Field[] arrA = mClazz.getDeclaredFields();
        for (Field field : arrA) {
            System.out.println(field);
        }
        System.out.println("====getDeclaredFields结果====");
        Field field = mClazz.getDeclaredField("bonus");
        System.out.println(field);
        field = mClazz.getDeclaredField("Department");
        System.out.println(field);
        //field = mClazz.getDeclaredField("name");

        System.out.println("====getFields结果====");
        Field[] arrB = mClazz.getFields();
        for (Field field2 : arrB) {
            System.out.println(field2);
        }
        System.out.println("====getFields结果====");
        field = mClazz.getField("Department");
        System.out.println(field);
        field = mClazz.getField("salary");
        System.out.println(field);
        //field = mClazz.getField("bonus");
    }
}

输出结果

====getDeclaredFields结果====
private java.lang.Double edu.jyu.reflect.Manager.bonus
public java.lang.String edu.jyu.reflect.Manager.Department
====getDeclaredFields结果====
private java.lang.Double edu.jyu.reflect.Manager.bonus
public java.lang.String edu.jyu.reflect.Manager.Department
====getFields结果====
public java.lang.String edu.jyu.reflect.Manager.Department
public java.lang.Double edu.jyu.reflect.Employee.salary
====getFields结果====
public java.lang.String edu.jyu.reflect.Manager.Department
public java.lang.Double edu.jyu.reflect.Employee.salary

其中那两句被注释的代码如果去掉注释,将会抛出NoSuchFieldException异常。

4. 获取方法

类的方法,是用java.lang.reflect包中的类Method表示的,获取一个类的方法,有以下几个方法

  1. Method[] getDeclaredMethods():返回已加载类声明的所有方法的 Method 对象数组,不包括从父类继承的方法.
  2. Method getDeclaredMethod(String name,Class[] paramTypes):返回已加载类声明的所有方法的 Method对象,不包括从父类继承的方法,参数name指定方
    法的名称,参数 paramTypes指定方法的参数类型.
  3. Method[] getMethods():返回已加载类声明的所有方法的Method对象数组,包
    括从父类继承的方法.
  4. Method getMethod(String name,Class[] paramTypes):返回已加载类声明的
    所有方法的Method对象,包括从父类继承的方法,参数name指定方法的名称,参
    数paramTypes指定方法的参数类型

大家可以观察到,获取类方法的这几个方法与获取类的字段十分相像,所以在此便不再多解释了。

5. 获取其它信息

当你得到一个类的Class对象的时候,基本上你就可以获取它的全部信息了,除了构造方法,字段,方法外还有一些类的包名、父类等等信息,如下面这几种。

  1. int getModifiers():返回已加载类的修饰符的整形标识值.
  2. Package getPackage():返回已加载类的包名
  3. Class getSuperclass():返回已加载类的父类的 Class实例.
  4. Class [] getInterfaces():返回已加载类实现的接口的 Class对象数组.
  5. boolean isInterface():返回已加载类是否是接口

还有更多的反射方法大家可以去查看API


如果上面的内容有错误的地方或者讲的不好的地方,还请大家指点一下,我好及时修改。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值