Java反射学习记录

反射学习记录,欢迎纠错o(╥﹏╥)o

什么是反射(Reflection)

在程序的运行过程中,可以获取任何一个已加载的Class的内部信息,并且可以动态实例化它。


反射有什么用

提高程序的灵活性;(动态代理)

实现hook;(android 插件化、热修复等)

反射可以说是框架设计的灵魂。


反射怎么用

我们定义的每一个类文件,最终都会被类加载器加载为一个对象(java.lang.Class类对象)。这个Class类对象保存了这个类的信息(Class、Constructor、Field、Method、Type等),通过拿到这个类对象便能够获取其信息。


Class
//获取Class类对象有三种方式。

//方式1:通过实例对象获取
A a1 = new A("a1", 1);
a1.print();
Class class1 = a1.getClass();

//方式2:通过 类.class 获取
Class<A> class2 = A.class;

//方式3:通过 类名查找 获取
Class class3 = Class.forName("com.llk.kt.Test$A"); 

Constructor
public class Test {
	static class A{
        public String s;
        private int i;

        public A(String s, int i){
            this.s = s;
            this.i = i;
        }

        public void print(){ System.out.println("A s=" + s + " i=" + i); }
    }
  
  class B{
        public String s;

        public B(String s){ this.s = s; }

        public void print(){ System.out.println("B s=" + s); }
    }
}
静态内部类、外部类的实例化
Class clazz = Class.forName("com.llk.kt.Test$A");
Constructor constructor = clazz.getConstructor(String.class, int.class); //获取构造方法,getConstructor需要指定该构造方法的传参的Class对象
A a3 = (A) constructor.newInstance("reflectionA", 10086); //通过构造方法,实例化对象。
a3.print();
非静态内部类的实例化
//先获取外部类对象
Class clazz = Class.forName("com.llk.kt.Test");
Constructor constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
//非静态内部类,是需要传入外部类对象(非静态内部类默认持有外部类对象!!!)
Class cB = Class.forName("com.llk.kt.Test$B");
Constructor constructorB = cB.getConstructor(obj.getClass(), String.class);
B b = (B) constructorB.newInstance(obj, "reflectionB");
b.print();

Field
public class D {
    public String s;
    protected float f;
    private int i;
    public static double d = 1.99d;
  	public final static String g = "Good";

    public D(String s, float f, int i){
        this.s = s;
        this.f = f;
        this.i = i;
    }

    public void print(){
        System.out.println("D s=" + s + " f=" + f + " i=" + i + " d=" + d);
    }
}

Class clazz = Class.forName("com.llk.kt.D");
Constructor constructor = clazz.getConstructor(String.class, float.class, int.class);
Object d = constructor.newInstance("reflectionD", 1.0f, 1);

//获取该类以及父类的所有变量
Field[] allField = clazz.getFields();
System.out.println(Arrays.toString(allField));

//获取该类的所有变量
Field[] allDeclaredField = clazz.getDeclaredFields();
System.out.println(Arrays.toString(allDeclaredField));
反射public属性
//public String s;
Field sF = clazz.getDeclaredField("s");
String s = (String) sF.get(d); //获取属性值
System.out.println("sF content =" + s);
sF.set(d, "llk"); //设置属性值
反射protected属性
//protected float f;
Field fF = clazz.getDeclaredField("f");
float f = (float) fF.get(d); //获取属性值
System.out.println("fF content =" + f);
fF.set(d, 99.99f); //设置属性值
反射private属性
//private int i;
Field iF = clazz.getDeclaredField("i");
iF.setAccessible(true); //私有必须加上
int i = (int) iF.get(d); //获取属性值
System.out.println("iF content =" + i);
iF.set(d, 69); //设置属性值
反射静态变属性
//反射静态变量 - 跟普通变量使用一致
//反射 private static double d = 1.99d;
Field dF = clazz.getDeclaredField("d");
double dou = (double) dF.get(null); //无需对象
System.out.println("dF content =" + dou);
dF.set(null, 88.88); //设置属性值,无需对象
反射常量属性
//反射 public final static String g = "Good";
Field gF = clazz.getDeclaredField("g");
String g = (String) gF.get(d);
System.out.println("gF content =" + g);
gF.set(d, "Bad"); //报错-> Can not set static final java.lang.String field

Method
public class C{
    public String s;
    private int i;

    public C(String s, int i){
        this.s = s;
        this.i = i;
    }

    public void print(){ System.out.println("C === print s=" + s + " i=" + i); }

    protected String get(){
        System.out.println("C === get");
        return s;
    }

    private void set(String s, int i){
        System.out.println("C === set s=" + s + " i=" + i);
        this.s = s;
        this.i = i;
    }

    private static int getInt(){ return 1688; }
}

Class clazz = Class.forName("com.llk.kt.C");
Constructor constructor = clazz.getConstructor(String.class, int.class);
Object c = constructor.newInstance("reflectionC", 1);

//获取该类以及父类的所有方法
Method[] allMethod = clazz.getMethods();
System.out.println(Arrays.toString(allMethod));

//获取该类的所有方法
Method[] declaredMethod = clazz.getDeclaredMethods();
System.out.println(Arrays.toString(declaredMethod));
反射public方法
//public void com.llk.kt.C.print()
Method printMethod = clazz.getDeclaredMethod("print");
printMethod.invoke(c);
反射private方法
//private void com.llk.kt.C.set(java.lang.String,int)
Method setMethod = clazz.getDeclaredMethod("set", String.class, int.class);
setMethod.setAccessible(true); //私有必须加上
setMethod.invoke(c, "Goodbye", 9527);
反射protected方法
//protected java.lang.String com.llk.kt.C.get()
Method getMethod = clazz.getDeclaredMethod("get");
Object returnObj = getMethod.invoke(c);
System.out.println("returnObj = " + returnObj);
反射静态方法
//private static int com.llk.kt.C.getInt()
Method getIntMethod = clazz.getDeclaredMethod("getInt");
getIntMethod.setAccessible(true); //私有必须加上
Object rObj = getIntMethod.invoke(null);
System.out.println("rObj = " + rObj);

Type接口

从Java1.5开始,Java提供了Type接口。Type是所有类型的父接口。

子接口有 ParameterizedType, TypeVariable, GenericArrayType, WildcardType, 实现类有Class。


Type有什么用

Java在1.5引入了泛型,泛型的引入使得一部分错误可以在编译时期被提前发现,极大地增强了代码的健壮性。但是Java的泛型是伪泛型,泛型在运行期会被擦除的。那么我们怎么样才能在运行期得到这些泛型信息呢?这就需要用到Type了。


Type的子接口
ParameterizedType(参数化类型)
class A{}

//这些都是ParameterizedType 带有<>的类型都是
Map<String, A> map;
List<String> list;
Class<?> clz;
A<String> a;

//不属于ParameterizedType
Map map;
List list;

//ParameterizedType主要Api:
//返回该Type类型的参数的实际类型数组。如Map<String, A>返回的是[String, A]
Type[] getActualTypeArguments(); 
//返回该Type。如Map<String, A>返回的是Map
Type getRawType();
//返回外部类的Type。如Map<String, A>返回的是null。Map.Entry<String, A>返回的是Map
Type getOwnerType();

TypeVariable(类型变量)
//泛型类A中的T就属于TypeVariable
class A<T extends String>{
  T tt;
  
  // 不属于 TypeTypeVariable
	T[] values;
	String str;
	List<T> list;
}

//TypeVariable主要Api:
//返回边界数组,如tt返回[String]。如果没有指定的话是Object
Type[] getBounds(); 
//返回声明这Type所在的类的Type,如tt返回A
D getGenericDeclaration();
//返回的是这个type的名称,如tt返回tt
String getName();

GenericArrayType(泛型数组)
//GenericArrayType表示一种元素类型是ParameterizedType或者TypeVariable的数组类型
//例如
List<String>[] list
T[] array
  
//不属于GenericArrayType
List<String> list;
String[] strings;
A[] as;

//GenericArrayType主要Api:
//返回泛型数组中元素的组成部分的Type类型。如T[] array 返回T
Type getGenericComponentType();

WildcardType(泛型的通配符(?)类型)
//extends指定上边界,没有指定上边界默认Object。super指定下边界,没有指定的话为null。

//WildcardType主要Api:
Type[] getLowerBounds() //得到上边界Type数组
Type[] getUpperBounds() //得到下边界Type数组

Type的应用
class A<T>{
		protected T t;
  
		public A(T t){ this.t = t; }
  
		public T get(){ return t; }
}

class B extends A<String>{
		public B(String s) { super(s); }
}

class C<T> extends A<T>{
		public C(T t) { super(t); }
  
  	public T get(){ return t; }
}

反射实例化泛型类
//泛型参数如果没有上界、下界的话,默认是Object
Class clazz = Class.forName("com.llk.kt.C");
Object obj = clazz.getConstructor(Object.class).newInstance(9527);
Object returnObj = clazz.getDeclaredMethod("get").invoke(obj);
System.out.println("===== " + returnObj.getClass() + " ===== " + returnObj); //输出 -> ===== class java.lang.Integer ===== 9527

通过Type获取父类的泛型参数信息
Type cType = clazz.getGenericSuperclass(); //获取带泛型的父类
System.out.println("C type=" + cType); //输出 -> C type=com.llk.kt.A<T>
if(cType instanceof ParameterizedType){ 
		ParameterizedType type = (ParameterizedType) cType;
		Type[] tArgs = type.getActualTypeArguments(); //获取实际类型参数
		System.out.println("C types=" + Arrays.toString(tArgs)); //输出 -> C types=[T]
}

Class b_clazz = Class.forName("com.llk.kt.B");
Type bType = b_clazz.getGenericSuperclass();
System.out.println("B type=" + bType); //输出 -> B type=com.llk.kt.A<java.lang.String>
if(bType instanceof ParameterizedType){
		ParameterizedType type = (ParameterizedType) bType;
		Type[] tArgs = type.getActualTypeArguments();
		System.out.println("B types=" + Arrays.toString(tArgs)); //输出 -> B types=[class java.lang.String]
}else {
		System.out.println("不是ParameterizedType");
}

Class是只能通过getGenericSuperclass获取到父类的泛型信息。


怎么获取当前类的泛型实参?

匿名类,通过{}语法糖创建匿名类(能够准确获取到泛型的实参)

A a = new A<String>("aa"){};
Type type = a.getClass().getGenericSuperclass();
System.out.println("A type=" + type); //输出 -> A type=com.llk.kt.A<java.lang.String>
//输出 -> A types=[class java.lang.String]
if(type instanceof ParameterizedType){
		ParameterizedType t = (ParameterizedType) type;
		Type[] tArgs = t.getActualTypeArguments();
		System.out.println("A types=" + Arrays.toString(tArgs));
}else {
		System.out.println("不是ParameterizedType");
}

通过获取当前类带有泛型的Field、Method是无法获取泛型的实参类型的

Field的getGenericType(获取自己带泛型信息的类型)

Class clazz = Class.forName("com.llk.kt.A");
Field field = clazz.getDeclaredField("t");
Type type = field.getGenericType(); //getType都是也只是Object
System.out.println("Field Type=" + type); //输出 -> Field Type=T

A<String> a = new A<>("aa");
Field field = a.getClass().getDeclaredField("t");
Type type = field.getType();
System.out.println("Field Type=" + type); //输出 -> Field Type=class java.lang.Object
Type type2 = field.getGenericType(); //type2的类型是TypeVariable
System.out.println("Field GenericType=" + type2); //输出 -> Field GenericType=T

Method的getGenericReturnType(获取带泛型信息的返回值类型)、getGenericParameterTypes(获取带泛型信息的传参类型)

Class clazz = Class.forName("com.llk.kt.C");
Method method = clazz.getDeclaredMethod("get");
Type type = method.getGenericReturnType(); //type的类型是TypeVariable
System.out.println("Method ReturnType=" + type); //输出 -> Method ReturnType=T

反射private的Field与Method为啥要setAccessible()?

setAccessible 启用和禁用访问安全检查的开关。

值为true 则指示反射的对象在使用时应该取消 Java 语言访问检查

值为 false 则指示反射的对象不实施 Java语言访问检查。


提供反射性能的小技巧

1. 降低Class.forName、clazz.getXXX的使用频率
long startTime = System.currentTimeMillis();
Class a_clazz = Class.forName("com.llk.kt.Test$A");
Constructor a_Constructor = a_clazz.getConstructor(String.class, int.class);
A a;
int count = 0;
while (count < 1000000) { //分别运行下边4个栗子
	 count++;
                
   //输出 -> 执行次数:1000000 总耗时:9
   a = new A("a1", 1);
  
   //执行次数:1000000 总耗时:46
   a = (A) a_Constructor.newInstance("a2", 2);

   //输出 -> 执行次数:1000000 总耗时:317
   a = (A) a_clazz
       .getConstructor(String.class, int.class)
       .newInstance("a3", 3);

   //执行次数:1000000 总耗时:803
   a = (A) Class.forName("com.llk.kt.Test$A")
        .getConstructor(String.class, int.class)
        .newInstance("a4", 4);
}
System.out.println("执行次数:"+ count +" 总耗时:" + (System.currentTimeMillis() - startTime));

2. setAccessible(true) 禁用Java语言安全检查
long startTime = System.currentTimeMillis();
Class a_clazz = Class.forName("com.llk.kt.Test$A");
Constructor constructor = a_clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("a", 1);

int count = 0;
while (count < 1000000) { //分别运行下边2个栗子
    count++;

    //输出 -> 执行次数:1000000 总耗时:3762
    Method method1 = a_clazz.getMethod("print");
    method1.setAccessible(false);
    method1.invoke(obj);

    //输出 -> 执行次数:1000000 总耗时:2704
    Method method2 = a_clazz.getMethod("print");
    method2.setAccessible(true);
    method2.invoke(obj);
}
System.out.println("执行次数:"+ count +" 总耗时:" + (System.currentTimeMillis() - startTime));

反射api大全

点它点它 -> Reflection API

反射工具类

package com.qihoo360.replugin.utils;

import android.content.Context;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * 和反射操作有关的Utils
 *
 * @author RePlugin Team
 */
public final class ReflectUtils {

    // ----------------
    // Class & Constructor
    // ----------------

    public static Class<?> getClass(final String className) throws ClassNotFoundException {
        return Class.forName(className);
    }

    public static <T> T invokeConstructor(Class<T> cls, Class[] parameterTypes, Object... args) throws
            NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<T> c = cls.getConstructor(parameterTypes);
        if (c != null) {
            c.setAccessible(true);
            return c.newInstance(args);
        }
        return null;
    }

    // ----------------
    // Field
    // ----------------

    public static Field getField(Class<?> cls, String fieldName) {
        // From Apache: FieldUtils.getField()

        // check up the superclass hierarchy
        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                final Field field = acls.getDeclaredField(fieldName);
                // getDeclaredField checks for non-public scopes as well
                // and it returns accurate results
                setAccessible(field, true);

                return field;
            } catch (final NoSuchFieldException ex) { // NOPMD
                // ignore
            }
        }
        // check the public interface case. This must be manually searched for
        // incase there is a public supersuperclass field hidden by a private/package
        // superclass field.
        Field match = null;
        for (final Class<?> class1 : cls.getInterfaces()) {
            try {
                final Field test = class1.getField(fieldName);
                Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
                        + "; a matching field exists on two or more implemented interfaces.", fieldName, cls);
                match = test;
            } catch (final NoSuchFieldException ex) { // NOPMD
                // ignore
            }
        }
        return match;
    }

    public static Object readStaticField(Class<?> c, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        return readField(c, null, fieldName);
    }

    public static Object readField(Object target, String fieldName) throws IllegalAccessException, NoSuchFieldException {
        return readField(target.getClass(), target, fieldName);
    }

    public static Object readField(Class<?> c, Object target, String fieldName) throws IllegalAccessException, NoSuchFieldException {
        Field f = getField(c, fieldName);

        return readField(f, target);
    }

    public static Object readField(final Field field, final Object target) throws IllegalAccessException {
        return field.get(target);
    }

    public static void writeField(Object target, String fName, Object value) throws NoSuchFieldException, IllegalAccessException {
        writeField(target.getClass(), target, fName, value);
    }

    public static void writeField(Class<?> c, Object object, String fName, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field f = getField(c, fName);
        writeField(f, object, value);
    }

    public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException {
        field.set(target, value);
    }

    public static List<Field> getAllFieldsList(final Class<?> cls) {
        // From Apache: FieldUtils.getAllFieldsList()

        Validate.isTrue(cls != null, "The class must not be null");
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            for (final Field field : declaredFields) {
                allFields.add(field);
            }
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
    }

    public static void removeFieldFinalModifier(final Field field) {
        // From Apache: FieldUtils.removeFinalModifier()
        Validate.isTrue(field != null, "The field must not be null");

        try {
            if (Modifier.isFinal(field.getModifiers())) {
                // Do all JREs implement Field with a private ivar called "modifiers"?
                final Field modifiersField = Field.class.getDeclaredField("modifiers");
                final boolean doForceAccess = !modifiersField.isAccessible();
                if (doForceAccess) {
                    modifiersField.setAccessible(true);
                }
                try {
                    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
                } finally {
                    if (doForceAccess) {
                        modifiersField.setAccessible(false);
                    }
                }
            }
        } catch (final NoSuchFieldException ignored) {
            // The field class contains always a modifiers field
        } catch (final IllegalAccessException ignored) {
            // The modifiers field is made accessible
        }
    }

    // ----------------
    // Method
    // ----------------

    public static Method getMethod(Class<?> cls, String methodName, Class<?>... parameterTypes) {
        // check up the superclass hierarchy
        for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) {
            try {
                final Method method = acls.getDeclaredMethod(methodName, parameterTypes);
                // getDeclaredField checks for non-public scopes as well
                // and it returns accurate results
                setAccessible(method, true);

                return method;
            } catch (final NoSuchMethodException ex) { // NOPMD
                // ignore
            }
        }
        // check the public interface case. This must be manually searched for
        // incase there is a public supersuperclass field hidden by a private/package
        // superclass field.
        Method match = null;
        for (final Class<?> class1 : cls.getInterfaces()) {
            try {
                final Method test = class1.getMethod(methodName, parameterTypes);
                Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s"
                        + "; a matching field exists on two or more implemented interfaces.", methodName, cls);
                match = test;
            } catch (final NoSuchMethodException ex) { // NOPMD
                // ignore
            }
        }
        return match;
    }


    public static Object invokeMethod(final Object object, final String methodName, Class<?>[] methodParamTypes, Object... args)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        Class clz = object.getClass();
        Method m = getMethod(clz, methodName, methodParamTypes);
        return m.invoke(args);
    }

    public static Object invokeMethod(ClassLoader loader, String clzName,
                                      String methodName, Object methodReceiver,
                                      Class<?>[] methodParamTypes, Object... methodParamValues) throws
            ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        if (methodReceiver == null) {
            return null;
        }

        Class clz = Class.forName(clzName, false, loader);
        if (clz != null) {
            Method med = clz.getMethod(methodName, methodParamTypes);
            if (med != null) {
                med.setAccessible(true);
                return med.invoke(methodReceiver, methodParamValues);
            }
        }
        return null;
    }

    public static void setAccessible(AccessibleObject ao, boolean value) {
        if (ao.isAccessible() != value) {
            ao.setAccessible(value);
        }
    }

    // ----------------
    // Other
    // ----------------

    public static final void dumpObject(Object object, FileDescriptor fd, PrintWriter writer, String[] args) {
        try {
            Class<?> c = object.getClass();
            do {
                writer.println("c=" + c.getName());
                Field fields[] = c.getDeclaredFields();
                for (Field f : fields) {
                    boolean acc = f.isAccessible();
                    if (!acc) {
                        f.setAccessible(true);
                    }
                    Object o = f.get(object);
                    writer.print(f.getName());
                    writer.print("=");
                    if (o != null) {
                        writer.println(o.toString());
                    } else {
                        writer.println("null");
                    }
                    if (!acc) {
                        f.setAccessible(acc);
                    }
                }
                c = c.getSuperclass();
            } while (c != null && !c.equals(Object.class) && !c.equals(Context.class));
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }


}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值