Java反射使用总结
最近公司招了几名刚毕业的大学生,在给他们培训的过程中,讲到反射,他们有些人听不懂,对反射的概念云里雾里的,不知道反射有什么用。 因此就有了本文的诞生。 反射是java提供的一个重要功能,可以在运行时检查类、接口、方法和变量等信息,无需知道类的名字,方法名等。还可以在运行时实例化新对象,调用方法以及设置和获取变量值。 反射非常强大和有用,很多java框架中都有反射的影子,例如spring、mybatis等等, JDBC利用反射将数据库的表字段映射到java对象的getter/setter方法。 Jackson,GSON,Boon等类库也是利用反射将JSON文件的属性映射到java对的象getter/setter方法。 可见,只要使用java,反射就无处不在。 Class对象 检查一个类之前,必须获取到java.lang.Class对象,java中的所有类型,包括long,int,数组等基本数据类型,都和Class对象有关系。 我们很多人去医院参加体检的时候,都做过B超检查,医生只需把一个探头在我们身上滑动就可以将我们体内的肝、胆、肾等器官反射到B超设备上显示。 Class类对象就相当于B超的探头,将一个类的方法、变量、接口、类名、类修饰符等信息告诉运行的程序。 Java提供了两种方式获取Class对象,一种是使用.class,另外一种是使用Class.forName()。 Class alunbarClass = Alunbar.class; Class.forName()方式适用于运行时动态获取Class对象,只需将类名作为forName方法的参数: try{ Class alunbarClass1 = Class.forName("Alunbar"); } catch(ClassNotFoundException e){ System.out.println("找不到Alunbar类"); } 这个方法会出现类找不到的情况,因此使用这个方法获取Class对象时,必须捕获ClassNotFoundException异常。 获取类名 package cn.alunbar; public class Alunbar { public static void main(String arts[]){ Class alunbarClass = Alunbar.class; System.out.println(alunbarClass.getName()); System.out.println(alunbarClass.getSimpleName()); } } 上面代码运行结果如下: cn.alunbar.Alunbar Alunbar getName()方法获取的类名包含包信息。getSimpleName()方法只是获取类名,不包含包信息。 获取类修饰符 public class Alunbar { public static void main(String arts[]){ Class alunbarClass = Alunbar.class; System.out.println(alunbarClass.getModifiers()); System.out.println(Modifier.isPublic(alunbarClass.getModifiers())); Class birdClass = Bird.class; System.out.println(birdClass.getModifiers()); System.out.println(Modifier.isPublic(birdClass.getModifiers())); } private class Bird{ } } 类修饰符有public、private等类型,getModifiers()可以获取一个类的修饰符,但是返回的结果是int,结合Modifier提供的方法,就可以确认修饰符的类型。 Modifier.isAbstract(int modifiers) Modifier.isFinal(int modifiers) Modifier.isInterface(int modifiers) Modifier.isNative(int modifiers) Modifier.isPrivate(int modifiers) Modifier.isProtected(int modifiers) Modifier.isPublic(int modifiers) Modifier.isStatic(int modifiers) Modifier.isStrict(int modifiers) Modifier.isSynchronized(int modifiers) Modifier.isTransient(int modifiers) Modifier.isVolatile(int modifiers) 获取包信息 package cn.alunbar; public class Alunbar { public static void main(String arts[]){ Class birdClass = Bird.class; System.out.println(birdClass.getPackage()); } private class Bird{ } } getPackage()方法获取包信息。上面代码运行的结果: package cn.alunbar 获取父类的Class对象 public class Alunbar { public static void main(String arts[]){ Class birdClass = Bird.class; Class superclass = birdClass.getSuperclass(); System.out.println(superclass.getSimpleName()); } private class Bird extends Animal{ } private class Animal{ } } 上面代码运行的结果: Animal getSuperclass()方法返回的父类的Class对象。 获取接口信息 Class[] interfaces = birdClass.getInterfaces(); 一个类可以实现多个接口,所以getInterfaces()方法返回的是Class[]数组。 获取构造函数Constructor Class birdClass = Bird.class; Constructor[] constructors = birdClass.getConstructors(); 一个类会有多个构造函数,getConstructors()返回的是Constructor[]数组,包含了所有声明的用public修饰的构造函数。 如果你已经知道了某个构造的参数,可以通过下面的方法获取到回应的构造函数对象: public class Alunbar { public static void main(String arts[]){ Class birdClass = Bird.class; try{ Constructor constructors = birdClass.getConstructor(new Class[]{String.class}); }catch(NoSuchMethodException e){ } } private class Bird { public Bird(){ } public Bird(String eat){ } } } 上面获取构造函数的方式有2点需要注意: 获取构造函数的参数 Constructor constructors = birdClass.getConstructor(new Class[]{String.class}); Class[] parameterTypes = constructors.getParameterTypes(); 初始化对象 通过反射获取到构造器之后,通过newInstance()方法就可以生成类对象。 public class Alunbar { public static void main(String arts[]){ Class birdClass = Bird.class; try{ Constructor constructors = birdClass.getConstructor(new Class[]{String.class}); Bird bird = (Bird)constructors.newInstance("eat tea"); }catch(Exception e){ System.out.println("没有对应的构造函数"); } } class Bird { public Bird(){ } protected Bird(String eat){ } } } newinstance()方法接受可选数量的参数,必须为所调用的构造函数提供准确的参数。如果构造函数要求String的参数,在调用newinstance()方法是,必须提供String类型的参数。 获取Methods方法信息 Method[] metchods = birdClass.getMethods(); Method[] metchods1 = birdClass.getDeclaredMethods(); Method eatMetchod = birdClass.getMethod("eat",new Class[]{int.class}); Method eatMetchod1 = birdClass.getDeclaredMethod("eat",new Class[]{int.class}); 无参的getMethods()获取到所有public修饰的方法,返回的是Method[]数组。 Method eatMetchod = birdClass.getMethod("eat",null); 无参的getMethods()和getDeclaredMethods()都只能获取到类声明的成员方法,不能获取到继承父类的方法。 获取成员方法参数 Class birdClass = Bird.class; Class[] parameterTypes = eatMetchod1.getParameterTypes(); 获取成员方法返回类型 Class birdClass = Bird.class; Class returnType = eatMetchod1.getReturnType(); invoke()方法 Class birdClass = Bird.class; Constructor constructors1 = birdClass.getConstructor(); Method eatMetchod = birdClass.getMethod("eat",new Class[]{int.class}); System.out.println(eatMetchod.invoke(constructors1.newInstance(),2)); invoke方法有两个参数,第一个参数是要调用方法的对象,上面的代码中就是Bird的对象,第二个参数是调用方法要传入的参数。如果有多个参数,则用数组。 如果调用的是static方法,invoke()方法第一个参数就用null代替: public class Alunbar { public static void main(String arts[]){ try{ Class birdClass = Bird.class; Constructor constructors1 = birdClass.getConstructor(); Method eatMetchod = birdClass.getMethod("eat",new Class[]{int.class}); System.out.println(eatMetchod.invoke(null,2)); }catch(Exception e){ e.printStackTrace(); System.out.println("没有对应的构造函数"); } } } class Bird{ public static int eat(int eat){ return eat; } public Bird(){ } public Bird(String eat){ } private void talk(){} } class Animal{ public void run(){ } } 使用反射可以在运行时检查和调用类声明的成员方法,可以用来检测某个类是否有getter和setter方法。getter和setter是java bean必须有的方法。 public static void printGettersSetters(Class aClass){ Method[] methods = aClass.getMethods(); for(Method method : methods){ if(isGetter(method)) System.out.println("getter: " + method); if(isSetter(method)) System.out.println("setter: " + method); } } public static boolean isGetter(Method method){ if(!method.getName().startsWith("get")) return false; if(method.getParameterTypes().length != 0) return false; if(void.class.equals(method.getReturnType()) return false; return true; } public static boolean isSetter(Method method){ if(!method.getName().startsWith("set")) return false; if(method.getParameterTypes().length != 1) return false; return true; } 获取成员变量 Class birdClass = Bird.class; Field[] fields1 = birdClass.getFields(); Field[] fields2 = birdClass.getDeclaredFields(); Field fields3 = birdClass.getField("age"); Field fields4 = birdClass.getDeclaredField("age"); getFields()方法获取所有public修饰的成员变量,getField()方法需要传入变量名,并且变量必须是public修饰符修饰。 getDeclaredFields方法获取所有生命的成员变量,不管是public还是private。 获取成员变量类型 Field fields4 = birdClass.getDeclaredField("age"); Object fieldType = fields4.getType(); 成员变量赋值和取值 Class birdClass = Bird.class; Field fields3 = birdClass.getField("age"); Bird bird = new Bird(); Object value = fields3.get(bird); fields3.set(bird,value); 访问私有变量 使用 Class.getDeclaredField(String name)或者Class.getDeclaredFields()才能获取到私有变量。 package field; import java.lang.reflect.Field; public class PrivateField { protected String name; public PrivateField(String name){ this.name = name; } } public class PrivateFieldTest { public static void main(String args[])throws Exception{ Class privateFieldClass = PrivateField.class; Field privateName = privateFieldClass.getDeclaredField("name"); privateName.setAccessible(false); PrivateField privateField = new PrivateField("Alunbar"); String privateFieldValue = (String) privateName.get(privateField); System.out.println("私有变量值:" + privateFieldValue); } } 上面的代码有点需要注意:必须调用setAccessible(true)方法,这是针对私有变量而言,public和protected等都不需要。这个方法是允许通过反射访问类的私有变量。 访问私有方法 public class PrivateMethod { private String accesPrivateMethod(){ return "成功访问私有方法"; } } public class PrivateMethodTest { public static void main(String args[])throws Exception{ Class privateMethodClass = PrivateMethod.class; Method privateStringMethod = privateMethodClass.getDeclaredMethod("accesPrivateMethod",null); privateStringMethod.setAccessible(true); String returnValue = (String)privateStringMethod.invoke(new PrivateMethod(),null); System.out.println("returnValue = " + returnValue); } } 和访问私有变量一样,也要调用setAccessible(true)方法,允许通过反射访问类的私有方法。 访问类注解信息 访问类的所有注解信息: Class aClass = TheClass.class; Annotation[] annotations = aClass.getAnnotations(); for(Annotation annotation : annotations){ if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } } 访问类特定的注解信息: Class aClass = TheClass.class; Annotation annotation = aClass.getAnnotation(MyAnnotation.class); if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } 访问方法注解信息: Method method = ... //obtain method object Annotation[] annotations = method.getDeclaredAnnotations(); for(Annotation annotation : annotations){ if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } } 访问特定方法注解信息: Method method = ... // obtain method object Annotation annotation = method.getAnnotation(MyAnnotation.class); if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } 访问参数注解信息: Method method = ... //obtain method object Annotation[][] parameterAnnotations = method.getParameterAnnotations(); Class[] parameterTypes = method.getParameterTypes(); int i=0; for(Annotation[] annotations : parameterAnnotations){ Class parameterType = parameterTypes[i++]; for(Annotation annotation : annotations){ if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("param: " + parameterType.getName()); System.out.println("name : " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } } } Method.getParameterAnnotations()方法返回的是一个二维的Annotation数组,其中包含每个方法参数的注解数组。 访问类所有变量注解信息: Field field = ... //obtain field object Annotation[] annotations = field.getDeclaredAnnotations(); for(Annotation annotation : annotations){ if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } } 访问类某个特定变量的注解信息: Field field = ... // obtain method object Annotation annotation = field.getAnnotation(MyAnnotation.class); if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } 获取泛型信息 获取到 泛型方法返回类型 public class MyClass { protected List<String> stringList = ...; public List<String> getStringList(){ return this.stringList; } } 下面的代码使用反射检测getStringList()方法返回的是 Method method = MyClass.class.getMethod("getStringList",null); Type returnType = method.getGenericReturnType(); if(returnType instanceof ParameterizedType){ ParameterizedType type = (ParameterizedType) returnType; Type[] typeArguments = type.getActualTypeArguments(); for(Type typeArgument : typeArguments){ Class typeArgClass = (Class) typeArgument; System.out.println("typeArgClass = " + typeArgClass); } } 上面这段代码会打印: 泛型方法参数类型 public class MyClass { protected List<String> stringList = ...; public void setStringList(List<String> list){ this.stringList = list; } } Method类提供了getGenericParameterTypes()方法获取方法的泛型参数。 method = Myclass.class.getMethod("setStringList",List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for(Type genericParameterType : genericParameterTypes){ if(genericParameterType instanceof ParameterizedType){ ParameterizedType aType = (ParameterizedType) genericParameterType; Type[] parameterArgTypes = aType.getActualTypeArguments(); for(Type parameterArgType : parameterArgTypes){ Class parameterArgClass = (Class) parameterArgType; System.out.println("parameterArgClass = " + parameterArgClass); } } } 上面的代码会打印出 泛型变量类型 public class MyClass { public List<String> stringList = ...; } 通过反射的Filed对象获取到泛型变量的类型信息: Field field = MyClass.class.getField("stringList"); Type genericFieldType = field.getGenericType(); if(genericFieldType instanceof ParameterizedType){ ParameterizedType aType = (ParameterizedType) genericFieldType; Type[] fieldArgTypes = aType.getActualTypeArguments(); for(Type fieldArgType : fieldArgTypes){ Class fieldArgClass = (Class) fieldArgType; System.out.println("fieldArgClass = " + fieldArgClass); } } Field对象提供了getGenericType()方法获取到泛型变量。 动态代理 使用反射可以在运行时创建接口的动态实现,java.lang.reflect.Proxy类提供了创建动态实现的功能。我们把运行时创建接口的动态实现称为动态代理。 动态代理可以用于许多不同的目的,例如数据库连接和事务管理、用于单元测试的动态模拟对象以及其他类似aop的方法拦截等。 创建代理 调用java.lang.reflect.Proxy类的newProxyInstance()方法就可以常见动态代理,newProxyInstance()方法有三个参数: InvocationHandler handler = new MyInvocationHandler(); MyInterface proxy = (MyInterface) Proxy.newProxyInstance( MyInterface.class.getClassLoader(),new Class[] { MyInterface.class },handler); 运行上面代码后,proxy变量包含了MyInterface接口的动态实现。 对代理的所有调用都将由到实现了InvocationHandler接口的handler 对象来处理。 InvocationHandler public interface InvocationHandler{ Object invoke(Object proxy,Method method,Object[] args) throws Throwable; } 实现InvocationHandler接口的类: public class MyInvocationHandler implements InvocationHandler{ public Object invoke(Object proxy,Object[] args) throws Throwable { //do something "dynamic" } } 下面详细介绍传递给invoke方法的三个参数。 Object proxy参数,实现接口的动态代理对象。通常不需要这个对象。 Method method参数,表示在动态代理实现的接口上调用的方法。通过Method对象,可以获取到方法名,参数类型,返回类型等信息。 Object[] args参数,包含调用接口中实现的方法时传递给代理的参数值。注意:如果接口中的参数是int、long等基本数据时,这里的args必须使用Integer,Long等包装类型。 上面代码中会生成一个MyInterface接口的对象proxy,通过proxy对象调用的方法都会由MyInvocationHandler类的invoke方法处理。 动态代理使用场景: 本文重点介绍了如何通过反射获取到某个类的方法、成员变量、构造函数等信息,同时也介绍动态代理的用法,这些都是反射的基础功能,反射的其他功能里就不一一介绍了。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- java – Intellij IDEA持久变更列表(提交后)?
- vue实现输入框的模糊查询(节流函数的应用场景)
- Feign【替换默认的feign client】
- java.lang.IllegalStateException:无法检索unit
- java序列化与ObjectOutputStream和ObjectInputSt
- the import javax.jms cannot be resolved问题
- 春天 – JPA和DAO – 什么是标准方法?
- 使用maven一步一步构建spring mvc项目(图文详解
- java – Selenium的Eclipse缺少源代码
- 我可以删除任何隐式导入的Java库吗?