本文转自Java反射 并加以修改
概念
JAVA反射(reflection)机制 是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。 反射主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
程序中一般的对象的类型都是在编译期就确定下来的,而Java反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。即我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
反射机制主要提供以下功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。
实际使用:
当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。
反射最重要的用途就是开发各种通用框架。 很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。
对于框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。
Class类 Class可以说是反射能够实现的基础
class关键字是在声明java类时使用的;而 Class 是java JDK提供的一个类,一个描述类的类(也就是描述类本身),封装了描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性,完整路径为 java.lang.Class
对于每一个类,Java虚拟机都会初始化出一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件里。
当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。
Class对象构造器是私有的,只有JVM才可以调用这个构造函数创建Class的对象 4. 每个class(注意class是小写,代表普通类)类,无论创建多少个实例对象,在JVM中都对应同一个Class对象。 5. Class是反射能够实现的基础的另一个原因是:Java反射包java.lang.reflect中的所有类都没有public构造方法,要想获得这些类实例,只能通过Class类获取。所以说如果想使用反射,必须得获得Class对象。
基本运用 获取Class对象
通过类的类型获取Class对象,基本类型同样可以使用这种方法
1 2 3 4 Class<?> c = boolean .class; Class<?> classInt = Integer.TYPE;
通过对象实例获取对应Class对象Object.getClass()
–对于基本类型无法使用这种方法
1 2 StringBuilder str = new StringBuilder("123" ); Class<?> klass = str.getClass();
使用Class类的forName静态方法(通过类的全限定名)
1 public static Class<?> forName(String className)
在JDBC开发中常用此方法加载数据库驱动: 要使用全类名来加载这个类,一般数据库驱动的配置信息会写在配置文件中。加载这个驱动前要先导入jar包
另外还有一些反射方法可以获取Class对象,但前提是你已经获取了一个Class对象
Class.getClasses()
Class.getDeclaredClasses()
Class.getDeclaringClass()
Class.getEnclosingClass()
java.lang.reflect.Field.getDeclaringClass()
java.lang.reflect.Method.getDeclaringClass()
java.lang.reflect.Constructor.getDeclaringClass()
示例:通过Class获取类修饰,类型等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public class TestReflection { private static final String TAG = "Reflection" ; public void testReflection () { Class<?> c = HashMap.class; System.out.println(TAG + "Class : " + c.getCanonicalName()); System.out.println(TAG + "Modifiers : " + Modifier.toString(c.getModifiers())); TypeVariable<?>[] tv = c.getTypeParameters(); if (tv.length != 0 ) { StringBuilder parameter = new StringBuilder("Parameters : " ); for (TypeVariable<?> t : tv) { parameter.append(t.getName()); parameter.append(" " ); } System.out.println(TAG + parameter.toString()); } else { System.out.println(TAG + " -- No Type Parameters --" ); } Type[] intfs = c.getGenericInterfaces(); if (intfs.length != 0 ) { StringBuilder interfaces = new StringBuilder("Implemented Interfaces : " ); for (Type intf : intfs) { interfaces.append(intf.toString()); interfaces.append(" " ); } System.out.println(TAG + interfaces.toString()); } else { System.out.println(TAG + " -- No Implemented Interfaces --" ); } List<Class> l = new ArrayList<>(); printAncestor(c, l); if (l.size() != 0 ) { StringBuilder inheritance = new StringBuilder("Inheritance Path : " ); for (Class<?> cl : l) { inheritance.append(cl.getCanonicalName()); inheritance.append(" " ); } System.out.println(TAG + inheritance.toString()); } else { System.out.println(TAG + " -- No Super Classes --" ); } Annotation[] ann = c.getAnnotations(); if (ann.length != 0 ) { StringBuilder annotation = new StringBuilder("Annotations : " ); for (Annotation a : ann) { annotation.append(a.toString()); annotation.append(" " ); } System.out.println(TAG + annotation.toString()); } else { System.out.println(TAG + " -- No Annotations --" ); } } private static void printAncestor (Class<?> c, List<Class> l) { Class<?> ancestor = c.getSuperclass(); if (ancestor != null ) { l.add(ancestor); printAncestor(ancestor, l); } } public static void main (String[] args) { new TestReflection().testReflection(); } }
判断是否为某个类的实例 一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()
方法来判断是否为某个类的实例,它是一个Native方法:
1 public native boolean isInstance (Object obj) ;
创建实例 通过反射来生成对象主要有两种方式。
使用Class对象的newInstance()方法来创建Class对象对应类的实例。
利用newInstance创建对象:调用的类必须有无参的构造器
1 2 3 4 5 6 7 8 Class<?> c = String.class; Object str = c.newInstance();
先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。
1 2 3 4 5 6 Class<?> c = String.class; Constructor constructor = c.getConstructor(String.class); Object obj = constructor.newInstance("23333" );
Member 类成员主要包括构造函数 ,变量 和方法 ,Java中的操作基本都和这三者相关,而Member的这三个实现类就分别对应他们。
java.lang.reflect.Field :对应类变量
java.lang.reflect.Method :对应类方法
java.lang.reflect.Constructor :对应类构造函数
反射就是通过这三个类才能在运行时改变对象状态
突破java的权限检测 Java运行时会进行访问权限检查,private类型的变量无法进行直接访问java.lang.reflect.AccessibleObject
AccessibleObject为我们提供了一个方法 setAccessible(boolean flag),该方法的作用就是可以取消 Java 语言访问权限检查。 所以任何继承AccessibleObject的类的对象都可以使用该方法取消 Java 语言访问权限检查。(final类型变量也可以通过这种办法访问)
1 public final class Field extends AccessibleObject implements Member
Field、Method和Constructor都是继承AccessibleObject
测试类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface Invoke {} class PersonBean { private String name; int id; public String getName () { return name; } public void setName (String name) { this .name = name; } } interface User { public void login () ; } public class UserBean extends PersonBean implements User { @Override public void login () { System.out.println("login" ); } public void eat (String food) { System.out.println("eat food " + food); } public void eat (String... foods) { StringBuilder s = new StringBuilder(); for (String food : foods) { s.append(food); s.append(" " ); } System.out.println("eat food " + s.toString()); } class B { } public String userName; protected int i; static int j; @Deprecated private int k; private long userId; public UserBean () { } public UserBean (String userName, long userId) { this .userName = userName; this .userId = userId; } @Override public String getName () { return userName; } public long getId () { return userId; } @Invoke public static void staticMethod (String devName, int a) { System.out.printf("Hi %s, I'm a static method" , devName); } @Invoke public void publicMethod () { System.out.println("I'm a public method" ); } @Invoke private void privateMethod () { System.out.println("I'm a private method" ); } @Override public String toString () { return "UserBean{" + "userName='" + userName + '\'' + ", userId=" + userId + '}' ; } }
Field 通过Field你可以访问给定对象的类变量,包括获取变量的类型、修饰符、注解、变量名、变量的值或者重新设置变量值,即使变量是private的。
获取Field Class提供了4种方法获得给定类的Field。
1 2 3 4 5 6 7 8 getDeclaredField(String name) getField(String name) getDeclaredFields() getFields()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public void testField () { Class c = UserBean.class; Field[] fields = c.getDeclaredFields(); for (Field f : fields) { StringBuilder builder = new StringBuilder(); builder.append("filed name = " ); builder.append(f.getName()); builder.append(" type = " ); builder.append(f.getType()); builder.append(" modifiers = " ); builder.append(Modifier.toString(f.getModifiers())); Annotation[] ann = f.getAnnotations(); if (ann.length != 0 ) { builder.append(" annotations = " ); for (Annotation a : ann) { builder.append(a.toString()); builder.append(" " ); } } else { builder.append(" -- No Annotations --" ); } System.out.println(builder.toString()); } }
获取、设置变量值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public void setField () { UserBean userBean = new UserBean("Tom" , 2 ); Class c = userBean.getClass(); try { Field fieldName = c.getField("userName" ); Field fieldId = c.getDeclaredField("userId" ); fieldName.setAccessible(true ); String name = (String) fieldName.get(userBean); long age = fieldId.getLong(userBean); System.out.println("before set, UserBean userName = " + name + " userId = " + age); fieldName.set(userBean, "Timmy" ); fieldId.setInt(userBean, 3 ); System.out.println("after set, UserBean " + userBean.toString()); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } }
Method 获取方法 Class依然提供了4种方法获取Method:
1 2 3 4 5 6 7 8 9 10 11 getDeclaredMethods() getMethods() getDeclaredMethod(String name, Class<?>... parameterTypes) getMethod(String name, Class<?>... parameterTypes)
注意:获取带参数方法时,如果参数类型错误会报NoSuchMethodException,对于参数是泛型的情况,泛型须当成Object处理(Object.class)
getMethods()和getDeclaredMethods()区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void getMethod () { Class clazz = UserBean.class; System.out.println("------共有方法------" ); for (Method method : clazz.getMethods()) { String name = method.getName(); System.out.println(name); } System.out.println("------独占方法------" ); for (Method method : clazz.getDeclaredMethods()) { String name = method.getName(); System.out.println(name); } }
获取所有方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void getAllMethods () { Class c = UserBean.class; for (Method method : c.getDeclaredMethods()) { StringBuilder methodString = new StringBuilder(Modifier.toString(method.getModifiers()) + " " ); methodString.append(method.getReturnType().getSimpleName()).append(" " ); methodString.append(method.getName()).append("(" ); Class[] parameters = method.getParameterTypes(); Parameter[] p = method.getParameters(); for (Class parameter : parameters) { methodString.append(parameter.getSimpleName()).append(" " ); } methodString.append(")" ); System.out.println(methodString.toString()); } }
获取方法返回类型
getReturnType():获取目标方法返回类型对应的Class对象
getGenericReturnType():获取目标方法返回类型对应的Type对象
这两个方法有啥区别呢?
getReturnType()返回类型为Class,getGenericReturnType()返回类型为Type, Class实现Type。
返回值为普通简单类型如Object, int, String等,getGenericReturnType()返回值和getReturnType()一样
例如public String function1()
,那么各自返回值为:
getReturnType():class java.lang.String
getGenericReturnType():class java.lang.String
返回值为泛型 例如public T function2()
,那么各自返回值为:
getReturnType():class java.lang.Object
getGenericReturnType():T
返回值为参数化类型 例如public Class<String> function3()
,那么各自返回值为:
getReturnType():class java.lang.Class
getGenericReturnType():java.lang.Class<java.lang.String>
其实反射中所有形如getGenericXXX()的方法规则都与上面所述类似。
获取方法参数类型
getParameterTypes():获取目标方法各参数类型对应的Class对象
getGenericParameterTypes():获取目标方法各参数类型对应的Type对象
返回值为数组,区别同上 “方法返回类型的区别” 。
获取方法声明抛出的异常的类型
getExceptionTypes():获取目标方法抛出的异常类型对应的Class对象
getGenericExceptionTypes():获取目标方法抛出的异常类型对应的Type对象
返回值为数组,区别同上
获取方法参数名称 .class文件中默认不存储方法参数名称,如果想要获取方法参数名称,需要在编译的时候加上-parameters参数。(构造方法的参数获取方法同样)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Parameter[] params = m.getParameters(); for (int i = 0 ; i < params.length; i++) { Parameter p = params[i]; p.getType(); p.getName(); p.getModifiers(); p.isNamePresent(); }
获取方法修饰符 方法与Filed等类似
几个Method方法
method.isVarArgs():判断方法参数是否是可变参数
1 2 public Constructor<T> getConstructor (Class<?>... parameterTypes) public Constructor<T> getConstructor (Class<?> [] parameterTypes)
method.isSynthetic():判断是否是复合方法,个人理解复合方法是编译期间编译器生成的方法,并不是源代码中有的方法
method.isBridge():判断是否是桥接方法,桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法
通过反射调用方法 反射通过Method的invoke()方法来调用目标方法。第一个参数为需要调用的目标类对象,如果方法为static的,则该参数为null。后面的参数都为目标方法的参数值,顺序与目标方法声明中的参数顺序一致。
1 2 public native Object invoke (Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
注意:如果方法是private的,可以使用method.setAccessible(true)方法绕过权限检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public void testMethod () { Class<?> c = UserBean.class; try { Constructor constructor = c.getConstructor(String.class, long .class); Object userBean = constructor.newInstance( "Jack" , 3 ); Method sleep = c.getDeclaredMethod("login" ); sleep.invoke(userBean); Method eat = c.getDeclaredMethod("eat" , String.class); eat.invoke(userBean, "meat" ); Class[] argTypes = new Class[] { String[].class }; Method varargsEat = c.getDeclaredMethod("eat" , argTypes); String[] foods = new String[]{ "meat" , "vegetable" }; varargsEat.invoke(userBean, (Object)foods); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); } }
被调用的方法本身所抛出的异常在反射中都会以InvocationTargetException抛出。换句话说,反射调用过程中如果异常InvocationTargetException抛出,说明反射调用本身是成功的,因为这个异常是目标方法本身所抛出的异常。
Constructor 和Method一样,Class也为Constructor提供了4种方法获取
1 2 3 4 5 6 7 8 getDeclaredConstructor(Class<?>... parameterTypes) getConstructor(Class<?>... parameterTypes) getDeclaredConstructors() getConstructors()
构造方法的名称、限定符、参数、声明的异常等获取方法都与Method类似,请参照Method。
之前见过,有两种方式创建实例
java.lang.reflect.Constructor.newInstance() 2, Class.newInstance()
区别:
Class.newInstance()仅可用来调用无参的构造方法;Constructor.newInstance()可以调用任意参数的构造方法
Class.newInstance()会将构造方法中抛出的异常不作处理原样抛出;Constructor.newInstance()会将构造方法中抛出的异常都包装成InvocationTargetException抛出。
Class.newInstance()需要拥有构造方法的访问权限;Constructor.newInstance()可以通过setAccessible(true)方法绕过访问权限访问private构造方法。
1 2 3 4 5 6 7 Class<?> c = UserBean.class; try { Constructor constructor = c.getConstructor(String.class, long .class); UserBean userBean = (UserBean) constructor.newInstance( "Jack" , 3 ); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); }
注意:反射不支持自动封箱,传入参数时要小心(自动封箱是在编译期间的,而反射在运行期间)
数组和枚举 数组和枚举也是对象,但是在反射中,对数组和枚举的创建、访问和普通对象有那么一丢丢的不同,所以Java反射为数组和枚举提供了一些特定的API接口。
数组 数组类型 数组类型:数组本质是一个对象,所以它也有自己的类型。 例如对于int[] intArray
,数组类型为class [I
。数组类型中的[
个数代表数组的维度,例如[
代表一维数组,[[
代表二维数组;[
后面的字母代表数组元素类型,I
代表int,一般为类型的首字母大写(long类型例外,为J)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class [B class [S class [I class [C class [J class [F class [D class [Lcom.dada.Season class [[Ljava.lang.String
1 2 3 4 5 6 7 Class<?> c = field.getType(); if (c.isArray()) { c.getComponentType() }
创建和初始化数组 Java反射为我们提供了java.lang.reflect.Array类用来创建和初始化数组。
1 2 3 4 5 6 Array.newInstance(Class<?> componentType, int ... dimensions) Array.set(Object array, int index, int value) Array.get(Object array, int index)
例:用反射创建int[] array = new int[]{1, 2}
1 2 3 Object array = Array.newInstance(int .class, 2 ); Array.setInt(array , 0 , 1 ); Array.setInt(array , 1 , 2 );
注意:反射支持数据自动转换,但不允许数据强制转换。意思是对于上述set方法,你可以在int类型数组中 set short类型数据,但不可以set long类型数据,否则会报IllegalArgumentException
多维数组 Java反射没有提供能够直接访问多维数组元素的API,但你可以把多维数组当成数组的数组处理。
1 2 3 4 5 6 7 8 Object matrix = Array.newInstance(int .class, 2 , 2 ); Object row0 = Array.get(matrix, 0 ); Object row1 = Array.get(matrix, 1 ); Array.setInt(row0, 0 , 1 ); Array.setInt(row0, 1 , 2 ); Array.setInt(row1, 0 , 3 ); Array.setInt(row1, 1 , 4 );
或
1 2 3 4 5 6 7 8 9 10 11 Object matrix = Array.newInstance(int .class, 2 ); Object row0 = Array.newInstance(int .class, 2 ); Object row1 = Array.newInstance(int .class, 2 ); Array.setInt(row0, 0 , 1 ); Array.setInt(row0, 1 , 2 ); Array.setInt(row1, 0 , 3 ); Array.setInt(row1, 1 , 4 ); Array.set(matrix, 0 , row0); Array.set(matrix, 1 , row1);
枚举 枚举隐式继承自java.lang.Enum,Enum继承自Object,所以枚举本质也是一个类,也可以有成员变量,构造方法,方法等;对于普通类所能使用的反射方法,枚举都能使用;另外java反射额外提供了几个方法为枚举服务。 枚举隐式继承自java.lang.Enum,Enum继承自Object,所以枚举本质也是一个类,也可以有成员变量,构造方法,方法等;对于普通类所能使用的反射方法,枚举都能使用;另外java反射额外提供了几个方法为枚举服务。
1 2 3 4 5 6 Class.isEnum() Class.getEnumConstants() java.lang.reflect.Field.isEnumConstant()
其他方法 注解中常用方法:
1 2 3 4 5 6 7 8 Annotation[] annotations = (Annotation[]) class1.getAnnotations(); Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class); Type genericSuperclass = class1.getGenericSuperclass(); getGenericInterfaces();
获取Class对象其他信息方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 boolean isPrimitive = class1.isPrimitive();boolean isArray = class1.isArray();boolean isAnnotation = class1.isAnnotation();boolean isInterface = class1.isInterface();boolean isEnum = class1.isEnum();boolean isAnonymousClass = class1.isAnonymousClass();boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);String className = class1.getName(); Package aPackage = class1.getPackage(); String simpleName = class1.getSimpleName(); int modifiers = class1.getModifiers();Class<?>[] declaredClasses = class1.getDeclaredClasses(); Class<?> declaringClass = class1.getDeclaringClass(); ClassLoader ClassLoader = class1.getClassLoader() getSuperclass(): getInterfaces():
静态元素 静态的类,方法,字段和实例类,方法,字段完全不一样,因为它无需初始化类就可以直接使用。
反射缺点
性能问题。因为反射是在运行时而不是在编译时,所有不会利用到编译优化,同时因为是动态生成,因此,反射操作的效率要比那些非反射操作低得多。
安全问题。使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。
代码问题。由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
参考 Java反射 Java基础13:反射详解
代理模式