在Java常用的框架中,反射被频繁的使用,本文介绍反射的简单使用。

Java反射(Reflection)(一)

1. 什么是反射

  • 通过Class实例获取class(包含类和接口)信息的方法称为反射(Reflection)。

2. Class类

  • 除了基本数据类型:int double long等之外,其他的类型都是class,包括了interface
  • class 是由JVM动态加载的
  • 每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。
1
2
3
public final class Class{
private Class(){}
}
  • String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后为String类创建一个Class实例与之关联起来
1
Class c1 = new Class(String);
  • Class实例是JVM内部创建的,Class类的构造方法是private,只能由JVM能创建Class实例

    • JVM拥有的每一个Class实例都关联了一个数据类型(class 或者 interface)
    • Class实例包含了class的所有信息:name,package,super,interface,field,method等
    • 因此我们可以通过Class实例来获取对应class的所有信息
  • 如何获取到Class实例(三种方法)
    1. 通过类的静态变量

      1
      Class strClass = String.class;
    2. 通过类实例getClass()方法

      1
      2
      Stirng s = "Hello";
      Class strClass = s.getClass();
    3. 通过Class.forName("全限定类名")获取

      1
      Class strClass = Class.forName("java.lang.String");
    4. 注意
      • 因为Class实例在JVM中是唯一的,所以可以用==比较两个Class

3. 反射的目的

1. 获取Class实例的全部信息

  • 运行实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package top.tobing.reflection;
    /**
    * 通过反射获取类的全部信息
    * @author Tobing
    */
    public class Demo01Reflection {
    public static void main(String[] args) {
    printClassINFO(" ".getClass());
    printClassINFO(Runnable.class);
    printClassINFO(java.time.Year.class);
    printClassINFO(String[].class);
    printClassINFO(int.class);
    }
    /**
    * 打印传入Class的信息
    * @param c 传入的Class
    */
    public static void printClassINFO(Class c) {
    System.out.println("------------------------------------------");
    System.out.println("简单类名:"+c.getSimpleName());
    System.out.println("全限定类名:"+c.getName());
    if(c.getPackage()!=null) {
    System.out.println("包名:"+c.getPackageName());
    }
    System.out.println("接口?:"+c.isInterface());
    System.out.println("枚举?:"+c.isEnum());
    System.out.println("数组?:"+c.isArray());
    System.out.println("基本数据类型?:"+c.isPrimitive());
    }
    }
  • 运行结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    ------------------------------------------
    简单类名:String
    全限定类名:java.lang.String
    包名:java.lang
    接口?:false
    枚举?:false
    数组?:false
    基本数据类型?:false
    ------------------------------------------
    简单类名:Runnable
    全限定类名:java.lang.Runnable
    包名:java.lang
    接口?:true
    枚举?:false
    数组?:false
    基本数据类型?:false
    ------------------------------------------
    简单类名:Year
    全限定类名:java.time.Year
    包名:java.time
    接口?:false
    枚举?:false
    数组?:false
    基本数据类型?:false
    ------------------------------------------
    简单类名:String[]
    全限定类名:[Ljava.lang.String;
    接口?:false
    枚举?:false
    数组?:true
    基本数据类型?:false
    ------------------------------------------
    简单类名:int
    全限定类名:int
    接口?:false
    枚举?:false
    数组?:false
    基本数据类型?:true

2. 通过Class实例来创建对应类型实例

  • 代码如下

    1
    2
    Class c = String.class;					//获取String Class的实例
    String s = (String)c.newInstance(); //通过实例创建String实例
  • 注意

    • Class.newInstance()方式创建类实例时,只能调用该类的public无参数构造方法

4. 反射的一些细节

动态加载
  • JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载。
  • 动态加载class的特性对于Java程序非常重要。利用此特性,我们才能在运行期根据条件加载不同的实现类。

例如,Commons Logging总是优先使用Log4j,只有当Log4j不存在时,才使用JDK的logging。利用JVM动态加载特性,大致的实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Commons Logging优先使用Log4j:
LogFactory factory = null;
if (isClassPresent("org.apache.logging.log4j.Logger")) {
factory = createLog4j();
} else {
factory = createJdkLog();
}

boolean isClassPresent(String name) {
try {
Class.forName(name);
return true;
} catch (Exception e) {
return false;
}
}

这就是为什么我们只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。

总结

  • JVM为每个加载的类(或者接口)创建了与之关联的Class实例
  • 通过Class实例可以获取class全部信息,此过程称为反射(Reflection)。
  • JVM是动态加载class的

注意:本文章参考于廖雪峰,如要学习,请访问廖雪峰网站

评论