Java反射示例教程
Java Reflection提供了检查和修改应用程序运行时行为的功能。
Java中的反射是核心Java的高级主题之一。
使用java反射,即使在编译时无法访问类,我们也可以在运行时检查类,接口,枚举,获取其结构,方法和字段信息。
我们还可以使用反射来实例化对象,调用其方法,更改字段值。
Java反射
Java中的反射是一个非常强大的概念,在常规编程中很少使用,但它是大多数Java,J2EE框架的基础。
使用Java反射的一些框架是:
- Java中的反思
列表是无止境的,它们都使用Java反射,因为所有这些框架都不了解并且不能访问用户定义的类,接口,其方法等。
- 类的Java反射获取类对象
- 获得超级班
- 获取公共成员类
- 获取声明的类
- 获取声明类
- 获取软件包名称
由于以下缺点,我们不应在已经可以访问类和接口的常规编程中使用反射。
在Java中,每个对象都是原始类型或者引用。
所有的类,枚举,数组都是引用类型,并继承自java.lang.Object。
基本类型为-布尔,字节,短型,整数,长型,字符,浮点型和双精度型。
- 获取类修饰符
- 获取类型参数
- 获取已实现的接口
- 获取所有公共方法
- 获取所有公共构造函数
java.lang.Class是所有反射操作的入口点。
对于每种类型的对象,JVM实例化一个java.lang.Class的不可变实例,该实例提供方法来检查对象的运行时属性并创建新对象,调用其方法并获取/设置对象字段。
在本节中,我们将研究Class的重要方法,为方便起见,我将创建一些具有继承层次结构的类和接口。
让我们看一下课堂上一些重要的补充方法。
package com.theitroad.reflection; public interface BaseInterface { public int interfaceInt=0; void method1(); int method2(String str); }
package com.theitroad.reflection; public class BaseClass { public int baseInt; private static void method3(){ System.out.println("Method3"); } public int method4(){ System.out.println("Method4"); return 0; } public static int method5(){ System.out.println("Method5"); return 0; } void method6(){ System.out.println("Method6"); } //inner public class public class BaseClassInnerClass{} //member public enum public enum BaseClassMemberEnum{} }
package com.theitroad.reflection; @Deprecated public class ConcreteClass extends BaseClass implements BaseInterface { public int publicInt; private String privateString="private string"; protected boolean protectedBoolean; Object defaultObject; public ConcreteClass(int i){ this.publicInt=i; } @Override public void method1() { System.out.println("Method1 impl."); } @Override public int method2(String str) { System.out.println("Method2 impl."); return 0; } @Override public int method4(){ System.out.println("Method4 overriden."); return 0; } public int method5(int i){ System.out.println("Method4 overriden."); return 0; } //inner classes public class ConcreteClassPublicClass{} private class ConcreteClassPrivateClass{} protected class ConcreteClassProtectedClass{} class ConcreteClassDefaultClass{} //member enum enum ConcreteClassDefaultEnum{} public enum ConcreteClassPublicEnum{} //member interface public interface ConcreteClassPublicInterface{} }
我们可以使用三种方法来获取对象的类-通过静态变量class
,使用对象的getClass()
方法和java.lang.Class.forName(String fullyClassifiedClassName)
。
对于基本类型和数组,我们可以使用静态变量" class"。
包装器类提供了另一个静态变量" TYPE"来获取该类。
获取类对象
getCanonicalName()返回基础类的规范名称。
请注意,java.lang.Class使用泛型,它有助于框架确保所检索的Class是框架Base Class的子类。
查看Java Generics Tutorial,以了解泛型及其通配符。
//Get Class using reflection Class<?> concreteClass = ConcreteClass.class; concreteClass = new ConcreteClass(5).getClass(); try { //below method is used most of the times in frameworks like JUnit //Spring dependency injection, Tomcat web container //Eclipse auto completion of method names, hibernate, Struts2 etc. //because ConcreteClass is not available at compile time concreteClass = Class.forName("com.theitroad.reflection.ConcreteClass"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(concreteClass.getCanonicalName()); //prints com.theitroad.reflection.ConcreteClass //for primitive types, wrapper classes and arrays Class<?> booleanClass = boolean.class; System.out.println(booleanClass.getCanonicalName()); //prints boolean Class<?> cDouble = Double.TYPE; System.out.println(cDouble.getCanonicalName()); //prints double Class<?> cDoubleArray = Class.forName("[D"); System.out.println(cDoubleArray.getCanonicalName()); //prints double[] Class<?> twoDStringArray = String[][].class; System.out.println(twoDStringArray.getCanonicalName()); //prints java.lang.String[][]
Class对象的getSuperclass()方法返回该类的超类。
如果该Class表示Object类,接口,原始类型或者void,则返回null。
如果此对象表示数组类,则返回表示Object类的Class对象。
获得超级班
对象的Class表示形式的getClasses()方法返回一个数组,其中包含Class对象,这些Class对象表示作为此Class对象表示的类的成员的所有公共类,接口和枚举。
这包括从超类继承的公共类和接口成员,以及由该类声明的公共类和接口成员。
如果此Class对象没有公共成员类或者接口,或者该Class对象表示原始类型,数组类或者void,则此方法返回长度为0的数组。
Class<?> superClass = Class.forName("com.theitroad.reflection.ConcreteClass").getSuperclass(); System.out.println(superClass); //prints "class com.theitroad.reflection.BaseClass" System.out.println(Object.class.getSuperclass()); //prints "null" System.out.println(String[][].class.getSuperclass());//prints "class java.lang.Object"
获取公共成员类
getDeclaredClasses()方法返回一个Class对象数组,该数组反映所有声明为该Class对象表示的类成员的所有类和接口。
返回的数组不包含在继承的类和接口中声明的类。
Class<?>[] classes = concreteClass.getClasses(); //[class com.theitroad.reflection.ConcreteClass$ConcreteClassPublicClass, //class com.theitroad.reflection.ConcreteClass$ConcreteClassPublicEnum, //interface com.theitroad.reflection.ConcreteClass$ConcreteClassPublicInterface, //class com.theitroad.reflection.BaseClass$BaseClassInnerClass, //class com.theitroad.reflection.BaseClass$BaseClassMemberEnum] System.out.println(Arrays.toString(classes));
获取声明的类
getDeclaringClass()方法返回代表声明该类的类的Class对象。
//getting all of the classes, interfaces, and enums that are explicitly declared in ConcreteClass Class<?>[] explicitClasses = Class.forName("com.theitroad.reflection.ConcreteClass").getDeclaredClasses(); //prints [class com.theitroad.reflection.ConcreteClass$ConcreteClassDefaultClass, //class com.theitroad.reflection.ConcreteClass$ConcreteClassDefaultEnum, //class com.theitroad.reflection.ConcreteClass$ConcreteClassPrivateClass, //class com.theitroad.reflection.ConcreteClass$ConcreteClassProtectedClass, //class com.theitroad.reflection.ConcreteClass$ConcreteClassPublicClass, //class com.theitroad.reflection.ConcreteClass$ConcreteClassPublicEnum, //interface com.theitroad.reflection.ConcreteClass$ConcreteClassPublicInterface] System.out.println(Arrays.toString(explicitClasses));
获取声明类
getPackage()方法返回此类的包。
此类的类加载器用于查找包。
我们可以调用Package的getName()方法来获取包的名称。
Class<?> innerClass = Class.forName("com.theitroad.reflection.ConcreteClass$ConcreteClassDefaultClass"); //prints com.theitroad.reflection.ConcreteClass System.out.println(innerClass.getDeclaringClass().getCanonicalName()); System.out.println(innerClass.getEnclosingClass().getCanonicalName());
获取软件包名称
getModifiers()方法返回类修饰符的int表示形式,我们可以使用java.lang.reflect.Modifier.toString()方法以源代码中使用的字符串格式获取它。
//prints "com.theitroad.reflection" System.out.println(Class.forName("com.theitroad.reflection.BaseInterface").getPackage().getName());
获取类修饰符
如果有任何与该类关联的Type参数,则getTypeParameters()
将返回TypeVariable数组。
类型参数的返回顺序与声明的顺序相同。
System.out.println(Modifier.toString(concreteClass.getModifiers())); //prints "public" //prints "public abstract interface" System.out.println(Modifier.toString(Class.forName("com.theitroad.reflection.BaseInterface").getModifiers()));
获取类型参数
getGenericInterfaces()方法返回具有通用类型信息的类实现的接口数组。
我们也可以使用getInterfaces()获取所有已实现接口的类表示。
//Get Type parameters (generics) TypeVariable<?>[] typeParameters = Class.forName("java.util.HashMap").getTypeParameters(); for(TypeVariable<?> t : typeParameters) System.out.print(t.getName()+",");
获取已实现的接口
getMethods()方法返回Class的公共方法数组,包括其超类和超级接口的公共方法。
Type[] interfaces = Class.forName("java.util.HashMap").getGenericInterfaces(); //prints "[java.util.Map<K, V>, interface java.lang.Cloneable, interface java.io.Serializable]" System.out.println(Arrays.toString(interfaces)); //prints "[interface java.util.Map, interface java.lang.Cloneable, interface java.io.Serializable]" System.out.println(Arrays.toString(Class.forName("java.util.HashMap").getInterfaces()));
获取所有公共方法
getConstructors()方法返回对象的类引用的公共构造函数的列表。
Method[] publicMethods = Class.forName("com.theitroad.reflection.ConcreteClass").getMethods(); //prints public methods of ConcreteClass, BaseClass, Object System.out.println(Arrays.toString(publicMethods));
获取所有公共构造函数
" getFields()"方法返回该类的公共字段的数组,包括其超类和超级接口的公共字段。
//Get All public constructors Constructor<?>[] publicConstructors = Class.forName("com.theitroad.reflection.ConcreteClass").getConstructors(); //prints public constructors of ConcreteClass System.out.println(Arrays.toString(publicConstructors));
获取所有公共字段
getAnnotations()方法返回元素的所有注释,我们也可以将其与类,字段和方法一起使用。
请注意,只有反射可用的注释才具有RUNTIME的保留策略,请查看Java注释教程。
我们将在后面的部分中对此进行更详细的研究。
//Get All public fields Field[] publicFields = Class.forName("com.theitroad.reflection.ConcreteClass").getFields(); //prints public fields of ConcreteClass, it's superclass and super interfaces System.out.println(Arrays.toString(publicFields));
获取所有注释
Reflection API提供了几种方法来分析Class字段并在运行时修改它们的值,在本节中,我们将研究一些方法的常用反射函数。
java.lang.annotation.Annotation[] annotations = Class.forName("com.theitroad.reflection.ConcreteClass").getAnnotations(); //prints [@java.lang.Deprecated()] System.out.println(Arrays.toString(annotations));
- 获取所有公共领域
在上一节中,我们看到了如何获取类的所有公共字段的列表。
Reflection API还提供了通过getField()
方法获取类的特定公共字段的方法。
此方法在指定的类引用中查找字段,然后在超级接口中查找字段,然后在超级类中查找。
获取公共字段
上面的调用将返回由ConcreteClass实现的BaseInterface字段。
如果找不到任何字段,则抛出NoSuchFieldException。
Field field = Class.forName("com.theitroad.reflection.ConcreteClass").getField("interfaceInt");
我们可以使用字段对象的getDeclaringClass()
来获取声明字段的类。
字段声明类
getType()方法返回声明的字段类型的Class对象,如果field是原始类型,则返回包装类对象。
try { Field field = Class.forName("com.theitroad.reflection.ConcreteClass").getField("interfaceInt"); Class<?> fieldClass = field.getDeclaringClass(); System.out.println(fieldClass.getCanonicalName()); //prints com.theitroad.reflection.BaseInterface } catch (NoSuchFieldException | SecurityException e) { e.printStackTrace(); }
获取字段类型
我们可以使用反射获取并设置对象中字段的值。
Field field = Class.forName("com.theitroad.reflection.ConcreteClass").getField("publicInt"); Class<?> fieldType = field.getType(); System.out.println(fieldType.getCanonicalName()); //prints int
获取/设置公共字段值
get()方法返回Object,因此,如果field是原始类型,则它将返回相应的Wrapper类。
如果该字段是静态的,则可以在get()方法中将Object作为null传递。
Field field = Class.forName("com.theitroad.reflection.ConcreteClass").getField("publicInt"); ConcreteClass obj = new ConcreteClass(5); System.out.println(field.get(obj)); //prints 5 field.setInt(obj, 10); //setting field value to 10 in object System.out.println(field.get(obj)); //prints 10
有几种set *()方法可将Object设置为该字段或者将不同类型的基本类型设置为该字段。
我们可以获取字段的类型,然后调用正确的函数来正确设置字段值。
如果该字段为final,则set()方法将引发java.lang.IllegalAccessException。
我们知道,私有字段和方法无法在类外部访问,但是通过反射,我们可以通过关闭字段修饰符的Java访问检查来获取/设置私有字段的值。
获取/设置私有字段值
使用反射,我们可以获得有关方法的信息,我们也可以调用它。
在本节中,我们将学习获取方法,调用方法和访问私有方法的不同方法。
Field privateField = Class.forName("com.theitroad.reflection.ConcreteClass").getDeclaredField("privateString"); //turning off access check with below method call privateField.setAccessible(true); ConcreteClass objTest = new ConcreteClass(1); System.out.println(privateField.get(objTest)); //prints "private string" privateField.set(objTest, "private string updated"); System.out.println(privateField.get(objTest)); //prints "private string updated"
- 获取所有注释
我们可以使用getMethod()获取类的公共方法,我们需要传递方法名称和方法的参数类型。
如果在类中未找到该方法,则反射API在超类中查找该方法。
获取公共方法
在下面的示例中,我使用反射获取HashMap的put()方法。
该示例还显示了如何获取方法的参数类型,方法修饰符和返回类型。
我们可以使用Method对象的invoke()方法来调用方法,在下面的示例代码中,我使用反射在HashMap上调用put方法。
Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class); //get method parameter types, prints "[class java.lang.Object, class java.lang.Object]" System.out.println(Arrays.toString(method.getParameterTypes())); //get method return type, return "class java.lang.Object", class reference for void System.out.println(method.getReturnType()); //get method modifiers System.out.println(Modifier.toString(method.getModifiers())); //prints "public"
调用公共方法
如果该方法是静态的,则可以将NULL作为对象参数传递。
Method method = Class.forName("java.util.HashMap").getMethod("put", Object.class, Object.class); Map<String, String> hm = new HashMap<>(); method.invoke(hm, "key", "value"); System.out.println(hm); //prints {key=value}
我们可以使用getDeclaredMethod()获取私有方法,然后关闭访问检查以调用它,下面的示例显示了如何调用静态且没有参数的BaseClass的method3()。
调用私人方法
Reflection API提供了一些方法来获取类的构造函数进行分析,并且我们可以通过调用构造函数来创建类的新实例。
我们已经学习了如何获取所有公共构造函数。
//invoking private method Method method = Class.forName("com.theitroad.reflection.BaseClass").getDeclaredMethod("method3", null); method.setAccessible(true); method.invoke(null, null); //prints "Method3"
- Java字段的反射获取公共字段
我们可以在对象的类表示形式上使用getConstructor()方法来获取特定的公共构造函数。
下面的示例演示如何获取上面定义的ConcreteClass构造函数和HashMap的无参数构造函数。
它还显示了如何获取构造函数的参数类型数组。
获取公共构造函数
我们可以在构造函数对象上使用newInstance()方法来实例化该类的新实例。
由于在编译时没有类信息时会使用反射,因此可以将其分配给Object,然后进一步使用反射来访问其字段并调用其方法。
Constructor<?> constructor = Class.forName("com.theitroad.reflection.ConcreteClass").getConstructor(int.class); //getting constructor parameters System.out.println(Arrays.toString(constructor.getParameterTypes())); //prints "[int]" Constructor<?> hashMapConstructor = Class.forName("java.util.HashMap").getConstructor(null); System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes())); //prints "[]"
使用构造函数实例化对象
Java 1.5中引入了注释,以提供有关类,方法或者字段的元数据信息,现在它在诸如Spring和Hibernate之类的框架中大量使用。
反射API也进行了扩展,以提供在运行时分析注释的支持。
Constructor<?> constructor = Class.forName("com.theitroad.reflection.ConcreteClass").getConstructor(int.class); //getting constructor parameters System.out.println(Arrays.toString(constructor.getParameterTypes())); //prints "[int]" Object myObj = constructor.newInstance(10); Method myObjMethod = myObj.getClass().getMethod("method1", null); myObjMethod.invoke(myObj, null); //prints "Method1 impl." Constructor<?> hashMapConstructor = Class.forName("java.util.HashMap").getConstructor(null); System.out.println(Arrays.toString(hashMapConstructor.getParameterTypes())); //prints "[]" HashMap<String,String> myMap = (HashMap<String,String>) hashMapConstructor.newInstance(null);
使用反射API,我们可以分析其保留策略为Runtime的注释。