[Java反射]初始反射,简单对反射认识和应用

103 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

1. 反射

1.1. 反射介绍

1.1.1. 什么是反射?

Reflection(反射)是java被视为动态语言的关键,反射机制允许程序在执行期借助与Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

1.1.2. 反射优点和缺点

优点: 可以实现动态创建对象和编译,体现出很大的灵活性

缺点: 让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。

我们先定义一个TargetObject给我们操作:

public class TargetObject {
    private String value;
​
    public TargetObject() {
        value = "JavaGuide";
    }
​
    public void publicMethod(String s) {
        System.out.println("I love " + s);
    }
​
    private void privateMethod() {
        System.out.println("value is " + value);
    }
}

1.2. 获取 Class 对象的四种方式

1. 知道具体类的情况下可以使用:

Class<TargetObject> targetObjectClass = TargetObject.class;
String name = targetObjectClass.getName();
System.out.println(name);
// 结果:
// com.he.interview.pojo.TargetObject

但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化

2. 通过 Class.forName()传入类的全路径获取:

Class targetObject = Class.forName("com.he.interview.pojo.TargetObject");
System.out.println(targetObject);
// 结果:
// class com.he.interview.pojo.TargetObject

3. 通过对象实例instance.getClass()获取:

TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();

4. 通过类加载器xxxClassLoader.loadClass()传入类路径获取:

Class targetObject4Class = ClassLoader.getSystemClassLoader().loadClass("com.he.interview.pojo.TargetObject");
System.out.println(targetObject4Class);
// 结果:
// class com.he.interview.pojo.TargetObject

通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行

示例:

public class 获取Class对象的四种方式 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 1. 知道具体类的情况下可以使用
        Class<TargetObject> targetObjectClass = TargetObject.class;
        String name = targetObjectClass.getName();
        System.out.println(name);
​
        // 2. 通过 Class.forName()传入类的全路径获取:
        Class targetObject = Class.forName("com.he.interview.pojo.TargetObject");
        System.out.println(targetObject);
​
        // 3. 通过对象实例instance.getClass()获取:
        TargetObject targetObject3 = new TargetObject();
        Class<? extends TargetObject> targetObject3Class = targetObject3.getClass();
​
        // 4. 通过类加载器xxxClassLoader.loadClass()传入类路径获取:
        Class targetObject4Class = ClassLoader.getSystemClassLoader().loadClass("com.he.interview.pojo.TargetObject");
        System.out.println(targetObject4Class);
    }
}

1.3. 哪些类型可以有Class对象

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
  • interface:接口
  • []: 数组
  • enum: 枚举
  • annotation:注解@interface
  • primtitve Type:基本数据类型
  • void:返回值
package com.he.interview.reflection;
​
import java.lang.annotation.ElementType;
​
public class 哪些类型可以有Class对象 {
    public static void main(String[] args) {
        Class c1 = Object.class;     //类
        Class c2 = Comparable.class; //接口
        Class c3 = String[].class;   //一维数组
        Class c4 = int[][].class;    //二维数组
        Class c5 = Override.class;   //注解
        Class c6 = ElementType.class;//enum枚举
        Class c7 = Integer.class;    //基本类型
        Class c8 = void.class;       //返回值类型
        System.out.println("类" + c1);
        System.out.println("接口" + c2);
        System.out.println("一维数组" + c3);
        System.out.println("二维数组" + c4);
        System.out.println("注解" + c5);
        System.out.println("enum枚举" + c6);
        System.out.println("基本类型" + c7);
        System.out.println("返回值类型" + c8);
        //只要元素类型与维度一样,就是同一个class对象
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }
}
​

输出结果:

class java.lang.Object
接口interface java.lang.Comparable
一维数组class [Ljava.lang.String;
二维数组class [[I
注解interface java.lang.Override
enum枚举class java.lang.annotation.ElementType
基本类型class java.lang.Integer
返回值类型void
1163157884
1163157884

1.4. 使用反射操作这个类的方法以及参数

public class 使用反射操作这个类的方法以及参数 {
    public static void main(String[] args) throws Exception {
        // 获取 TargetClass 类的 Class 对象并且创建 TargetClass类实例
        Class<?> targetClass = Class.forName("com.he.interview.pojo.TargetObject");
        TargetObject targetObject = (TargetObject) targetClass.newInstance();
​
        // 获取 TargetObject类定义的所有方法(包括private等)
        Method[] declaredMethods = targetClass.getDeclaredMethods();
        System.out.println("获得的类的方法有:");
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod.getName());
        }
        // 获得指定方法并调用
        Method publicMethod = targetClass.getDeclaredMethod("publicMethod", String.class);
        publicMethod.invoke(targetObject, "正在调用"); // 第一个参数是类的实例对象
​
        // 获取类的指定参数并进行修改
        Field field = targetClass.getDeclaredField("value");
        System.out.println("输出获取类的指定参数:" + field);
        //为了对类中的参数进行修改我们取消安全检查
        field.setAccessible(true);
        field.set(targetObject, "heriec");
        System.out.println("指定参数的值为:" + field.get(targetObject));
        // 调用 private 方法
        Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
        // 为调用 private方法我们取消安全检查
        privateMethod.setAccessible(true);
        privateMethod.invoke(targetObject);
    }
}

输出结果:

获得的类的方法有:
publicMethod
privateMethod
正在调用publicMethod方法
输出获取类的指定参数:private java.lang.String com.he.interview.pojo.TargetObject.value
指定参数的值为:heriec
value is heriec