Java反射机制

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

  • 在运行时分析类的能力
  • 在运行时查看对象
  • 实现通用的数组操作代码
  • 利用Method对象
  • 生成动态代理

Class类

程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。可以通过专门的Java类访问这些信息,保存这些信息的类被称为Class。

那么在Java中哪些类型可以有Class对象呢?

  • class:外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive type:基本数据类型
  • void

获取Class对象引用的三种方式

(1)Class.forName(类的全限定名)

(2)实例对象调用getClass方法

(3)类名.class

​ 一个Class对象就表示的是一个类型,而这个类型未必一定是一种类。例如基本数据类型int、short等不是一个类,但是int.class是一个Class类型的对象。没创建一个新的类,就会产生一个新的Class对象。

​ 虚拟机为每个类型管理一个Class对象,因此,可以利用“==”运算符实现两个Class对象比较的操作。如:

e.getClass() == Employee.class

​ 有一个很有用的方法newInstance()(该方法属于Class类),可以用来动态地创建一个类的实例。

e.getClass().newInstance()

​ 创建了一个与e具有相同类型的实例。newInstance方法调用默认的构造器(无参构造器)初始化新创建的对象,如果这个类没有无参构造器,就会抛出异常。所以,如果想要通过反射获取某个类的Class对象的引用,然后通过Class对象调用newInstance方法创建这个类的实例,那么前提条件是这个类必须含有无参构造器(显式地写出来)。

​ 使用Class.forName方法时,要进行异常的捕获处理,防止找不到指定的参数类。

​ 实际上,我们创建的每一个类也都是对象,即类本身是java.lang.Class类的实例对象。这个实例对象称之为类对象,也就是Class对象。

检查类的结构

​ 在java.lang.reflect包中有三个类Field、Method和Constructor分别用来描述类的域、方法和构造器。

​ Class类中的getFields、getMethods、getConstructors方法分别返回类提供的public域、方法以及构造器数组,如果此类继承于某个超类,同时也包括超类的public部分(公有成员)。

​ Class类中的getDeclareFields、getDeclareMethods、geDeclaretConstructors方法将返回类中声明的全部域、方法以及构造器数组,其中包括私有和受保护成员,但是不包括超类的成员。

​ 其中以上三个类都有一个getModifiers方法,该方法用于返回一个整型数值,用于描述当前的当前的数据域、方法或者构造器的修饰符,单单调用这个方法并不能清楚的知晓当前的数据域、方法或者构造器的修饰符是什么样的,需要将getModifiers方法的返回值与java.lang.reflect包中Modifier类的静态方法toString结合起来使用,便可获取到修饰符是什么样的。

​ 当然Modifier类中也有isPublic、isPrivate这样的静态方法,可以用于判断方法或者构造器或者数据域是否是public、private或者final。

​ 我们首先可以查看一下Modifier类的这些方法的源码,该方法会将getModifiers方法返回的整型数值与方法中的静态常量做位运算,然后返回布尔值,确定是不是对应类型的修饰符。

public static boolean isPublic(int mod) {
    return (mod & PUBLIC) != 0;
}

public static boolean isPrivate(int mod) {
    return (mod & PRIVATE) != 0;
}

public static boolean isProtected(int mod) {
    return (mod & PROTECTED) != 0;
}

public static boolean isStatic(int mod) {
    return (mod & STATIC) != 0;
}

​ 其中PUBLIC、PRIVATE等都是静态常量,其值都是十六进制的,如下图所示:

​ Modifier类的静态方法toString的源码如下,也是通过位运算返回对应位置的修饰符名称:

public static String toString(int mod) {
    StringBuilder sb = new StringBuilder();
    int len;

    if ((mod & PUBLIC) != 0)        sb.append("public ");
    if ((mod & PROTECTED) != 0)     sb.append("protected ");
    if ((mod & PRIVATE) != 0)       sb.append("private ");

    /* Canonical order */
    if ((mod & ABSTRACT) != 0)      sb.append("abstract ");
    if ((mod & STATIC) != 0)        sb.append("static ");
    if ((mod & FINAL) != 0)         sb.append("final ");
    if ((mod & TRANSIENT) != 0)     sb.append("transient ");
    if ((mod & VOLATILE) != 0)      sb.append("volatile ");
    if ((mod & SYNCHRONIZED) != 0)  sb.append("synchronized ");
    if ((mod & NATIVE) != 0)        sb.append("native ");
    if ((mod & STRICT) != 0)        sb.append("strictfp ");
    if ((mod & INTERFACE) != 0)     sb.append("interface ");

    if ((len = sb.length()) > 0)    /* trim trailing space */
        return sb.toString().substring(0, len-1);
    return "";
}

实际案例:

1、先创建一个User类,用于反射

package com.test2;

public class Users {

    private int id;
    private String name;
    private float amount;

    public Users(int id, String name, float amount) {
        this.id = id;
        this.name = name;
        this.amount = amount;
    }

    private Users(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Users{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", amount=" + amount +
                '}';
    }

    public Users() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getAmount() {
        return amount;
    }

    public void setAmount(float amount) {
        this.amount = amount;
    }

    public void printFlag(){
        System.out.println(this.name + "正在使用反射!");
    }

    public static void print(){
        System.out.println("lalalala");
    }
}

2、开始反射测试

Class类的主要方法:

  • getName():获得类的完整名字。
  • getFields():获得类及其超类的public类型的属性。
  • getDeclaredFields():获得当前类的所有属性。
  • getDeclaredField(String name):返回类中指定名称的域
  • getField(String name):返回指定名称的公有域
  • getMethods():获得类即超类的public类型的方法。
  • getDeclaredMethods():获得当前类的所有方法。
  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型的Class对象。
  • getConstrutors():获得类及其超类的public类型的构造方法。
  • getDeclaredConstructors() :获得当前类的所有构造器。
  • getConstrutor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型的Class对象。
  • newInstance():通过类的不带参数的构造方法(无参构造器)创建这个类的一个对象,类的Class对象调用。
  • forName(String className):返回参数类名指定的Class对象

Constructor类的主要方法:

  • int getModifiers() :获得构造器类的修饰符(用int表示)
  • String Modifier.toString(int mod) :以字符串输出修饰符
  • Class[] Constructor.getParameterTypes():获得构造器对象的所有参数组成的每个参数对应的Class对象数组
  • getName():返回一个用于描述构造器的字符串
  • Class getDeclaringClass():如果调用该方法的类或接口是另一个类的成员,则此方法返回该类的声明类的Class对象。否则,此方法返回null。

Method类的主要方法:

  • int getModifiers():获得方法类的修饰符(用int表示)
  • String Modifier.toString(int mod):以字符串输出修饰符
  • Class[] Method.getParameterTypes():获得方法类的参数Class类数组
  • Class<?> getReturnType():返回当前方法对象的返回值类型的Class对象
  • getName():返回一个用于描述方法的字符串
  • Class getDeclaringClass():如果调用该方法的类或接口是另一个类的成员,则此方法返回该类的声明类的Class对象。否则,此方法返回null。
  • Object invoke(Object obj, Object... args):调用利用反射获取得到的方法。第一个参数是要操作的对象;第二个参数是要调用的方法时应传入的实参。如果要调用的是一个静态方法,那么第一个参数可以写为null;如果方法没有参数,第二个参数也可以设置为null。

Field类的主要方法:

  • int getModifiers():获得域类的修饰符(用int表示)
  • String Modifier.toString(int mod):以字符串输出修饰符
  • getName():返回一个用于描述数据域的字符串
  • Class getDeclaringClass():如果调用该方法的类或接口是另一个类的成员,则此方法返回该类的声明类的Class对象。否则,此方法返回null
  • Object get(Object obj)():返回obj对象中用调用这个方法的Field表示的域值
  • void set(Object obj,Object newValue):用一个新值设置Obj对象中的Field对象表示的域

AccessibleObject类的方法:

​ 该类是Field、Method、Constructor类的公共超类。反射机制的默认行为受限于Java的访问控制,如果一个Java程序没有收到安全管理器的控制,就可以覆盖访问控制。AccessibleObject中的setAccessible方法就可以达到这个目的:

public void setAccessible(boolean flag)

​ 该方法用于为反射对象设置可访问标志,flag为true表示屏蔽Java语言的访问检查使得对象的私有属性和方法也可以被查询和设置。

(1)利用反射获取数据域

package com.test2;

import java.lang.reflect.*;

public class TestReflect {

    public static void main(String[] args) {

        try {

            Class clazz = Class.forName("com.test2.Users");
            
            //getFields返回的是参数类提供的公有域
            System.out.println("公有域的数量:"+clazz.getFields().length);
            //getDeclaredFields返回类中声明的全部域
            System.out.println("全部域的数量:" + clazz.getDeclaredFields().length);

            System.out.println("利用反射获取分析类的字段:.....");
            Field[] fields = clazz.getDeclaredFields();

            for (Field field : fields) {
                //获取当前这个数据域对象的名称,对应参数类中的成员属性名
                System.out.println("field.getName() = "+field.getName());
                //获取当前这个数据域对象所属类型的Class对象
                System.out.println("field.getType() = "+field.getType());
                int modifiers = field.getModifiers();
                System.out.println("field.getModifiers() = "+modifiers);
                System.out.println("当前字段的修饰符 = "+Modifier.toString(modifiers));
                System.out.println("************************************************");
            }
            // 通过反射给User类的私有域设置值
            Users userss = new Users();
            Field name = clazz.getDeclaredField("name");
            // 设置可以访问类的私有域
            name.setAccessible(true);
            // 为私有域设置值,set方法的第一个参数就是一个实例对象,即反射操作的类的对象;第二个参数是要给这个私有域设置的值
            name.set(userss,"fym");
            System.out.println("反射为私有域赋值成功:" + name.get(userss));
            
            userss.setAmount(102);
            Field amount = clazz.getDeclaredField("amount");
            amount.setAccessible(true);
            Float o = (Float) amount.get(userss);
            float aFloat = amount.getFloat(userss);
            System.out.println(o);
            System.out.println(aFloat);
            
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

(2)利用反射获取构造器

 		   System.out.println("利用反射获取分析类的构造器:.....");
            System.out.println("公有构造器(包括父类的公有构造器)的数量 = "+ clazz.getConstructors().length);
            System.out.println("当前类的全部构造器的数量(无父类的构造器) = " + clazz.getDeclaredConstructors().length);

            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
            for (Constructor declaredConstructor : declaredConstructors) {
                System.out.println("构造器:" + declaredConstructor);
                Class[] parameterTypes = declaredConstructor.getParameterTypes();
                for (Class parameterType : parameterTypes) {
                    System.out.println("构造器的参数类型:" + parameterType);
                }

                int modifiers = declaredConstructor.getModifiers();
                System.out.println("构造器的修饰符" + Modifier.toString(modifiers));
                System.out.println("************************************************");
            }

            // 获取一个私有的构造器对象,并用这个私有的构造器对象创建一个该类的实例
            Constructor privateCon = clazz.getDeclaredConstructor(int.class, String.class);
            System.out.println("获得的私有构造器对象为:" + privateCon);
            // 因为当前的构造器是私有的,所以不能直接访问,此处将这个私有的方法设置为可访问的
            // 反射机制的默认行为受限于Java的访问控制
            privateCon.setAccessible(true);

            // 根据这个私有的构造器对象创建一个类的对象
            Object user = privateCon.newInstance(1, "fym");
            System.out.println("通过反射获取的私有构造器对象创建的对象为:" + user);

输出结果为:

(3)利用反射获取方法

            //反射操作方法
            System.out.println("利用反射获取分析类的方法:.....");

            System.out.println("利用反射得到的公有方法数量(包括超类):" + clazz.getMethods().length);
            System.out.println("利用反射得到的当前类的所有的方法数量" + clazz.getDeclaredMethods().length);

            Method[] methods = clazz.getMethods();

            System.out.println("利用反射得到的所有公有方法(包括超类):");
            for (Method method : methods) {
                System.out.println("方法:" + method);
                System.out.println("方法名:" + method.getName());
                System.out.println("方法的参数类型:");
                for (Class<?> parameterType : method.getParameterTypes()) {
                    System.out.println(parameterType);
                }

                System.out.println("方法的返回值类型:" + method.getReturnType());
                System.out.println("方法的修饰符为:" + Modifier.toString(method.getModifiers()));
                System.out.println("************************************************");
            }

            System.out.println("利用反射得到的当前类的所有方法:");
            Method[] declaredMethods = clazz.getDeclaredMethods();

            for (Method declaredMethod : declaredMethods) {

                System.out.println("方法:" + declaredMethod);
                System.out.println("方法名:" + declaredMethod.getName());
                System.out.println("方法的参数类型:");
                for (Class<?> parameterType : declaredMethod.getParameterTypes()) {
                    System.out.println(parameterType);
                }

                System.out.println("方法的返回值类型:" + declaredMethod.getReturnType());
                System.out.println("方法的修饰符为:" + Modifier.toString(declaredMethod.getModifiers()));
                System.out.println("************************************************");
            }

            // 调用利用反射获取得到的方法
            // getDeclaredMethod方法第一个参数是方法名,第二个参数是方法的参数类型对应的Class对象
            Method setName = clazz.getDeclaredMethod("setName", String.class);
            // 调用获取的方法
            setName.invoke(userss,"pyx");
            System.out.println("成功调用利用反射获取的参数类中的方法,操作的结果为:" + userss.getName());

            // 调用利用反射获取的静态方法
            // 如果方法没有参数,写null即可
            Method print = clazz.getDeclaredMethod("print", null);
            // 第一个参数,对象类型;第二个参数是参数类型
            print.invoke(userss,null);
            // 因为方法是static静态的,所以为null可以
            print.invoke(null,null);

输出结果为:

利用反射获取分析类的方法:.....
利用反射得到的公有方法数量(包括超类):17
利用反射得到的当前类的所有的方法数量9

利用反射得到的所有公有方法(包括超类):
方法:public java.lang.String com.test2.Users.toString()
方法名:toString
方法的参数类型:
方法的返回值类型:class java.lang.String
方法的修饰符为:public
************************************************
方法:public java.lang.String com.test2.Users.getName()
方法名:getName
方法的参数类型:
方法的返回值类型:class java.lang.String
方法的修饰符为:public
************************************************
方法:public int com.test2.Users.getId()
方法名:getId
方法的参数类型:
方法的返回值类型:int
方法的修饰符为:public
************************************************
方法:public void com.test2.Users.setName(java.lang.String)
方法名:setName
方法的参数类型:
class java.lang.String
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public static void com.test2.Users.print()
方法名:print
方法的参数类型:
方法的返回值类型:void
方法的修饰符为:public static
************************************************
方法:public void com.test2.Users.printFlag()
方法名:printFlag
方法的参数类型:
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public void com.test2.Users.setAmount(float)
方法名:setAmount
方法的参数类型:
float
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public void com.test2.Users.setId(int)
方法名:setId
方法的参数类型:
int
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public float com.test2.Users.getAmount()
方法名:getAmount
方法的参数类型:
方法的返回值类型:float
方法的修饰符为:public
************************************************
方法:public final void java.lang.Object.wait() throws java.lang.InterruptedException
方法名:wait
方法的参数类型:
方法的返回值类型:void
方法的修饰符为:public final
************************************************
方法:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
方法名:wait
方法的参数类型:
long
int
方法的返回值类型:void
方法的修饰符为:public final
************************************************
方法:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
方法名:wait
方法的参数类型:
long
方法的返回值类型:void
方法的修饰符为:public final native
************************************************
方法:public boolean java.lang.Object.equals(java.lang.Object)
方法名:equals
方法的参数类型:
class java.lang.Object
方法的返回值类型:boolean
方法的修饰符为:public
************************************************
方法:public native int java.lang.Object.hashCode()
方法名:hashCode
方法的参数类型:
方法的返回值类型:int
方法的修饰符为:public native
************************************************
方法:public final native java.lang.Class java.lang.Object.getClass()
方法名:getClass
方法的参数类型:
方法的返回值类型:class java.lang.Class
方法的修饰符为:public final native
************************************************
方法:public final native void java.lang.Object.notify()
方法名:notify
方法的参数类型:
方法的返回值类型:void
方法的修饰符为:public final native
************************************************
方法:public final native void java.lang.Object.notifyAll()
方法名:notifyAll
方法的参数类型:
方法的返回值类型:void
方法的修饰符为:public final native
************************************************


利用反射得到的当前类的所有方法:
方法:public java.lang.String com.test2.Users.toString()
方法名:toString
方法的参数类型:
方法的返回值类型:class java.lang.String
方法的修饰符为:public
************************************************
方法:public java.lang.String com.test2.Users.getName()
方法名:getName
方法的参数类型:
方法的返回值类型:class java.lang.String
方法的修饰符为:public
************************************************
方法:public int com.test2.Users.getId()
方法名:getId
方法的参数类型:
方法的返回值类型:int
方法的修饰符为:public
************************************************
方法:public void com.test2.Users.setName(java.lang.String)
方法名:setName
方法的参数类型:
class java.lang.String
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public static void com.test2.Users.print()
方法名:print
方法的参数类型:
方法的返回值类型:void
方法的修饰符为:public static
************************************************
方法:public void com.test2.Users.printFlag()
方法名:printFlag
方法的参数类型:
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public void com.test2.Users.setAmount(float)
方法名:setAmount
方法的参数类型:
float
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public void com.test2.Users.setId(int)
方法名:setId
方法的参数类型:
int
方法的返回值类型:void
方法的修饰符为:public
************************************************
方法:public float com.test2.Users.getAmount()
方法名:getAmount
方法的参数类型:
方法的返回值类型:float
方法的修饰符为:public
************************************************
成功调用利用反射获取的参数类中的方法,操作的结果为:pyx
lalalala
lalalala

​ 可以看出,getMethods方法获取的是当前类以及这个类的超类的所有的公有方法;getDeclaredMethods方法获取的是当前类的所有方法,包括私有的和受保护的。

posted @ 2021-03-07 17:45  有心有梦  阅读(120)  评论(0编辑  收藏  举报