在Java常用的框架中,反射被频繁的使用,本文介绍反射的简单使用。
Java反射(Reflection)(一)
1. 什么是反射
- 通过
Class
实例获取class
(包含类和接口)信息的方法称为反射(Reflection)。
2. Class类
- 除了基本数据类型:
int double long
等之外,其他的类型都是class
,包括了interface
- class 是由JVM动态加载的
- 每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。
1 | public final class 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实例(三种方法)
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
30package 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
2Class 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的
注意:本文章参考于廖雪峰,如要学习,请访问廖雪峰网站