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

下面介绍下反射在Java项目中的使用

Class类的使用

概念理解

在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。

获取Class实例的方式

不能直接创建Class的实例对象,因为Class类的构造方法是私有的,只有jvm可以去创建。(我们没有创建Class对象的权限,只能有JVM在运行时创建出Class对象)

  • 利用对象调用getClass()方法获取该对象的Class实例;
  • 使用Class类的静态方法forName(),用类的名字获取一个Class实例,源码如下;
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException
{
    if (loader == null) {
        loader = BootClassLoader.getInstance();
    }
    Class<?> result;
    try {
        // 这个过程是通过JVM完成的,此处为Native方法
        result = classForName(name, initialize, loader);
    } catch (ClassNotFoundException e) {
        Throwable cause = e.getCause();
        if (cause instanceof LinkageError) {
            throw (LinkageError) cause;
        }
        throw e;
    }
    return result;
}
  • 运用.class的方式获取Class实例,对于基本数据类型的封装类,还可以采用TYPE来获取对应的基本数据类型的Class实例

综上所述,其实我们代码中创建的每一个类都是一个对象,只不过它是Class类的实例对象,这个对象我们称为该类的类类型。并且一个类只可能是Class类的一个实例对象,即获取的类类型是相同的

JVM创建Class对象的过程

  1. 源文件经过编译(javac.exe)以后,得到一个或多个.class文件。
  2. .class文件经过运行(java.exe)这步,就需要进行类的加载(通过JVM的类的加载器),加载到内存中的缓存。
  3. 每一个放入缓存中的.class文件就是一个Class的实例.

下面是获取Class对象的四种方法

public class ClazzDemo {

    public static void main(String[] args) {

        // 通过类名直接获取
        Class<ClazzDemo> clazz1 = ClazzDemo.class;

        // 通过对象的getClass()获取
        ClazzDemo demo = new ClazzDemo();
        Class<ClazzDemo> clazz2 = (Class<ClazzDemo>) demo.getClass();

        // 通过Class.forName()动态加载获取
        Class<ClazzDemo> clazz3 = null;
        try {
            clazz3 = (Class<ClazzDemo>) Class.forName("reflect.ClazzDemo");
        } catch (ClassNotFoundException ignored) {
        }

        // 通过类加载器进行获取
        ClassLoader classLoader = demo.getClass().getClassLoader();
        Class<ClazzDemo> clazz4 = null;
        try {
            clazz4 = (Class<ClazzDemo>) classLoader.loadClass("reflect.ClazzDemo");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("clazz1 " + clazz1.hashCode());
        System.out.println("clazz2 " + clazz2.hashCode());
        System.out.println("clazz3 " + clazz3.hashCode());
        System.out.println("clazz4 " + clazz4.hashCode());
        /*
          结果: hashcode 一样,为同一个对象
         clazz1 1956725890
         clazz2 1956725890
         clazz3 1956725890
         clazz4 1956725890
         */
    }
}

根据Class类型动态创建类

//~ClazzDemo中
private static void newInstanceTest() {
    // 通过类的类型创建实例对象
    ClazzDemo demo = null;
    Class<ClazzDemo> clazz = ClazzDemo.class;
    try {
        // 使用newInstance()动态创建, 调用无参的构造方法
        demo = clazz.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
    assert demo != null;
    demo.showMsg();


    /*
    结果
    clazzDemo is private , but we can contract this by reflect
    ClazzDemo #showMsg()
    */
}


// 反射也可以在类外部调用私有构造器
private ClazzDemo(){
    System.out.println("clazzDemo is private , but we can contract this by reflect");
}

private void showMsg() {
    System.out.println("ClazzDemo #showMsg()");
}

动态获取类的信息

/**
 * 动态获取Class类的信息
 */
private static void getClassInfo() {
    Class<ClazzDemo> clazz = ClazzDemo.class;
    // Get class name.
    System.out.println("Class name " + clazz.getName());

    // Get all declared methods.
    // getMethod()只能获取所有公共方法
    // getDeclaredMethods()获取所有声明的方法,包括私有
    Method[] methods = clazz.getDeclaredMethods();
    for (Method aMethod : methods) {
        System.out.println("Method: " + aMethod.getName());
        // 1. 获取注解
        Annotation[] annotations = aMethod.getDeclaredAnnotations();
        for (Annotation aAnnotation : annotations) {
            System.out.println("    Annotation:" + aAnnotation);
        }

        // 2.获取返回类型
        Class returnType = aMethod.getReturnType();
        System.out.println("return:" + returnType.getSimpleName());

        // 3. 获取参数类型
        Class[] params = aMethod.getParameterTypes();
        for (Class aParams : params) {
            System.out.println("    params: " + aParams.getSimpleName());
        }

        // 4. 获取异常类型
        Class[] expts = aMethod.getExceptionTypes();
        for (Class aExpts : expts) {
            System.out.println("    expt:" + aExpts.getSimpleName());
        }

        // 5. 获取声明关键字,public,protected,static等
        int modifier = aMethod.getModifiers();
        System.out.println("isPrivate: " + Modifier.isPrivate(modifier));
        System.out.println("isStatic: " + Modifier.isStatic(modifier));
        System.out.println("------------------------------------");
    }
}

运行结果

Class name reflect.ClazzDemo
Method: getClassInfo
return:void
isPrivate: true
isStatic: true
------------------------------------
Method: newInstanceTest
return:void
isPrivate: true
isStatic: true
------------------------------------
Method: showMsg
return:void
    expt:RuntimeException
isPrivate: true
isStatic: false
------------------------------------
Method: testMethod
return:void
isPrivate: false
isStatic: false
------------------------------------
Method: getClazzTest
return:void
isPrivate: true
isStatic: true
------------------------------------
Method: main
return:void
    params: String[]
isPrivate: false
isStatic: true
------------------------------------

获取声明的成员变量

/**
 * 获取所有声明的类变量
 */
private static void getClassField() {
    Class<ClazzDemo> clazz = ClazzDemo.class;
    Field[] fields = clazz.getDeclaredFields();
    for (Field aField : fields) {
        // 获取名称
        System.out.println(aField.getName());

        // 获取类型
        System.out.println(aField.getType().getSimpleName());

        // 获取声明关键字
        int modifder = aField.getModifiers();
        System.out.println("isFinal " + Modifier.isFinal(modifder));

        System.out.println("-------------------------------");
    }
}

运行结果

mPubField
String
isFinal false
-------------------------------
mProField
String
isFinal false
-------------------------------
mPriField
String
isFinal false
-------------------------------
$assertionsDisabled
boolean
isFinal true
-------------------------------

获取类的构造器

/**
 * 获取类的构造器
 */
private static void getClassConstrator() {
    Class<ClazzDemo> clazz = ClazzDemo.class;
    Constructor<ClazzDemo>[] constructors = (Constructor<ClazzDemo>[]) clazz.getDeclaredConstructors();
    for (Constructor<ClazzDemo> aConstructor : constructors) {
        System.out.println(aConstructor.getName());
        // 获取参数类型
        Class[] parameter = aConstructor.getParameterTypes();
        for (Class aParameter : parameter) {
            System.out.println("    " + aParameter.getName());
        }
        System.out.println("---------------------------------");
    }
}

运行结果

reflect.ClazzDemo
    java.lang.String
    java.lang.String
    java.lang.String
---------------------------------
reflect.ClazzDemo
---------------------------------

反射调用方法

使用setAccessible(true)取消安全检查,提高反射效率

AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。

将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问

由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的

/**
 * 反射调用某个方法
 */
private static void invokeClazzMethod() {
    Class<ClazzDemo> clazz = ClazzDemo.class;
    ClazzDemo demo = new ClazzDemo();
    try {
        // 使用反射找到某个方法,此处为无参方法
        // getMethod()只能调用public方法,使用getDeclaredMethod()可以调用任何方法
        Method method = clazz.getDeclaredMethod("showMsg");
        // 反射调用
        method.invoke(demo);

        // 调用有参的方法
        Method method1 = clazz.getDeclaredMethod("showMsg", String.class);
        method1.invoke(demo, "Hello World");


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

下面是重载的两个方法

private void showMsg() throws RuntimeException {
    System.out.println("ClazzDemo #showMsg()");
}

private void showMsg(String msg) {
    System.out.println(msg);

}

反射调用结果

ClazzDemo #showMsg()
Hello World

反射修改成员变量的值

public String mPubField;
protected String mProField;
private String mPriField;

/**
 * 修改成员变量的值
 */
private static void modifyFieldValue() {
    Class<ClazzDemo> clazz = ClazzDemo.class;
    ClazzDemo demo = new ClazzDemo();
    try {
        // 获取属性
        Field pub = clazz.getDeclaredField("mPubField");
        Field pro = clazz.getDeclaredField("mProField");
        Field pri = clazz.getDeclaredField("mPriField");

        // 取消Java运行时的安全检查,提高执行效率
        pub.setAccessible(true);
        pro.setAccessible(true);
        pri.setAccessible(true);

        // 动态修改这个对象的属性值
        pub.set(demo, "public_field");
        pro.set(demo, "protected_field");
        pri.set(demo, "private_field");

        System.out.println(demo.mPubField);
        System.out.println(demo.mProField);
        System.out.println(demo.mPriField);

    } catch (Exception e) {
        e.printStackTrace();
    }
}
/*运行结果
  public_field
  protected_field
  private_field
  */

通过反射修改final修饰的成员变量

通过反射修改final修饰的成员变量需要分两种情况进行考虑.

  1. 当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了。
  2. 当final修饰的成员变量在定义的时候并没有初始化值的话,那么就还能通过java反射机制来动态修改它的值。

原因是编译期间final类型的数据自动被优化了,即:所有用到该变量的地方都被替换成了常量。所以我们就无法通过反射修改属性值了

private static void modifyFinalFieldValue() {
    Class<Target> clazz = Target.class;
    Target target = Target.newInstance();
    // 修改两个属性值
    try {
        final Field field1 = clazz.getDeclaredField("canModify");
        final Field field2 = clazz.getDeclaredField("cannotModify");
        final Field field3 = clazz.getDeclaredField("CONSTANT");
        /**
         * 修改static final修饰常量,需要去掉final的影响
         * 修改这些变量由于JVM的优化特性可能不成功, 如int,String,在JVM运行时会将这些值优化成常量
         * 这里使用Object作为演示,JVM不会优化这个变量,所以能够成功
         */
        final Field modifiers = Field.class.getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.set(field3, field3.getModifiers() & ~Modifier.FINAL);

        // 通过setAccessible(true)提高性能,否则修改私有的属性会出现IllegalAccessException
        field1.setAccessible(true);
        field2.setAccessible(true);
        field3.setAccessible(true);

        // 使用反射修改两个final成员变量
        field1.set(target, "changed_value");
        field2.set(target, "changed_value");
        field3.set(target, "changed_value");

        System.out.println(target.getCanModify());
        System.out.println(target.getCannotModify());
        System.out.println(Target.getCONSTANT());
        /*结果, 第一个修改了,第二个没有修改
          changed_value
          original_string
          changed_value
         */
    } catch (NoSuchFieldException | IllegalAccessException e) {
        e.printStackTrace();
    }
}

反射使用的Target对象

public class Target {

    /***
     * 修改static final字段
     */
    private static final Object CONSTANT = "original_constant";

    // 反射修改用`final`修饰的成员变量
    // 如果在类定义时声明,则不能通过反射修改
    private final String cannotModify = "original_string";
    // 在构造器中进行定义的成员变量,能够通过反射修改
    private final String canModify;

    public static Target newInstance() {
        return new Target("original_string");

    }

    private Target(String canModify) {
        // 此处的变量在构造器中初始化, 可以通过反射进行修改
        this.canModify = canModify;
        System.out.println("私有构造器执行了");
    }

    public String getCannotModify() {
        return cannotModify;
    }

    public String getCanModify() {
        return canModify;
    }

    public static Object getCONSTANT() {
        return CONSTANT;
    }
}

反射调用构造器

/**
 * 通过反射调用构造器
 */
private static void invokeConstructor() {
    Constructor constructor = null;
    try {
        constructor = Target.class.getDeclaredConstructor(String.class);
        constructor.setAccessible(true);
        Target target = (Target) constructor.newInstance("123");
        System.out.println(target.getCanModify());
        //结果
        // 私有构造器执行了
        // 123
    } catch (Exception e) {
        e.printStackTrace();
    }
}

通过反射破坏泛型

public static void main(String[] args) {
   ArrayList list = new ArrayList();
   ArrayList<String> list1 = new ArrayList<String>();
   list1.add("hello");
   //list1.add(20);编译错误
   Class c1 = list.getClass();
   Class c2 = list1.getClass();
   System.out.println(c1 == c2);
   //反射的操作都是编译之后的操作

   try {
       // 编译后泛型会被擦除成边界,这是通过add方法可以添加任何类型的对象了
       Method m = c2.getMethod("add",Object.class);
       m.invoke(list1,20);//绕过编译操作就绕过了泛型
       System.out.println(list1.size());
       System.out.println(list1);
   } catch (Exception e) {
       e.printStackTrace();
   }

}