1、Method类
java.lang.reflect.Method
使用 Java 的反射机制获得的指定类中指定方法的对象;
Method 类的对象可以是类方法,也可以是实例方法;
通过反射调用类的私有方法时,要先在这个私有方法对应的 Method 对象上调用 setAccessible(true) 来取消对这个方法的访问检查,再调用 invoke() 方法来执行这个私有方法。
1)invoke方法
Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
obj:调用方法的对象;
args:为指定方法传递的参数值,是一个可变参数;
invoke() 方法的返回值为动态调用指定方法后的实际返回值;
如果调用的底层方法是静态的,则指定的obj参数被忽略。它可以是null。
如果调用的底层方法所需的形式参数数量为0,提供的args数组的长度可以为0或null。
想调用的底层方法没有访问权限报IllegalAccessException;调用的底层方法有异常报InvocationTargetException。
2)其他常用方法
—— StringgetName()
以字符串的形式返回此方法对象所表示的方法的名称。
—— Class>[] getParameterTypes()
返回一个Class对象数组,该数组以声明顺序表示该方法对象的参数类型。
—— Class> getReturnType()
返回一个Class对象,该Class对象表示该方法对象的返回值类型 。
—— Class>[] getExceptionTypes()
返回一个Class对象数组,该数组表示由该方法对象抛出的异常类型。
——boolean isAccessible()
获取该方法对象的可访问标志
——setAccessible(boolean flag)
设置该方法对象的可访问标志,在其他类里调用该方法对象时,如果该方法为私有方法,需要设置访问标志为true,否则会报异常
——Class> getDeclaringClass()
返回方法所在类的Class对象。
——int getModifiers()
返回修饰该方法对象修饰符的整数形式,使用 Modifier 类对其进行解码:Modifier.toString(method.getModifiers())
2、Field类
1)简介
Field类位于java.lang.reflect包下。在Java反射中Field类描述的是类的属性信息,功能包括:
获取当前对象的成员变量的类型
对成员变量重新设值
2)获取Field对象
Class.getFields(): 获取类中public类型的属性,返回一个包含某些 Field 对象的数组,该数组包含此 Class 对象所表示的类或接口的所有可访问公共字段
getDeclaredFields(): 获取类中所有的属性(public、protected、default、private),但不包括继承的属性,返回 Field 对象的一个数组
getField(String name): 获取类特定的方法,name参数指定了属性的名称
getDeclaredField(String name): 获取类特定的方法,name参数指定了属性的名称
3)获取变量类型和变量修饰符
类中的变量分为两种类型:基本类型和引用类型:
基本类型( 8 种)
整数:byte, short, int, long;浮点数:float, double;字符:char;布尔值:boolean
引用类型
所有的引用类型都继承自 java.lang.Object;类,枚举,数组,接口都是引用类型;java.io.Serializable 接口,基本类型的包装类(比如 java.lang.Double)也是引用类型。
java.lang.reflect.Field 提供了两个方法获去变量的类型:
——Class> getType()
返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。
——Type getGenericType()
返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型(如果当前属性有签名属性类型就返回,否则就返回getType())。
成员变量可以被以下修饰符修饰:
访问权限控制符:public, protected, private;限制只能有一个实例的:static;不允许修改的:final;不会被序列化:transient;线程共享数据的一致性:volatile;注解
类似获取 Class 的修饰符,我们可以使用 Field.getModifiers() 方法获取当前成员变量的修饰符。 返回 java.lang.reflect.Modifier 中定义的整形值。然后使用 Modifier.toString(int mod)解码成字符串。
4)获取和设置成员变量的值
基本类型的获取方法:
byte getByte(Object obj)
获取一个静态或实例 byte 字段的值。
int getInt(Object obj)
获取 int 类型或另一个通过扩展转换可以转换为 int 类型的基本类型的静态或实例字段的值。
......
基本类型的setter方法:
void setByte(Object obj, byte b)
将字段的值设置为指定对象上的一个 byte 值。
void setShort(Object obj, short s)
将字段的值设置为指定对象上的一个 short 值。
......
引用类型的getters方法:
Object get(Object obj)
返回指定对象上此 Field 表示的字段的值。
引用类型的setters方法:
void set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
setAccessible()方法,Field的setAccessible()方法是从AccessibleObject类继承而来的。AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。
它提供了在使用时 取消默认 Java 语言访问控制检查的能力。一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在Field,Method和Constructor这些类上调用setAccessible()方法,我们可以操作这些字段无法访问的字段。
5)常见错误
常见错误 1 :无法转换类型导致的 java.lang.IllegalArgumentException
在使用反射获取或者修改一个变量的值时,编译器不会进行自动装/拆箱。所以我们无法给 Integer 类型的属性使用 setInt() 方法重新设值,必须给它赋一个 Integer 对象才可以。否则会因为无法转换类型而出现java.lang.IllegalArgumentException。
常见错误 2:反射非 public 的变量导致的 NoSuchFieldException
如果你使用 Class.getField() 或者 Class.getFields() 获取非 public 的变量,编译器会报 java.lang.NoSuchFieldException 错。
常见错误 3 :修改 final类型的变量导致的 IllegalAccessException
当你想要获取或者修改 不可修改(final)的变量时,会导致IllegalAccessException。
由于 Field 继承自 AccessibleObject , 我们可以使用 AccessibleObject.setAccessible() 方法告诉安全机制,这个变量可以访问。也就是Field.setAccessible(true)。告诉安全机制当前的这字段不做访问权限检查,这样我们就能反射修改final修饰的常量。
3、Constructor类
一个类中可以存在多个构造方法,如果要想取得类中构造的调用,就可以使用Class类中提供的两个方法:
// 取得指定参数类型的构造
public Constructor getConstructor(Class>... parameterTypes)throws NoSuchMethodException, SecurityException{}
// 取得类中的所有构造
public Constructor>[] getConstructors() throws SecurityException{}
以上两个方法返回的类型都是java.lang.reflect.Constructor类的实例化对象,这个类之中大家只需要关注一个方法:
public T newInstance(Object ... initargs)throws InstantiationException,IllegalAccessException,IllegalArgumentException, InvocationTargetException{}
4、反射
1)静态加载和动态加载
Java创建对象的常用方式是使用new 关键字,如 User user = new User(); 这种是静态加载,即在编译期就已经获取User类的有关信息,如果User类不存在或有错误,编译会报错。动态加载就是用上面的Class.forName("包名.User");来加载User类,如果User类不存在或是有错误,在编译时是不会报错的,因为根本没去加载User类。只有当程序运行到该处,JVM才会去加载User,而动态加载则是依赖反射实现的。
Java反射机制是指java程序在运行过程中,可以获取任意一个类的相关信息,并且能够调用其方法和属性,这种动态获取信息和动态调用方法的功能叫做Java的反射机制。
2)反射操作(核心类)
利用反射可以做出一个对象具备的所有操作行为,最为关键的是这一切的操作都可以基于Object进行。
3)反射的优缺点
反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。这是一个相对高级的特性,只有那些语言基础非常扎实的开发者才应该使用它。如果能把这句警示时刻放在心里,那么反射机制就会成为一项强大的技术,可以让应用程序做一些几乎不可能做到的事情。
反射的缺点:
反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
使用反射技术要求程序必须在一个没有安全限制的环境中运行,如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题。
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用:代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
反射破坏了代码的封装性。
4)提高反射效率
尽量不要getMethods()后再遍历筛选,而直接用getMethod(methodName)来根据方法名获取方法。
需要多次动态创建一个类的实例的时候,有缓存的写法会比没有缓存要快很多。
使用高性能的反射库,比自己写缓存效果好,如joor,或者apache的commons相关工具类。
使用高版本JDK也很重要,反射性能一直在提高。
5)反射问题
常量是否能被反射更改?
对于基本类型的静态常量以及String类型常量,JAVA在编译的时候就会把代码中对此常量中引用的地方替换成相应常量值。其他常量能被反射更改。