美文网首页Java
Java 反射机制(一)

Java 反射机制(一)

作者: mghio | 来源:发表于2019-12-29 22:35 被阅读0次

前言

Java 中有两种方式可以让我们在运行时识别对象和类的信息。一种是 RTTI(运行时类型识别:Run-Time Type Identification),它假定了我们在编译时已经知道了所有的类型;另一种是我们本文要说的反射机制,它允许我们在运行时获取和使用类的信息。无论是 RTTI 还是反射,其本质都是一样的,都是去动态的获取类的信息。它们唯一不同的是,RTTI 在编译时期知道要解析的类型,而反射是在运行时才知道要解析的类型。

反射概述

反射就是把 Java 类中的各个部分(属性、方法、构造方法等)映射成一个个对象。Class 类与 java.lang.reflect 类库一起对反射的概念提供了支持,类库中包含了 FieldMethodConstructor 类,每个类都实现了 Member 接口。这些类型的对象都是由 JVM 运行时创建的,用来表示未知类里对应的成员。这样我们就可以使用 Constructor 创建新的对象,用 getset 方法读取和修改类中与 Field 对象关联的字段,用 invoke 方法调用类中与 Method 对象关联的方法等。
Java 反射机制是在运行状态中的,对于任意一个类我们可以通过反射获取这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。重要的是,要认识到反射机制并没有什么特别之处,当我们通过反射和一个未知类型的对象打交道时,JVM 只是简单的对这个对象做检查,看它属于哪个类,在用它做其它事情之前必须先加载那个类 Class 对象。所以那个类的字节码文件对象对于 JVM 来说必须是可获取的,要么在本地机器上,要么通过网络获取。

反射 API 的使用

想要通过反射获取一个类的信息之前,首先要先获取这个类的 Class 对象,在 Java 中所有类型都有与之关联的 Class 对象。

获取类的 Class 对象

Java 中获取一个类的 Class 对象有三种方式:
第 ① 种 使用 Class 类的 forName 静态方法,当我们知道一个类的全路径时,可以通过 Class.forName 方法获取类的 Class 对象。

Class stringClass = Class.forName("java.lang.String");
System.out.println(stringClass);

运行结果

class java.lang.String

第 ② 种 使用 .class 获取,这种方式只适合在编译前就已经知道了要操作的 Class

Class stringClass = String.class;
System.out.println(stringClass);

运行结果

class java.lang.String

第 ③ 种 使用 getClass() 方法获取

Class stringClass = "mghio".getClass();
System.out.println(stringClass);

运行结果

class java.lang.String
通过反射创建类对象

通过反射创建类对象有两种方式:

第 ① 种 通过调用 Class 对象的 newInstance() 方法创建

Class<Person> personClass = Person.class;
Person person = personClass.newInstance();

第 ② 种 通过调用 Constructor 对象的 newInstance() 方法创建

Class<Person> personClass = Person.class;
Constructor personConstructor = personClass.getConstructor();
Person person = (Person) personConstructor.newInstance();

两者的区别是,通过 ClassnewInstance 方法只能通过无参构造方法创建,这就要求这个类必须有一个无参的构造方法,而通过 ConstructornewInstance 可以指定参数来选择特定的构造方法来创建对象。以下代码就是指定参数然后通过特定的构造方法创建对象的。

Class<Person> personClass = Person.class;
Constructor personConstructor = personClass.getConstructor();
Person person = (Person) personConstructor.newInstance("mghio", "中国上海");
通过反射获取类的属性

Class 类提供了两种方式获取一个类的属性,第 ① 种是通过 Class 对象的 getFields 方法获取类的属性,该方法只能获取类的 public 属性。

Class<Person> personClass = Person.class;
Field[] fields = personClass.getFields();
System.out.println(Arrays.toString(fields));

运行结果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.id, 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.name]

第 ② 种是通过 Class 对象的 getDeclaredFields 方法获取类的属性,该方法可以获取类的所有属性(包括 private 修饰的属性)。

Class<Person> personClass = Person.class;
Field[] fields = personClass.getDeclaredFields();
System.out.println(Arrays.toString(fields));

运行结果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.id, 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.name, 
protected java.lang.Integer cn.mghio.blogmghiocode.reflect.Person.age, 
private java.lang.String cn.mghio.blogmghiocode.reflect.Person.address]
通过反射获取类的方法

Class 也提供了两种方式获取类的方法,第 ① 种是通过 Class 对象的 getMethods 方法获取类的方法(包括继承而得的方法)。

Class<Person> personClass = Person.class;
Method[] methods = personClass.getMethods();
System.out.println(Arrays.toString(methods));

运行结果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.toString(), 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.getAddress(), 
...
public final native java.lang.Class java.lang.Object.getClass(), 
public final native void java.lang.Object.notify()]

第 ② 种是通过 Class 对象的 getDeclaredMethods 方法获取类的方法(只包含类中定义的方法,不包含继承而来的方法)。

Class<Person> personClass = Person.class;
Method[] methods = personClass.getDeclaredMethods();
System.out.println(Arrays.toString(methods));

运行结果

[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.toString(), 
public java.lang.String cn.mghio.blogmghiocode.reflect.Person.getAddress(), 
... 
protected void cn.mghio.blogmghiocode.reflect.Person.protectedMethod(), 
private void cn.mghio.blogmghiocode.reflect.Person.privateMethod()]

从以上结果可以看出这个方法只获取当前类中定义的方法,包含 private 方法,不会获取从父类中继承而来的方法。

通过反射获取类的构造方法

Class 也提供了两种方式获取类的构造方法,第 ① 种是通过 Class 对象的 getConstructors 方法获取类的构造方法(只能获取当前类的 public 构造方法)。

Class<Person> personClass = Person.class;
Constructor[] constructors = personClass.getConstructors();
System.out.println(Arrays.toString(constructors));

运行结果

[public cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)]

第 ② 种是通过 Class 对象的 getDeclaredConstructors 方法获取类的构造方法(只包含类中定义的所有构造方法)。

Class<Person> personClass = Person.class;
Constructor[] constructors = personClass.getDeclaredConstructors();
System.out.println(Arrays.toString(constructors));

运行结果

[public cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String), 
protected cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String), 
private cn.mghio.blogmghiocode.reflect.Person()]
通过反射获取类的类名

Class 类提供了两种方式获取类的类名,第 ① 种是通过 getName 方法获取类的全限定名(包含包名)。

Class<Person> personClass = Person.class;
String fullPersonClassName = personClass.getName();
System.out.println(fullPersonClassName);

运行结果

cn.mghio.blogmghiocode.reflect.Person

第 ② 种是通过 Class 对象的 getSimpleName 方法获取类的类名(不包含包名)。

 Class<Person> personClass = Person.class;
String fullPersonClassName = personClass.getSimpleName();
System.out.println(fullPersonClassName);

运行结果

Person
通过反射获取类的修饰符

可以通过 Class 类来获取一个类的修饰符,也就是我们熟知的 publicprotectedprivate 等关键字,通过调用 getModifiers 方法来获取一个类的修饰符。

Class<Person> personClass = Person.class;
int modifyInt = personClass.getModifiers();
System.out.println(modifyInt);

运行结果

1

返回 1 表示类 Person 的修饰符为 public,修饰符在 Modifier 类中都被包装成一个 int 类型的数字,部分修饰符定义如下

/**
  * The {@code int} value representing the {@code public}
  * modifier.
  */
public static final int PUBLIC           = 0x00000001;

/**
  * The {@code int} value representing the {@code private}
  * modifier.
  */
public static final int PRIVATE          = 0x00000002;

/**
  * The {@code int} value representing the {@code protected}
  * modifier.
  */
public static final int PROTECTED        = 0x00000004;
通过反射获取类的包信息

Class 对象通过 getPackage 方法获取类的包相关信息,可以使用 Class 对象通过如下的方式获取包信息

Class<Person> personClass = Person.class;
Package packageClazz = personClass.getPackage();
System.out.println(packageClazz.getName());

运行结果

cn.mghio.blogmghiocode.reflect
通过反射获取类的父类

可以通过 Class 类来获取一个类的父类,通过调用 getModifiers 方法来获取一个类的父类。

Class<Person> personClass = Person.class;
Class superclass = personClass.getSuperclass();
System.out.println(superclass.getName());

运行结果

java.lang.Object

以上运行结果表示 Person 类的父类是 Object 类,可以看到 superclass 对象其实就是一个 Class 类的实例,所以也可以继续在这个对象上进行反射操作。

通过反射获取类的实现接口

可以通过 Class 类来获取一个类的父类,通过调用 getInterfaces 方法来获取一个类实现的接口。

Class<Person> personClass = Person.class;
Class<?>[] interfaces = personClass.getInterfaces();
System.out.println(Arrays.toString(interfaces));

运行结果

[interface cn.mghio.blogmghiocode.reflect.IPerson]

Java 中一个类可以实现多个接口,因此 getInterfaces 方法返回一个 Class 数组,在 Java 中接口也同样有对应的 Class 对象。这个方法需要注意的是,getInterfaces 方法仅仅只返回当前类所实现的接口。当前类的父类如果实现了接口,这些接口是不会在返回的 Class 集合中的,尽管实际上当前类其实已经实现了父类接口。

通过反射获取泛型信息

当我们在声明一个类或者接口的时候可以指定它可以参数化,常用的 List 接口就是一个参数化接口的例子。比如想要检查 List 接口的参数化类型,我们是没有办法能知道它具体的参数化类型是什么。这个类型就可以是一个应用中所有的类型。但是,当你检查一个使用了被参数化的类型的变量或者方法,你可以获得这个被参数化类型的具体参数。
第 ① 种 泛型方法返回类型 当你获得了 Method 对象,那么就可以获取到这个方法的泛型返回类型信息。如果方法是在一个被参数化类型之中(例如: T foo()),那么将无法获得它的具体类型,但是如果方法返回的是一个泛型类(例如:List<String> foo()),那么就可以获得这个泛型类的具体参数化类型。下面这个例子中的类定义了一个返回类型是泛型的方法。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 通过反射获取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemo {

  protected List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81);

  public List<Integer> getStringList(){
    return this.stringList;
  }

}

我们可以获取上面这个类 ReflectGenericDemo 的方法 getStringList 的泛型返回类型。


/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 通过反射获取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemoTests {

  @Test
  public void testMethodReturnGenericType() throws NoSuchMethodException {
    Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class;
    Method method = reflectClass.getMethod("getStringList", (Class<?>) null);
    Type returnType = method.getGenericReturnType();
    if (returnType instanceof ParameterizedType) {
      ParameterizedType type = (ParameterizedType) returnType;
      Type[] typeArguments = type.getActualTypeArguments();
      for (Type typeArgument : typeArguments) {
        Class typeArgumentClass = (Class) typeArgument;
        System.out.println("typeArgumentClass = " + typeArgumentClass);
      }
    }
  }

}

运行结果

typeArgumentClass = class java.lang.Integer

typeArguments 数组只有一个值,这个数组中唯一的值是 IntegerClass 类的实例,同时 Class 类也实现了 Type 接口。

第 ② 种 泛型方法返回类型 泛型方法参数类型,我们也可以通过反射来获取方法参数的泛型类型。


/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 通过反射获取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemo {

  protected List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81);

  public void setStringList(List<Integer> stringList) {
    this.stringList = stringList;
  }
}

可以通过以下方式获取方法参数的泛型类型。


/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 通过反射获取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemoTests {

  @Test
  public void testMethodParameterGenericType() throws NoSuchMethodException {
    Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class;
    Method method = reflectClass.getMethod("setStringList", List.class);
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    for (Type genericParameterType : genericParameterTypes) {
      if (genericParameterType instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
        Type[] parameterArgTypes = parameterizedType.getActualTypeArguments();
        for (Type parameterArgType : parameterArgTypes) {
          Class parameterArgClass = (Class) parameterArgType;
          System.out.println("parameterArgClass = " + parameterArgClass);
        }
      }
    }
  }

}

运行结果

parameterArgClass = class java.lang.Integer

第 ③ 种 泛型变量类型 可以通过反射来访问类中定义变量的泛型类型,不管这个变量是一个类的静态成员变量或是实例成员变量。

/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 通过反射获取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemo {

  private List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81);

}

我们可以通过以下代码来获取类 ReflectGenericDemo 的私有变量 stringList 的泛型变量类型。


/**
 * @author mghio
 * @date: 2019-12-29
 * @version: 1.0
 * @description: 通过反射获取泛型信息
 * @since JDK 1.8
 */
public class ReflectGenericDemoTests {

  @Test
  public void testFieldGenericType() throws NoSuchFieldException {
    Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class;
    Field field = reflectClass.getDeclaredField("stringList");
    Type type = field.getGenericType();
    if (type instanceof ParameterizedType) {
      ParameterizedType fieldGenericType = (ParameterizedType) type;
      Type[] fieldGenericTypes = fieldGenericType.getActualTypeArguments();
      for (Type genericType : fieldGenericTypes) {
        Class fieldGenericTypeClass = (Class) genericType;
        System.out.println(fieldGenericTypeClass);
      }
    }
  }

}

运行结果

class java.lang.Integer

数组 fieldGenericTypes 只有一个元素,它代表类 IntegerClass 类的实例。我们可以得出通过反射获取泛型信息的套路都是先获取 Class 类对象,然后通过该对象获取相应的类,如果是要获取变量的泛型信息就先获取到 Field 类,如果是要获取方法的泛型信息就先获取到 Method 类,最后再通过是否是 ParameterizedType 的实例来判断是否是泛型类型。

总结

我们介绍了 Java 泛型的基本使用,反射可能在我们日常的工作中不怎么接触到,但是,在很多框架中都有运用,比如,SpringIOC/DI 也是反射;还有 JDBCclassForName 也是反射。所有深入了解 Java 反射机制很有必要。

方法 描述
Constructor getConstructor(Class[] params) 根据构造方法的参数,返回一个 public 类型的构造方法
Constructor getConstructors() 返回所有 public 类型的构造方法数组
Constructor getDeclaredConstructor(Class[] params) 根据构造方法的参数,返回一个具体的构造方法(所有的类型)
Constructor getDeclaredConstructors() 返回该类中所有的构造方法数组(所有的类型)
Method getMethod(String name, Class[] params) 根据方法名和参数,返回一个 public 类型的方法
Method[] getMethods() 返回所有 public 类型的方法数组
Method getDeclaredMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的方法(所有的类型)
Method[] getDeclaredMethods() 返回该类中的所有的方法数组(所有的类型)
Field getField(String name) 根据变量名,返回一个 public 类型的成员变量
Field[] getFields() 返回 public 类型的成员变量的数组
Field getDeclaredField(String name) 根据变量名,返回一个成员变量(所有的类型)
Field[] getDelcaredField() 返回所有成员变量组成的数组(所有的类型)

相关文章

  • Java反射机制入门

    Java反射机制入门 一、什么是反射 JAVA反射机制(The JAVA reflection mechanism...

  • 反射之一

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • 反射之二

    总结内容源自一下文章粗浅看java反射机制反射机制应用实践谈谈java反射机制Java Reflection(反射...

  • Java基础之反射

    Java基础之—反射(非常重要)Java中反射机制详解Java进阶之reflection(反射机制)——反射概念与...

  • 反射之三

    总结内容源自以下文章 粗浅看java反射机制 反射机制应用实践 谈谈java反射机制 Java Reflectio...

  • java反射机制

    java的反射机制 1 JAVA的反射机制是什么?反射机制能做什么?反射机制的优点与缺点2 认识 Class...

  • Java中反射的用途

    Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对大家以后学习框...

  • 详解Java反射机制(Reflection)

    详解Java反射机制(Reflection) 反射机制的作用 JAVA反射机制是在运行状态中,对于任意一个类,都能...

  • Chapter 13 . 反射机制

    阅读原文 Chapter 13 . 反射机制 13.1 Java反射机制研究及应用 Java Reflection...

  • Android 使用注解和反射自制简单版的butternife

    一.Java反射机制。 1.反射机制的定义。 Java反射机制是指在运行状态中,对于任意一个类,都能知道这个类的所...

网友评论

    本文标题:Java 反射机制(一)

    本文链接:https://www.haomeiwen.com/subject/jdvuoctx.html