1、 什么是反射?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
本教程将深入介绍Java反射。解释Java反射的基础知识,包括如何使用数组,注解,泛型和动态代理。展示如何执行更具体的java反射,例如读取类的所有getter方法,或访问类的私有字段和方法。
2、 反射思维导图:
3、反射实例讲解
在获取类的信息之前,必须获取一个java.lang.Class对象。
两种方式获取:
Class myObjectClass = MyObject.class
Class class = Class.forName(className);
Class.forName里面的字符串必须提供完全限定的类名,也就是包含包名的类名,如cn.java.my.Test。如果运行时无法在类路径上找到类,则 该Class.forName()方法可能抛出一个ClassNotFoundException。
反过来,从一个Class对象获取它的类名,也有两种方式。
第一个getName()是获取完全限定的类名。
Class aClass = MyObject.class
String className = aClass.getName();
第二个getSimpleName()仅仅是获取类名,没有包的名称。
Class aClass= MyObject.class
String simpleClassName = aClass.getSimpleName();
我们也可以通过Class来获取类的限定符(如public、private等)。
Class aClass = MyObject.class
int modifiers = aClass.getModifiers();
所有限定符都被包装成了int,我们可以用 java.lang.reflect.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)
获取包的信息:
Class aClass = MyObject.class
Package package = aClass.getPackage();
获取超类:
Class superclass = aClass.getSuperclass();
超类类对象是一个像任何其他对象一样的Class,因此也可以继续对其进行类反射。
获取类对象实现的接口,一个类可以实现多个接口,所以用Class数组进行返回值接收。
Class aClass = MyObject.class
Class[] interfaces = aClass.getInterfaces();
访问类的方法:
Method [] method = aClass.getMethods();
主要利用java.lang.reflect.Method这个类。该Method[]数组将为Method该类中声明的每个公共方法提供一个实例。
知道要访问的方法的精确参数类型,则可以这样做,而不是获取所有方法的数组。此示例返回名为“doSomething”的公共方法,在给定的类中采用String作为参数:
Class aClass = MyObject.class
Method method = aClass.getMethod(“doSomething”,new Class [] {String.class});
如果没有方法匹配给定的方法名和参数,在这种情况下String.class,一个NoSuchMethodException被抛出。
如果访问的方法不带参数,则传递null为参数类型数组,如下所示:
Class aClass = MyObject.class
Method method = aClass.getMethod(“doSomething”,null);
读取给定方法采用的参数,如下所示:
Method method = aClass.getMethod(“doSomething”,null);
Class[] parameterTypes = method.getParameterTypes();
访问如下方法的返回类型:
Method method = aClass.getMethod(“doSomething”,null);
Class returnType = method.getReturnType();
调用方法:
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");
null参数是要调用方法的对象。如果方法是静态的,则提供null而不是对象实例。
- Getter
getter方法的名称以“get”开头,不需要参数参数,并返回一个值。 - Setter
一个setter方法的名称以“set”开头,并带有1个参数。
Setter可能会也可能不会返回值,不应该对setter的返回类型做出任何假设。
下面是一个判断get/set方法的例子。
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;
}
访问类的构造函数:
Constructor [] constructors = aClass.getConstructors();
Constructor[]数组将为Constructor类中声明的每个公共构造函数提供一个实例。
如果知道访问的构造函数的精确参数类型,则可以这样做,而不是获取所有构造函数的数组。此示例返回给定类的公共构造函数,该构造函数采用String作为参数:
Class aClass = MyObject.class
Constructor constructor =aClass.getConstructor(new Class[]{String.class});
如果没有构造函数在给定构造函数的参数相匹配,在这种情况下String.class,一个NoSuchMethodException被抛出。
读取给定构造函数采用的参数,如下所示:
Constructor constructor = aClass.getConstructor(new Class[]{String.class});
Class[] parameterTypes = constructor.getParameterTypes();
Constructor.newInstance() 方法采用可选数量的参数,但必须在要调用的构造函数中为每个参数提供一个参数。在这种情况下,它是一个构造函数String
Constructor constructor = MyObject.class.getConstructor(String.class);
MyObject myObject = (MyObject)constructor.newInstance("constructor-arg1");
访问类的字段(成员变量),如下所示:
Field [] method = aClass.getFields();
Field[]数组将为Field该类中声明的每个公共字段提供一个实例。
如果知道要访问的字段的名称,则可以像这样访问它:
Class aClass = MyObject.class
Field field = aClass.getField(“someField”);
使用该Field.getName()方法获取其字段名称 ,如下所示:
Field field = aClass.getField(“someField”);
String fieldName = field.getName();
Field.getType() 方法确定字段的字段类型(String,int等):
Field field = aClass.getField(“someField”);
Object fieldType = field.getType();
获得Field引用后,可以使用Field.get()和Field.set()方法获取并设置其值 ,如下所示:
Class aClass = MyObject.class
Field field = aClass.getField(“someField”);
MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance);
field.set(objetInstance,value);
访问私有字段
需要调用Class.getDeclaredField(String name) or Class.getDeclaredFields()方法。方法Class.getField(String name) 和Class.getFields()方法只返回公共字段,因此它们不起作用。下面是一个带有私有字段的类的简单示例,下面是通过反射访问该字段的代码:
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Field privateStringField = PrivateObject.class.
getDeclaredField("privateString");
privateStringField.setAccessible(true);
String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue);
访问私有方法
需要调用Class.getDeclaredMethod(String name, Class[] parameterTypes) or Class.getDeclaredMethods()方法。方法Class.getMethod(String name, Class[] parameterTypes) 和Class.getMethods()方法只返回公共方法,因此它们不起作用。下面是一个带有私有方法的类的简单示例,下面是通过反射访问该方法的代码:
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
private String getPrivateString(){
return this.privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Method privateStringMethod = PrivateObject.class.
getDeclaredMethod("getPrivateString", null);
privateStringMethod.setAccessible(true);
String returnValue = (String)
privateStringMethod.invoke(privateObject, null);
System.out.println("returnValue = " + returnValue);
访问类的类注解,如下所示:
Annotation [] annotations = aClass.getAnnotations();
注解MyAnnotation定义
public @interface MyAnnotation {
public String name();
public String value();
}
访问类注解的示例:
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());
}
以下是带注解的方法示例:
public class TheClass {
@MyAnnotation(name =“someName”,value =“Hello World”)
public void doSomething(){}
}
访问方法注解,如下所示:
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());
}
}
访问特定的方法注解:
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());
}
为方法参数声明添加注解
public class TheClass {
public static void doSomethingElse(
@MyAnnotation(name="aName", value="aValue") String parameter){
}
}
Method对象访问参数注解
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());
}
}
}
以下是带注解的字段示例:
public class TheClass {
@MyAnnotation(name =“someName”,value =“Hello World”)
public String myField = null;
}
访问字段注解:
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());
}
}
访问特定的字段注解
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 stringList = ...;
public List 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);
}
}
动态代理
通过Proxy.newProxyInstance()来创建动态代理,这个方法需要三个参数:
加载动态代理的类的类加载器。
需要实现的接口数组
实现InvocationHandler类一个handle类,如下例子:
public class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//do something "dynamic"
}
}
这里有一个动态代理的例子。
public interface StartInterface {
void sing();
void play();
void run();
void collectMoney();
}
public class Star implements StartInterface{
@Override
public void sing() {
System.out.println("star sing!");
}
@Override
public void play() {
System.out.println("start play!");
}
@Override
public void run() {
System.out.println("star run!");
}
@Override
public void collectMoney() {
System.out.println("star collectMoney!");
}
}
public class StarHandle implements InvocationHandler {
private StartInterface star;
public StarHandle(StartInterface star) {
this.star = star;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sing")) {
method.invoke(star, args);
return null;
}
return null;
}
}
public class DynamicTest {
public static void main(String[] args) {
StartInterface star = new Star();
StarHandle starHandle = new StarHandle(star);
StartInterface obj =(StartInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
new Class[]{StartInterface.class},starHandle);
obj.sing();
obj.play();
}
}
最后输出:
star sing!
这里就是StartInterface代理到了Star类上,所以调用方法都是调用到了Star类的方法上,
3、 反射的应用场景
Java 反射可用于将JSON文件中的属性映射到Java对象中的getter / setter方法,如Jackson,GSON,Boon等 。或者,反射可用于将JDBC ResultSet 的列名映射到Java对象中的getter / setter方法。在spring创建单例bean的时候,也是利用反射来实现的。许多java框架的底层都用到了反射,反射构成了框架的基础。
本专栏定期发布java相关文章,欢迎关注!