java 反射的理解使用

231 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

反射(Reflection)的定义

学习反射之前首先知道两个概念:

**编译期:**把代码交给编译器编程成计算机可以执行的文件的过程。在java里就是把java代码编译成class文件的过程。编译期可以简单理解为未翻译,把人能看懂的东西翻译成jvm虚拟机可以看懂的东西;

运行期: 把编译后的文件交给计算机执行,直到程序运行结束。

java反射机制 是指在运行期过程中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意方法和属性;

其实java本身的“封装”特性是让外部不要访问私有类或者私有属性,但是反射机制就违背了这一原则;其实这就是一个工具,工具就看怎么用了

反射使用的API

  • Field类: 提供有关类的属性信息,以及对它的动态访问权限,一个封装反射类的属性的类
  • Method类: 提供类的方法的信息,包括抽象方法,一个用来封装反射类方法的一个类
  • Class类: 正在运行的java应用程序中的类的实例
  • Constructor类:获取有关类的构造方法的信息

实例演示

基础User对象类

public class User {

    /**
     * 用户姓名
     */
    private String userName;


    /**
     * 用户密码
     */
    private String password;

    /**
     * 用户年龄
     */
    public int age;

    public User(String userName, String password, int age) {
        this.userName = userName;
        this.password = password;
        this.age = age;
    }


    public User(){

    }


    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private String run(String param){
        System.out.println("这是一个私有函数");
        return "runrun";
    }
}

获取Class对象的三种方式


public static void main(String[] args) {
    try {
        //1、通过完整路径名获取class对象
        Class userClass1 = Class.forName("com.xxx.yyy.core.model.policy.User");
        System.out.println(userClass1);

        //2、通过类的class属性
        Class userClass2 = User.class;
        System.out.println(userClass2);

        //3、通过对象的getClass方法
        User user = new User();
        Class userClass3 = user.getClass();
        System.out.println(userClass3);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

获取成员变量属性


public static void main(String[] args) {
    try {
        //通过完整路径名获取class对象
        Class userClass1 = Class.forName("com.xxx.yyy.core.model.policy.User");

        //获取字段有两个API方法:getDeclaredFields  和 getFields.
         //getDeclaredFields() 获取所有声明的字段(公有and 私有)
         //getFields()仅获取公有字段
        Field[] fields = userClass1.getDeclaredFields();
        for (Field field : fields){
            System.out.println("结果字段:"+field);
        }


    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

输出结果

结果字段:private java.lang.String com.xxx.yyy.core.model.policy.User.userName
结果字段:private java.lang.String com.xxx.yyy.core.model.policy.User.password
结果字段:public int com.xxx.yyy.core.model.policy.User.age

获取构造方法


public static void main(String[] args) {
    try {
        //通过完整路径名获取class对象
        Class userClass1 = Class.forName("com.xxx.yyy.core.model.policy.User");
        //获取构造方法同样包含了两个 API:用于获取所有构造方法的 getDeclaredConstructors和用于获取公有构造方法的getConstructors
       Constructor[] constructors =  userClass1.getDeclaredConstructors();
       for (Constructor constructor : constructors){
           System.out.println("构造方法:"+ constructor);
       }


    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

输出结果

构造方法:public com.xxx.yyy.core.model.policy.User(java.lang.String,java.lang.String,int)
构造方法:public com.xxx.yyy.core.model.policy.User()

获取成员方法

 

public static void main(String[] args) {
    try {
        //通过完整路径名获取class对象
        Class userClass1 = Class.forName("com.xxx.yyy.core.model.policy.User");
        //获取成员方法的两个 API 是:获取所有声明的非构造函数的 getDeclaredMethods 和仅获取公有非构造函数的 getMethods:
        Method[] methods = userClass1.getDeclaredMethods();

        for (Method method : methods){
            System.out.println("获取的成员方法:"+ method.getName());
        }


    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

输出结果

获取的成员方法:run
获取的成员方法:setAge
获取的成员方法:setUserName
获取的成员方法:getAge
获取的成员方法:setPassword
获取的成员方法:getUserName
获取的成员方法:getPassword

实践、利用反射获取属性字段并赋值调用方法等


public static void main(String[] args) {
    try {
        //通过完整路径名获取class对象
        Class userClass1 = Class.forName("com.alipay.mrcher.core.model.policy.User");


        Constructor constructor = userClass1.getConstructor(String.class,String.class,int.class);
        constructor.setAccessible(true);//私有方法可使用
        //利用方法的newInstance方法创建对象,传入构造方法所需要的参数,

        Object user  = constructor.newInstance("xq","xq",19);
        //获取字段 并赋值
        Field nameField = userClass1.getDeclaredField("userName");
        nameField.setAccessible(true);
        nameField.set(user,"修改后的name");
        System.out.println("反射修改后的name值:"+ nameField.get(user));

        //使用invoke方法调用番薯
        Method runMethod = userClass1.getDeclaredMethod("run",String.class);
        runMethod.setAccessible(true);
        Object result = runMethod.invoke(user,"invoke参数");
        System.out.println(result);


    } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }

}

其他使用方法

boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型 
boolean isArray = class1.isArray();//判断是否是集合类
 boolean isAnnotation = class1.isAnnotation();//判断是否是注解类 
boolean isInterface = class1.isInterface();//判断是否是接口类 
boolean isEnum = class1.isEnum();//判断是否是枚举类 
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类 
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰 
String className = class1.getName();//获取class名字 包含包名路径 
Package aPackage = class1.getPackage();//获取class的包信息 
String simpleName = class1.getSimpleName();//获取class类名 
int modifiers = class1.getModifiers();//获取class访问权限 
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类 
Class<?> declaringClass = class1.getDeclaringClass();//外部类

getSuperclass():获取某类的父类  
getInterfaces():获取某类实现的接口

总结

反射 在逆向代码 、与注解相结合、单纯的反射框架(如eventbus),动态生成类等,动态赋值等,都有很方便的使用性;