Java 反射示例教程
Java 反射提供了检查和修改应用程序运行时行为的能力。Java 中的反射是核心 Java 的高级主题之一。使用 Java 反射,我们可以在运行时检查类、接口、枚举,获取它们的结构、方法和字段信息,即使类在编译时无法访问。我们还可以使用反射来实例化对象、调用其方法、更改字段值。
Java 反射
Java 中的反射是一个非常强大的概念,虽然在普通编程中用处不大,但它是大多数 Java、J2EE 框架的支柱。一些使用 Java 反射的框架包括:
- JUnit - 使用反射来解析@Test注释以获取测试方法然后调用它。
- Spring - 依赖注入,更多信息请阅读Spring 依赖注入
- Tomcat Web 容器通过解析其 web.xml 文件和请求 URI 将请求转发到正确的模块。
- Eclipse自动完成方法名称
- 支柱
- 休眠
这个列表是无穷无尽的,它们都使用 Java 反射,因为所有这些框架都不知道也无法访问用户定义的类、接口、它们的方法等。我们不应该在已经可以访问类和接口的正常编程中使用反射,因为存在以下缺点。
- 性能差- 由于 Java 反射动态解析类型,因此它涉及扫描类路径来查找要加载的类等处理,从而导致性能缓慢。
- 安全限制- Reflection 需要运行时权限,而安全管理器下运行的系统可能无法获得这些权限。这可能会导致您的应用程序在运行时因安全管理器而失败。
- 安全问题- 使用反射,我们可以访问我们不应该访问的代码部分,例如,我们可以访问类的私有字段并更改其值。这可能是一个严重的安全威胁,并导致您的应用程序行为异常。
- High Maintenance - Reflection code is hard to understand and debug, also any issues with the code can’t be found at compile time because the classes might not be available, making it less flexible and hard to maintain.
In java, every object is either a primitive type or reference. All the classes, enums, arrays are reference types and inherit from java.lang.Object
. Primitive types are - boolean, byte, short, int, long, char, float, and double. java.lang.Class is the entry point for all the reflection operations. For every type of object, JVM instantiates an immutable instance of java.lang.Class
that provides methods to examine the runtime properties of the object and create new objects, invoke its method and get/set object fields. In this section, we will look into important methods of Class, for convenience, I am creating some classes and interfaces with inheritance hierarchy.
package com.journaldev.reflection;
public interface BaseInterface {
public int interfaceInt=0;
void method1();
int method2(String str);
}
package com.journaldev.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.journaldev.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{}
}
Let’s look at some of the important refection methods for classes.
Get Class Object
We can get Class of an object using three methods - through static variable class
, using getClass()
method of object and java.lang.Class.forName(String fullyClassifiedClassName)
. For primitive types and arrays, we can use static variable class
. Wrapper classes provide another static variable TYPE
to get the class.
// 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.journaldev.reflection.ConcreteClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(concreteClass.getCanonicalName()); // prints com.journaldev.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[][]
getCanonicalName()
returns the canonical name of the underlying class. Notice that java.lang.Class uses Generics, it helps frameworks in making sure that the Class retrieved is subclass of framework Base Class. Check out Java Generics Tutorial to learn about generics and its wildcards.
Get Super Class
getSuperclass() method on a Class object returns the super class of the class. If this Class represents either the Object class, an interface, a primitive type, or void, then null is returned. If this object represents an array class then the Class object representing the Object class is returned.
Class<?> superClass = Class.forName("com.journaldev.reflection.ConcreteClass").getSuperclass();
System.out.println(superClass); // prints "class com.journaldev.reflection.BaseClass"
System.out.println(Object.class.getSuperclass()); // prints "null"
System.out.println(String[][].class.getSuperclass());// prints "class java.lang.Object"
Get Public Member Classes
getClasses()
method of a Class representation of object returns an array containing Class objects representing all the public classes, interfaces and enums that are members of the class represented by this Class object. This includes public class and interface members inherited from superclasses and public class and interface members declared by the class. This method returns an array of length 0 if this Class object has no public member classes or interfaces or if this Class object represents a primitive type, an array class, or void.
Class<?>[] classes = concreteClass.getClasses();
//[class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum,
//interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface,
//class com.journaldev.reflection.BaseClass$BaseClassInnerClass,
//class com.journaldev.reflection.BaseClass$BaseClassMemberEnum]
System.out.println(Arrays.toString(classes));
Get Declared Classes
getDeclaredClasses()
method returns an array of Class objects reflecting all the classes and interfaces declared as members of the class represented by this Class object. The returned array doesn’t include classes declared in inherited classes and interfaces.
//getting all of the classes, interfaces, and enums that are explicitly declared in ConcreteClass
Class<?>[] explicitClasses = Class.forName("com.journaldev.reflection.ConcreteClass").getDeclaredClasses();
//prints [class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultEnum,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPrivateClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassProtectedClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicClass,
//class com.journaldev.reflection.ConcreteClass$ConcreteClassPublicEnum,
//interface com.journaldev.reflection.ConcreteClass$ConcreteClassPublicInterface]
System.out.println(Arrays.toString(explicitClasses));
Get Declaring Class
getDeclaringClass()
method returns the Class object representing the class in which it was declared.
Class<?> innerClass = Class.forName("com.journaldev.reflection.ConcreteClass$ConcreteClassDefaultClass");
//prints com.journaldev.reflection.ConcreteClass
System.out.println(innerClass.getDeclaringClass().getCanonicalName());
System.out.println(innerClass.getEnclosingClass().getCanonicalName());
Getting Package Name
getPackage()
method returns the package for this class. The class loader of this class is used to find the package. We can invoke getName()
method of Package to get the name of the package.
//prints "com.journaldev.reflection"
System.out.println(Class.forName("com.journaldev.reflection.BaseInterface").getPackage().getName());
Getting Class Modifiers
getModifiers()
method returns the int representation of the class modifiers, we can use java.lang.reflect.Modifier.toString()
method to get it in the string format as used in source code.
System.out.println(Modifier.toString(concreteClass.getModifiers())); //prints "public"
//prints "public abstract interface"
System.out.println(Modifier.toString(Class.forName("com.journaldev.reflection.BaseInterface").getModifiers()));
Get Type Parameters
getTypeParameters()
returns the array of TypeVariable if there are any Type parameters associated with the class. The type parameters are returned in the same order as declared.
//Get Type parameters (generics)
TypeVariable<?>[] typeParameters = Class.forName("java.util.HashMap").getTypeParameters();
for(TypeVariable<?> t : typeParameters)
System.out.print(t.getName()+",");
Get Implemented Interfaces
getGenericInterfaces()
method returns the array of interfaces implemented by the class with generic type information. We can also use getInterfaces()
to get the class representation of all the implemented interfaces.
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()));
Get All Public Methods
getMethods()
method returns the array of public methods of the Class including public methods of it’s superclasses and super interfaces.
Method[] publicMethods = Class.forName("com.journaldev.reflection.ConcreteClass").getMethods();
//prints public methods of ConcreteClass, BaseClass, Object
System.out.println(Arrays.toString(publicMethods));
Get All Public Constructors
getConstructors()
method returns the list of public constructors of the class reference of object.
//Get All public constructors
Constructor<?>[] publicConstructors = Class.forName("com.journaldev.reflection.ConcreteClass").getConstructors();
//prints public constructors of ConcreteClass
System.out.println(Arrays.toString(publicConstructors));
Get All Public Fields
getFields()
method returns the array of public fields of the class including public fields of it’s super classes and super interfaces.
//Get All public fields
Field[] publicFields = Class.forName("com.journaldev.reflection.ConcreteClass").getFields();
//prints public fields of ConcreteClass, it's superclass and super interfaces
System.out.println(Arrays.toString(publicFields));
Get All Annotations
getAnnotations()
method returns all the annotations for the element, we can use it with class, fields and methods also. Note that only annotations available with reflection are with retention policy of RUNTIME, check out Java Annotations Tutorial. We will look into this in more details in later sections.
java.lang.annotation.Annotation[] annotations = Class.forName("com.journaldev.reflection.ConcreteClass").getAnnotations();
//prints [@java.lang.Deprecated()]
System.out.println(Arrays.toString(annotations));
Reflection API provides several methods to analyze Class fields and modify their values at runtime, in this section we will look into some of the commonly used reflection functions for methods.
Get Public Field
In last section, we saw how to get the list of all the public fields of a class. Reflection API also provides method to get specific public field of a class through getField()
method. This method look for the field in the specified class reference and then in the super interfaces and then in the super classes.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("interfaceInt");
Above call will return the field from BaseInterface that is implemented by ConcreteClass. If there is no field found then it throws NoSuchFieldException.
Field Declaring Class
We can use getDeclaringClass()
of field object to get the class declaring the field.
try {
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("interfaceInt");
Class<?> fieldClass = field.getDeclaringClass();
System.out.println(fieldClass.getCanonicalName()); //prints com.journaldev.reflection.BaseInterface
} catch (NoSuchFieldException | SecurityException e) {
e.printStackTrace();
}
Get Field Type
getType() method returns the Class object for the declared field type, if field is primitive type, it returns the wrapper class object.
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("publicInt");
Class<?> fieldType = field.getType();
System.out.println(fieldType.getCanonicalName()); //prints int
Get/Set Public Field Value
We can get and set the value of a field in an Object using reflection.
Field field = Class.forName("com.journaldev.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
get() method return Object, so if field is primitive type, it returns the corresponsing Wrapper Class. If the field is static, we can pass Object as null in get() method. There are several set*() methods to set Object to the field or set different types of primitive types to the field. We can get the type of field and then invoke correct function to set the field value correctly. If the field is final, the set() methods throw java.lang.IllegalAccessException.
Get/Set Private Field Value
We know that private fields and methods can’t be accessible outside of the class but using reflection we can get/set the private field value by turning off the java access check for field modifiers.
Field privateField = Class.forName("com.journaldev.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"
Using reflection we can get information about a method and we can invoke it also. In this section, we will learn different ways to get a method, invoke a method and accessing private methods.
Get Public Method
We can use getMethod() to get a public method of class, we need to pass the method name and parameter types of the method. If the method is not found in the class, reflection API looks for the method in superclass. In below example, I am getting put() method of HashMap using reflection. The example also shows how to get the parameter types, method modifiers and return type of a method.
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"
Invoking Public Method
We can use invoke() method of Method object to invoke a method, in below example code I am invoking put method on HashMap using reflection.
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}
If the method is static, we can pass NULL as object argument.
Invoking Private Methods
We can use getDeclaredMethod() to get the private method and then turn off the access check to invoke it, below example shows how we can invoke method3() of BaseClass that is static and have no parameters.
//invoking private method
Method method = Class.forName("com.journaldev.reflection.BaseClass").getDeclaredMethod("method3", null);
method.setAccessible(true);
method.invoke(null, null); //prints "Method3"
Reflection API provides methods to get the constructors of a class to analyze and we can create new instances of class by invoking the constructor. We have already learned how to get all the public constructors.
Get Public Constructor
We can use getConstructor() method on the class representation of object to get specific public constructor. Below example shows how to get the constructor of ConcreteClass defined above and the no-argument constructor of HashMap. It also shows how to get the array of parameter types for the constructor.
Constructor<?> constructor = Class.forName("com.journaldev.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 "[]"
Instantiate Object using Constructor
We can use newInstance() method on the constructor object to instantiate a new instance of the class. Since we use reflection when we don’t have the classes information at compile time, we can assign it to Object and then further use reflection to access it’s fields and invoke it’s methods.
Constructor<?> constructor = Class.forName("com.journaldev.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);
Java 1.5 中引入了注释,用于提供类、方法或字段的元数据信息,现在它在 Spring 和 Hibernate 等框架中被广泛使用。反射 API 也得到了扩展,以提供在运行时分析注释的支持。使用反射 API,我们可以分析保留策略为运行时的注释。我已经写了一篇关于注释以及如何使用反射 API 来解析注释的详细教程,所以我建议你看看Java 注释教程。这就是 Java 反射示例教程的全部内容,我希望你喜欢本教程并理解 Java 反射 API 的重要性。