反射学习记录,欢迎纠错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();
}
}
}