前言 Java 的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法; 并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
理解 假如你写了一段代码:Object o=new Object();运行了起来!
首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。
jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。
上面的流程就是你自己写好的代码扔给jvm去跑,跑完就over了,jvm关闭,你的程序也停止了。
反射能够做到什么那?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载。
上面是说反射在创建实例方面能带来的改变,当然,无论反射和new方式,有一点是绕不过的,就是Class对象。每个类只有一个class对象,作为方法区类的数据结构的接口。
反射能够获取类的所有属性和方法,也是依靠Class对象。
使用 获取类名 1 2 3 4 5 6 7 8 9 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()); } }
getName()方法获取的类名包含包信息。getSimpleName()方法只是获取类名,不包含包信息。
获取类修饰符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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()可以获取一个类的修饰符
获取包信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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()方法获取包信息
获取父类的Class对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 { } }
getSuperclass()方法返回的父类的Class对象。
获取接口信息 获取接口信息的方法:
Class[] interfaces = birdClass.getInterfaces();
一个类可以实现多个接口,所以getInterfaces()方法返回的是Class[]数组。 注意:getInterfaces()只返回指定类实现的接口,不会返父类实现的接口。
获取构造函数Constructor 获取构造函数的方法:
Class birdClass = Bird.class; Constructor[] constructors = birdClass.getConstructors();
一个类会有多个构造函数,getConstructors()返回的是Constructor[]数组,包含了所有声明的用public修饰的构造函数。
如果你已经知道了某个构造的参数,可以通过下面的方法获取到回应的构造函数对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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点需要注意: 1、只能获取到public修饰的构造函数。 2、需要捕获NoSuchMethodException异常。
获取构造函数的参数 获取到构造函数的对象之后,可以通过getParameterTypes()获取到构造函数的参数。
1 2 Constructor constructors = birdClass.getConstructor(new Class[]{String.class }) ; Class[] parameterTypes = constructors.getParameterTypes();
初始化对象 通过反射获取到构造器之后,通过newInstance()方法就可以生成类对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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方法信息 下面代码是通过反射可以获取到该类的声明的成员方法信息:
1 2 3 4 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[]数组。 无参的getDeclaredMethods()方法到的是所有的成员方法,和修饰符无关。 对于有参的getMethods()方法,必须提供要获取的方法名以及方法名的参数。
如果要获取的方法没有参数,则用null替代:
1 Method eatMetchod = birdClass.getMethod("eat" , null );
无参的getMethods()和getDeclaredMethods()都只能获取到类声明的成员方法,不能获取到继承父类的方法。
获取成员方法参数 1 2 Class birdClass = Bird.class ; Class[] parameterTypes = eatMetchod1.getParameterTypes();
获取成员方法返回类型 1 2 Class birdClass = Bird.class ; Class returnType = eatMetchod1.getReturnType();
invoke()方法 java反射提供invoke()方法,在运行时根据业务需要调用相应的方法,这种情况在运行时非常常见,只要通过反射获取到方法名之后,就可以调用对应的方法:
1 2 3 4 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代替:
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 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必须有的方法。 getter和setter方法有下面的一些规律: getter方法以get为前缀,无参,有返回值 setter方法以set为前缀,有一个参数,返回值可有可无, 下面的代码提供了检测一个类是否有getter和setter方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 ; }
获取成员变量 通过反射可以在运行时获取到类的所有成员变量,还可以给成员变量赋值和获取成员变量的值。
1 2 3 4 5 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。
获取成员变量类型 1 2 Field fields4 = birdClass.getDeclaredField("age" ); Object fieldType = fields4.getType();
成员变量赋值和取值 一旦获取到成员变量的Field引用,就可以获取通过get()方法获取变量值,通过set()方法给变量赋值:
1 2 3 4 5 Class birdClass = Bird.class ; Field fields3 = birdClass.getField("age" ); Bird bird = new Bird(); Object value = fields3.get(bird); fields3.set(bird, value);
访问私有变量 有很多文章讨论禁止通过反射访问一个对象的私有变量,但是到目前为止所有的jdk还是允许通过反射访问私有变量。
使用 Class.getDeclaredField(String name)或者Class.getDeclaredFields()才能获取到私有变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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等都不需要。这个方法是允许通过反射访问类的私有变量。
访问私有方法 和私有变量一样,私有方法也是不允许其他的类随意调用的,但是通过反射可以饶过这一限制。 使用Class.getDeclaredMethod(String name, Class[] parameterTypes)或者Class.getDeclaredMethods()方法获取到私有方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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)方法,允许通过反射访问类的私有方法。
访问类注解信息 通过反射可以在运行时获取到类、方法、变量和参数的注解信息。
访问类的所有注解信息:
1 2 3 4 5 6 7 8 9 10 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()); } }
访问类特定的注解信息: 1 2 3 4 5 6 7 8 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()); }
访问方法注解信息: 1 2 3 4 5 6 7 8 9 10 Method method = ... 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()); } }
访问特定方法注解信息: 1 2 3 4 5 6 7 8 Method method = ... 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()); }
访问参数注解信息: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Method method = ... 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数组,其中包含每个方法参数的注解数组。
访问类所有变量注解信息: 1 2 3 4 5 6 7 8 9 10 Field field = ... 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()); } }
访问类某个特定变量的注解信息: 1 2 3 4 5 6 7 8 Field field = ... 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()); }
获取泛型信息 很多人认为java类在编译的时候会把泛型信息给擦除掉,所以在运行时是无法获取到泛型信息的。其实在某些情况下,还是可以通过反射在运行时获取到泛型信息的。
获取到java.lang.reflect.Method对象,就有可能获取到某个方法的泛型返回信息。
泛型方法返回类型 下面的类中定义了一个返回值中有泛型的方法:
1 2 3 4 5 6 7 8 public class MyClass { protected List<String> stringList = ...; public List<String> getStringList () { return this .stringList; } }
下面的代码使用反射检测getStringList()方法返回的是List而不是List
1 2 3 4 5 6 7 8 9 10 11 12 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); } }
上面这段代码会打印:typeArgClass = java.lang.String
泛型方法参数类型 下面的类定义了一个有泛型参数的方法setStringList():
1 2 3 4 5 6 7 public class MyClass { protected List<String> stringList = ...; public void setStringList (List<String> list) { this .stringList = list; } }
Method类提供了getGenericParameterTypes()方法获取方法的泛型参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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); } } }
上面的代码会打印出parameterArgType = java.lang.String
泛型变量类型 通过反射也可以获取到类的成员泛型变量信息——静态变量或实例变量。下面的类定义了一个泛型变量:
1 2 3 public class MyClass { public List<String> stringList = ...; }
通过反射的Filed对象获取到泛型变量的类型信息:
1 2 3 4 5 6 7 8 9 10 11 12 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()方法获取到泛型变量。 上面的代码会打印出:fieldArgClass = java.lang.String
问题
性能问题
java反射的性能并不好,原因主要是编译器没法对反射相关的代码做优化。
安全问题
我们知道单例模式的设计过程中,会强调将构造器设计为私有,因为这样可以防止从外部构造对象。但是反射可以获取类中的域、方法、构造器,修改访问权限。所以这样并不一定是安全的。
代理