Java反射使用详解(java.lang.reflect)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

java的反射库(reflection library)提供了非常丰富且精心设计的工具及,以便能动态的操纵java代码。我们将从以下几个方面来解析反射机制的使用。


一、Class类

程序运行期间,java运行时系统始终为所有对象维护一个称为运行时的类型标识。这个信息跟踪这每个对象所属的类。虚拟机利用郧西是类型信息选择相应的方法执行。这个类就是Class(注意首字母大写),每一个Class实例都对应一种类(基本数据类型除外int.class是一个Class对象,但是int不是类)。我们后续对反射的应用都是基于Class类,先让我们来看看如何获取一个类的Class实例:
获得Class实例的方法有三种分别是:object.getClass();Class.forName();Object.class();
代码如下(示例):

public class Test {
    public static void main(String[] args) throws Exception{
    	Employee e = new Employee();
    	//getName获取类名
        System.out.println("e.getClass() : " + e.getClass().getName());
        System.out.println("Class.forName(\"org.Employee\") : "+Class.forName("org.Employee").getName());
        System.out.println("Employee.class : "+Employee.class.getName());
        //判断是否是同一个类
        System.err.println(e.getClass() == Class.forName("org.Employee"));
        System.err.println(e.getClass() == Employee.class);
    }
}
运行结果
e.getClass() : org.Employee
Class.forName("org.Employee") : org.Employee
Employee.class : org.Employee
true
true
class Employee {
    public String name;
    private double salary;
    private Date hireDay;

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

    public Employee() {}
    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Date getHireDay() {
        return hireDay;
    }

    public void setHireDay(Date hireDay) {
        this.hireDay = hireDay;
    }
}

二、用法:检查类结构

在java.lang.relect包中有三个类:Field、Method、Constructor分别用来描述类的域,方法和构造器。java.lang.relect中还有一个Modifier类,用来描述域,方法,构造器的访问权限修饰符的使用。下面我们分别举例如何来获得一个Class实例中Field、Method、Constructor,我们先给出代码,然后举例进行验证。

1.Constructor

我们定义一个方法,只需要传入一个Class实例,就可以获得Class实例对应的类的构造器。

public static void printConstructors(Class c){
        //显示所有构造器
        Constructor[] constructors = c.getDeclaredConstructors();
        //该方法只显示public类型的构造器
        //Constructor[] constructors = c.getConstructors();
        for (Constructor constructor : constructors){
            String name = constructor.getName();
            System.out.print(" ");
            String modifies = Modifier.toString(constructor.getModifiers());
            //打印访问权限修饰符
            if (modifies.length() > 0) System.out.print(modifies + " ");
            //打印构造器名
            System.out.print(name + "(");
            //打印形参列表
            Class[] paramTypes = constructor.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++){
                if (j > 0) System.out.print(", ");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

2.Method

同上面一样,我们给出一个方法只需要传入一个Class实例,就可以获得Class实例对应的类中的所有方法。

public static void printMethods(Class c){
        Method[] methods = c.getDeclaredMethods();
        //Method[] methods = c.getMethods();
        for (Method method : methods){
            //获得返回值类型
            Class retType = method.getReturnType();
            //获得方法名
            String name = method.getName();
            System.out.print(" ");
            String modifies = Modifier.toString(method.getModifiers());
            if (modifies.length() > 0) System.out.print(modifies + " ");
            System.out.print(retType.getName() + " " + name + "(");
            //打印形参列表
            Class[] paramTypes = method.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++){
                if (j > 0) System.out.print(", ");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

3.Field

老规矩,我们给出一个方法只需要传入一个Class实例,就可以获得Class实例对应的类中的变量。

public static void printFields(Class c){
        Field[] fields = c.getDeclaredFields();
        //Field[] fields = c.getFields();
        for (Field field : fields){
            //变量的Class
            Class type = field.getType();
            //变量的名字
            String name = field.getName();
            System.out.print(" ");
            //访问权限修饰符
            String modifies = Modifier.toString(field.getModifiers());
            if (modifies.length() > 0) System.out.print(modifies + " ");
            System.out.print(type.getName() + " " + name + ";");

        }
    }

测试代码如下

public class Test {
    public static void main(String[] args) throws Exception{
        //获得Class实例
        Class employer = Class.forName("org.Employee");
        printConstructors(employer);
        printMethods(employer);
        printFields(employer);
    }
}
运行结果
 public org.Employee(java.lang.String, double, java.util.Date);
 public org.Employee();
 public double getSalary();
 public void setSalary(double);
 public void setHireDay(java.util.Date);
 public java.util.Date getHireDay();
 public java.lang.String name; private double salary; private java.util.Date hireDay;

就这样,我们拿到了这个类的所有结构!!!

用法:运行时分析对象

上一节我们拿到了一个类的结构,当一个类实例化的时候,我们希望能更进一步的拿到这个类的数据域的实际内容。

1.分析对象工具类

class ObjectAnalyzer{
    private ArrayList<Object> visited = new ArrayList<>();

    //这里我们定一个toString方法,注意这里不是重写!!
    public String toString(Object obj) {
        if (obj == null) return "null";
        if(visited.contains(obj)) return "...";
        visited.add(obj);
        Class cl = obj.getClass();
        if (cl == String.class) return (String) obj;
        if (cl.isArray()){
            String r = cl.getComponentType() + "[]{";
            for (int i = 0; i < Array.getLength(obj); i++){
                if (i > 0) r+="";
                Object val = Array.get(obj, i);
                if (cl.getComponentType().isPrimitive()) r+=val;
                else r += toString(val);
            }
            return  r+"}";
        }

        String r = cl.getName();
        do {
            r+="[";
            //获取所有变量
            Field[] fields = cl.getDeclaredFields();
            //屏蔽访问权限修饰符,可以对任意权限的进行访问
            AccessibleObject.setAccessible(fields,true);
            for (Field field : fields){
                if (!Modifier.isStatic(field.getModifiers())){
                    if (!r.endsWith("[")) r+=",";
                    r+=field.getName() + "=";
                    try {
                    	//获取变量类型
                        Class t =field.getType();
                        //获取变量值
                        Object val = field.get(obj);
                        if (t.isPrimitive()) r+= val;
                        else r+=toString(val);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
            r+="]";
            cl = cl.getSuperclass();
        }while (cl != null);
        return r;
    }
}

2.测试用例

public class Test {
    public static void main(String[] args) throws Exception{
        Employee harry = new Employee("张三", 4000.0,new Date());
        System.out.println(new ObjectAnalyzer().toString(harry));
    }
运行结果
org.Employee[name=张三,salary=4000.0,hireDay=java.util.Date[fastTime=1629602062416,cdate=null][]][]

就这么简单,我们通过Field中的get()方法可以获取到实际实例中的变量值,如果变量是private修饰,我们在获取过程中或出现访问权限异常,这时候我需要使用setAccessible()方法屏蔽Java语言的访问检查。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值