1. 介绍
反射是一种能够在程序运行时动态访问、修改某个类中任意属性和方法的机制。
具体:
对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意一个方法和属性
在运行时,当加载完类之后,JVM在堆内存中会自动产生一个Class类型的对象,这个对象包含了完整的类的结构信息
这个Class对象就像一面镜子,透过这个镜子看到类的结构
那么,如何得到这个Class对象呢?以下可否
Class c = new Class();
答案是不行的,因为Class的构造函数定义为私有的,只有JVM可以访问
privateClass(ClassLoader loader) {
classLoader=loader;
}
Class对象获取的三种方式
Class c1 = Code.class;
Class c2=code1.getClass();
Class c3= Class.forName("com.trigl.reflect.Code");
举例
public classTestStudent {public static void main(String[] args) throwsClassNotFoundException {
Student student= new Student(1, "Jack");
Class class1=student.getClass();
Class class2= Student.class;
Class class3= Class.forName("com.example.refs.Student");
System.out.println(class1);
System.out.println(class2);
System.out.println(class3);
}
}
输出
classcom.example.refs.Studentclasscom.example.refs.Studentclass com.example.refs.Student
2. Java反射相关操作
本文以Student类为例:
接口
packagecom.example.refs;public interfaceInterFace {voidread();
}
类
packagecom.example.refs;public class Student implementsInterFace{private intid;publicString name;publicStudent() {}public Student(intid, String name) {this.id =id;this.name =name;
}public void setId(intid) {this.id =id;
}private intgetId() {returnid;
}
@Overridepublic voidread() {}
}
以下具体介绍下具体的用法
2.1 类名称、包名
public classClassName {public static voidmain(String[] args) {
Class> class2 = Student.class;
System.out.println("getSimpleName:" +class2.getSimpleName());
System.out.println("getName:" +class2.getName());
System.out.println("getPackage:" +class2.getPackage());
}
}
输出
getSimpleName:Student
getName:com.example.refs.Student
getPackage:package: com.example.refs
2.2 方法
public Method getDeclaredMethod(String name, Class>...parameterTypes) // 得到本类所有的方法(public/private/proteceted...)public Method getMethod(String name, Class>...parameterTypes) //得到该类所有的public方法,包括父类
举例
public classMethodTest {public static voidmain(String[] args) {
Class> class1 = Student.class;
Method[] methods=class1.getMethods();
Method[] declaredMethods=class1.getDeclaredMethods();for(Method method : methods) {
System.out.println("getMethods: " +method);
}
System.out.println();for(Method method : declaredMethods) {
System.out.println("getDeclaredMethods: " +method);
}
}
}
结果
getMethods: public void com.example.refs.Student.read()
getMethods: public void com.example.refs.Student.setId(int)
getMethods: public final void java.lang.Object.wait() throws java.lang.InterruptedException
getMethods: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
getMethods: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
getMethods: public boolean java.lang.Object.equals(java.lang.Object)
getMethods: public java.lang.String java.lang.Object.toString()
getMethods: public native int java.lang.Object.hashCode()
getMethods: public final native java.lang.Class java.lang.Object.getClass()
getMethods: public final native void java.lang.Object.notify()
getMethods: public final native void java.lang.Object.notifyAll()
getDeclaredMethods: public void com.example.refs.Student.read()
getDeclaredMethods: private int com.example.refs.Student.getId()
getDeclaredMethods: public void com.example.refs.Student.setId(int)
指定方法
public classMethodChangeVal {public static void main(String[] args) throwsIllegalAccessException, InstantiationException,
NoSuchMethodException, InvocationTargetException {
Class clazz= Student.class;
Object obj=clazz.newInstance();
Method methodGet= clazz.getDeclaredMethod("getId");
Method methodSet= clazz.getDeclaredMethod("setId", int.class);
methodSet.invoke(obj,123);
System.out.println(methodGet.invoke(obj));
}
}
异常
Exception in thread "main" java.lang.IllegalAccessException: Class
com.example.refs.MethodChangeVal can not access a member of class
com.example.refs.Student with modifiers "private"
原因:setId为私有方法,利用反射时会进行安全检查,用setAccessible(true)不进行安全检查,可以直接对调用私有方法、修改私有变量
public classMethodChangeVal {public static void main(String[] args) throwsIllegalAccessException, InstantiationException,
NoSuchMethodException, InvocationTargetException {
Class clazz= Student.class;
Object obj=clazz.newInstance();
Method methodGet= clazz.getDeclaredMethod("getId");
Method methodSet= clazz.getDeclaredMethod("setId", int.class);
methodGet.setAccessible(true);
methodSet.invoke(obj,123);
System.out.println(methodGet.invoke(obj));
}
}
2.3 构造函数
public Constructor getDeclaredConstructor(Class>... parameterTypes)public Constructor getConstructor(Class>... parameterTypes)
Constructor>[] allConstructors = class1.getDeclaredConstructors();//获取class对象的所有声明构造函数
Constructor>[] publicConstructors = class1.getConstructors();//获取class对象public构造函数
Constructor> constructor = class1.getDeclaredConstructor(String.class);//获取指定声明构造函数
Constructor publicConstructor = class1.getConstructor(String.class);//获取指定声明的public构造函数
举例
public classConstuctorTest {public static voidmain(String[] args) {
Class class2= Student.class;
Constructor>[] constructors =class2.getConstructors();for (Constructor>constructor : constructors) {
System.out.println(constructor+ ", Name:" +constructor.getName());
}
}
}
结果
publiccom.example.refs.Student(), Name:com.example.refs.Studentpublic com.example.refs.Student(int,java.lang.String), Name:com.example.refs.Student
2.4 成员变量
getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
getFields()获得某个类的所有的公共(public)的字段,包括父类。
举例
public classFieldTest {public static voidmain(String[] args) {
Class> aClass = Student.class;
Field[] declardFields=aClass.getDeclaredFields();
Field[] fields=aClass.getFields();for(Field field : declardFields) {
System.out.println("declaredField: " +field);
}
System.out.println();for(Field field : fields) {
System.out.println("field: " +field);
}
}
}
结果
declaredField: private intcom.example.refs.Student.id
declaredField:publicjava.lang.String com.example.refs.Student.name
field:public java.lang.String com.example.refs.Student.name
2.5 修饰符
举例
public classModifierTest {public static voidmain(String args[]) {
Class aClass= Student.class;int modifier =aClass.getModifiers();
System.out.println("modifier:" +modifier);
System.out.println(String.format("isAbstract: %s", Modifier.isAbstract(modifier)));
System.out.println(String.format("isPublic: %s", Modifier.isPublic(modifier)));
System.out.println(String.format("isStatic: %s", Modifier.isStatic(modifier)));
System.out.println(String.format("isFinal: %s", Modifier.isFinal(modifier)));
System.out.println(String.format("isSynchronized: %s", Modifier.isSynchronized(modifier)));
}
}
结果
modifier:1isAbstract:falseisPublic:trueisStatic:falseisFinal:falseisSynchronized:false
2.6 父类
public classGetParent {public static voidmain(String[] args) {
Class class2= Student.class;
System.out.println(class2.getSuperclass());
}
}
结果
class java.lang.Object
2.7 接口
public classInterfaceTest {public static voidmain(String[] args) {
Class> clazz = Student.class;
Class>[] inters =clazz.getInterfaces();for(Class>classIn : inters) {
System.out.println(classIn);
}
}
}
输出
interface com.example.refs.InterFace
2.8 创建对象实例
举例用2种方法创建
public classNewInstanceTest {public static void main(String[] args) throwsIllegalAccessException, InstantiationException,
NoSuchMethodException, InvocationTargetException {
Class clazz= Student.class;
Object obj=clazz.newInstance();
Constructor> constructor =clazz.getDeclaredConstructor();
Object obj2=constructor.newInstance();
Constructor> constructor2 = clazz.getDeclaredConstructor(int.class, String.class);
Object obj3= constructor2.newInstance(1, "HanMeimei");
System.out.println(obj);
System.out.println(obj2);
System.out.println(obj3);
}
}
结果
com.example.refs.Student@72ea2f77
com.example.refs.Student@33c7353a
com.example.refs.Student@681a9515
2.9 注解
自定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@interfaceMyAnnotation {publicString name();public String value();
注:RetentionPolicy.RUNTIME 这个表示运行期注解,这样在反射的时候才能取到
举例
public classAnnotationTest {
@MyAnnotation(name="someName", value = "Hello World")public voiddoSomething() {
}
@Deprecatedpublic voiddelFunc() {
System.out.println("Hello");
}public static void main(String[] args) throwsNoSuchMethodException {
Class clazz= AnnotationTest.class;
Method method= clazz.getMethod("doSomething");
Annotation[] declaredAnnotations=method.getDeclaredAnnotations();for(Annotation annotation : declaredAnnotations) {if (annotation instanceofMyAnnotation) {
MyAnnotation myAnnotation=(MyAnnotation)annotation;
System.out.println("name:" +myAnnotation.name());
System.out.println("value:" +myAnnotation.value());
}else{
System.out.println(annotation);
}
}
}
}
结果
name:someName
value:Hello World
2.10 泛型
参数类型、返回值类型举例
public classGenericTest {public void test01(Map map, Listlist) {
System.out.println("test01");
}public Maptest02() {
System.out.println("test02");return null;
}public static void main(String[] args) throwsNoSuchMethodException {
Method method= GenericTest.class.getMethod("test01", Map.class, List.class);
Type[] types=method.getGenericParameterTypes();for(Type type : types) {
System.out.println("#:" +type);if (type instanceofParameterizedType) {
Type[] genericType=((ParameterizedType)type).getActualTypeArguments();for(Type ontGenericType : genericType) {
System.out.println("泛型类型:" +ontGenericType);
}
}
}
Method method2= GenericTest.class.getMethod("test02");
Type returnType=method2.getGenericReturnType();
System.out.println("\nReturntype" +returnType);if (returnType instanceofParameterizedType) {
Type[] genericTypes=((ParameterizedType)returnType).getActualTypeArguments();for(Type type : genericTypes) {
System.out.println("返回值,泛型类型:" +type);
}
}
}
}
结果
#:java.util.Map
泛型类型:class java.lang.String
泛型类型:class com.example.refs.Student
#:java.util.List
泛型类型:class com.example.refs.Student
Returntypejava.util.Map
返回值,泛型类型:class java.lang.Integer
返回值,泛型类型:class com.example.refs.Student
2.11 数组
public classArrayTest {public static voidmain(String[] args) {int[] intArray = (int[])Array.newInstance(int.class, 3);
Array.set(intArray,0, 123);
Array.set(intArray,1, 124);
Array.set(intArray,2, 125);for (int i = 0; i < Array.getLength(intArray); ++i) {
System.out.println(String.format("array[%s]:%s", i, Array.get(intArray, i)));
}
Class aClass=intArray.getClass();
System.out.println("intArray是否是数组类型:" +aClass.isArray());
System.out.println("intArray成员类型:" +aClass.getComponentType());
}
}
结果
array[0]:123
array[1]:124
array[2]:125
intArray是否是数组类型:true
intArray成员类型:int
3. 分析
3.1 使用场景
操作因访问权限限制的属性和方法
实现自定义注解
动态加载第三方jar包
按需加载类,节省编译和初始化APK的时间
3.2 优缺点
优点:灵活、自由度高:不受类的访问权限限制
缺点
性能问题:通过反射访问、修改类的属性和方法时会远慢于直接操作,但性能问题的严重程度取决于在程序中是如何使用反射的。如果使用得很少,不是很频繁,性能将不是问题
安全性问题:反射可以随意访问和修改类的所有状态和行为,破坏了类的封装性,如果不熟悉被反射类的实现原理,随意修改可能导致潜在的逻辑问题
兼容性问题:因为反射会涉及到直接访问类的方法名和实例名,不同版本的API如果有变动,反射时找不到对应的属性和方法时会报异常
3.3 说明
通过反射访问方法比实例慢很多
有用到反射的类不能被混淆
反射存在性能问题,但使用不频繁、按需使用时,对程序性能影响并不大
反射存在安全性问题,因为可以随意修改类的所有状态和行为(包括private方法和实例)
使用反射访问Android的API时需要注意因为不同API版本导致的兼容性问题
3.4 性能对比
不使用反射、启用安全检查、启用安全检查进行对比
public classTestReflect {
@Testpublic voidtestNoneReflect() {
Student oneStudent= new Student(1, "HanMeimei");long start =System.currentTimeMillis();for (long i = 0; i < Integer.MAX_VALUE; ++i) { oneStudent.setId(1); }long count = System.currentTimeMillis() -start;
System.out.println("没有反射, 共消耗 毫秒");
}
@Testpublic void testNotAccess() throwsClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException,
InvocationTargetException {
Student oneStudent= new Student(1, "HanMeimei");
Method method= Class.forName("com.example.refs.Student").getMethod("setId", int.class);long start =System.currentTimeMillis();for (long i = 0; i < Integer.MAX_VALUE; ++i) { method.invoke(oneStudent, 1); }long count = System.currentTimeMillis() -start;
System.out.println("启用安全检查, 共消耗 毫秒");
}
@Testpublic void testUseAccess() throwsClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Student oneStudent= new Student(1, "HanMeimei");
Method method= Class.forName("com.example.refs.Student").getMethod("setId", int.class);
method.setAccessible(true);long start =System.currentTimeMillis();for (long i = 0; i < Integer.MAX_VALUE; ++i) { method.invoke(oneStudent, 1); }long count = System.currentTimeMillis() -start;
System.out.println("取消安全检查, 共消耗 毫秒");
}
}
结果对比
差异
耗时(ms)
没用反射
952
取消安全检查
4283
启用安全检查
14892