针对字段的Java反射
反射API提供了几种方法来分析类字段并在运行时修改它们的值,在本节中,我们将研究一些常用的方法反射函数。
获取公共字段
在上一篇文章中,我们看到了如何获取类的所有公共字段的列表。反射API还提供了通过getField()
方法获取类的特定公共字段的方法。这个方法先在指定的类引用中查找字段,然后在超级接口中查找,然后在超级类中查找。
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("interfaceInt");
上面的调用将从由ConcreteClass
实现的BaseInterface
返回字段。如果没有找到字段,则抛出NoSuchFieldException
。
字段声明类
我们可以使用field
对象的getDeclaringClass()
来获取声明字段的类。
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();
}
获取字段类型
方法返回声明的字段类型的类对象,如果字段是基元类型 primitive type
,则返回包装类对象。
Field field = Class.forName("com.journaldev.reflection.ConcreteClass").getField("publicInt");
Class<?> fieldType = field.getType();
System.out.println(fieldType.getCanonicalName()); //prints int
获取/设置公共字段值
我们可以使用反射来获取和设置对象中的场值。
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()
方法返回对象,因此如果field是基元类型,则返回相应的包装类。如果字段是静态的,我们可以在get()
方法中将对象作为null传递。
有几种set*()
方法可以将Object设置为字段或为字段设置不同类型的基元类型。我们可以获得字段的类型,然后调用正确的函数来正确设置字段值。如果字段是final
,则set()
方法抛出java.lang.IllegalAccessException
。
获取/设置私有字段值
我们知道私有字段和方法不能在类之外访问,但是使用反射,我们可以通过关闭对字段修饰符的java访问检查来获取/设置私有字段值。
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"
方法的Java反射
使用反射,我们可以获得有关方法的信息,也可以调用它。在本节中,我们将学习获取方法、调用方法和访问私有方法的不同方法。
获取公共方法
我们可以使用getMethod()
来获取类的一个公共方法,我们需要传递该方法的方法名和参数类型。如果在类中找不到该方法,则反射API将在超类中查找该方法。
在下面的示例中,我使用反射获取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"
调用公共方法
我们可以使用方法对象的invoke()
方法来调用方法,在下面的示例代码中,我使用反射调用HashMap
上的put
方法。
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}
如果方法是静态的,我们可以传递NULL作为对象参数。
调用私有方法
我们可以使用getDeclaredMethod()
来获取私有方法,然后关闭访问检查来调用它,下面的示例演示了如何调用基类的method3()
,它是静态的,没有参数。
//invoking private method
Method method = Class.forName("com.journaldev.reflection.BaseClass").getDeclaredMethod("method3", null);
method.setAccessible(true);
method.invoke(null, null); //prints "Method3"
构造函数的Java反射
反射API提供了一些方法来获取要分析的类的构造函数,我们可以通过调用构造函数来创建类的新实例。我们已经学会了如何让所有的公共建设者。
获取公共构造函数
我们可以在对象的类表示上使用getConstructor()
方法来获取特定的公共构造函数。下面的示例演示如何获取上面定义的ConcreteClass
的构造函数和HashMap的无参数构造函数。它还显示了如何获取构造函数的参数类型数组。
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 "[]"
使用构造函数实例化对象
我们可以在构造函数对象上使用newInstance()
方法实例化类的新实例。由于我们在编译时没有类信息时使用反射,所以我们可以将其分配给Object
,然后进一步使用反射访问它的字段并调用它的方法。
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);
注释的反射
现在在Spring.1的类中引入了大量的注释和方法,比如Hibernate的元数据和方法来提供信息。反射API也被扩展,以提供在运行时分析注释的支持。
使用反射API,我们可以分析保留策略为运行时的注释。
反射的其他用处还可以参考这篇文章:https://javakk.com/687.html