java反射详解

1、 什么是反射?

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

本教程将深入介绍Java反射。解释Java反射的基础知识,包括如何使用数组,注解,泛型和动态代理。展示如何执行更具体的java反射,例如读取类的所有getter方法,或访问类的私有字段和方法。

2、 反射思维导图:

image

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相关文章,欢迎关注!

你可能感兴趣的:(java反射详解)