Java反射_Class
反射
反射是Java中比较高阶的技巧,理解了反射对理解JVM、使用Spring框架都有好处
反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
Class类
先来看一段官方API
Instances of the class Class represent classes and interfaces in a running Java application.
(Class实例代表正在运行的类和接口)
An enum is a kind of class and an annotation is a kind of interface.
(枚举是类、注解是接口)
Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions.
(每个数组也属于一个反映为Class对象的类,该类对象由具有相同元素类型和维数的所有数组共享)
The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.
(Java的原始数据类型以及void关键字也表现为Class对象)
巴比巴卜、屋里哇啦说了些什么呢?
意思是全部的Java数据类型,只要一运行,都可以是Class的对象。
请注意,一个Class
对象实际上表示的是一个类型,而这个类型未必一定是一种类。
例如, int
不是类,但 int.class
是一个 Class 类型的对象。
在JVM第一次读取到一个class
时,会把它加载到内存,而且会为他创建一个Class
类型的实例(注意是Class还是class,不要搞错了!!)
创建
官方API:Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.
Class没有公有的构造方法,因为它的创建是当class加载时,通过类加载器的defineClass
方法,由JVM自动创建的。
上文中提到:请注意,一个
Class
对象实际上表示的是一个类型,而这个类型未必一定是一种类。
JVM持有的每个Class
实例都指向一个(class或interface)
:
1 | ┌───────────────────────────┐ |
每个Class
都详细的描述了这个class
的信息
1 | ┌───────────────────────────┐ |
JVM
为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
反射是什么呢?
反射就是通过Class
实例来获取class
信息
使用Class
虽然我们没有创建Class
的权利,但是我们可以调用它
[法一]:通过类名
1 | Class cls = String.class; |
[法二]:通过实例
1 | String str = "你好"; |
[法二]:通过调用forName()
方法,这个方法需要知道包路径(但是这个方法的参数只能填类或者接口,否则会抛出异常)
1 | Class.forName("java.lang.String"); |
JVM对于相同类型的类只会为它创建一个Class
,所以上述三个方法会调取到同一个Class
Class
实例比较和instanceof
区别
1 | Integer i = new Integer(1); |
instanceof
判定子类是父类的也是祖父类的实例
1 | System.out.println(i.getClass() == Integer.class);//true |
Class
实例只能使用==
比较,并且比较的两个必须相同类型(在参考资料中,最后这一个比较会返回false,而不是报错,而我实测编译报错,版本为JDK1.8)
获取信息
反射的目的就是调用Class
来获取class
的信息
Class中有大量的方法,我们写一个如下的函数
1 | static void printClassInfo(Class cls) { |
当我们输入参数为String.class
(一个类)
1 | Class name: java.lang.String |
当我们输入参数为Runnable.class
(一个接口)
1 | Class name: java.lang.Runnable |
当我们输入参数为java.time.Month.class
(一个枚举类)
1 | Class name: java.time.Month |
当我们输入参数为String[].class
(一个数组)
1 | Class name: [Ljava.lang.String; |
当我们输入参数为int[].class
(一个数组)
1 | Class name: [I |
当我们输入参数为int.class
(一个基本类型)
1 | Class name: int |
当我们输入参数为void.class
(一个基本类型)
1 | Class name: void |
上面的例子,基本上提到了所有可能出现的情况,类、接口、数组、枚举、基本类型(void)
Class都可以将他们的信息打印出来
(有意思的是:在打印void
的primitive
属性时,显示为true
,对这个感兴趣可以自己搜一下哈,在《Java编程思想》这本圣经级别的书中,它把void
也规定为了基本类型)
Class
也可以创建class
实例
1 | Class cls = String.class; |
以上这个方法相当于new String()
,通过newInstance()
方法可以创建实例,但是有一定的局限:只能调用public
的无参数构造方法,带参数的构造方法,或者非public
的构造方法都无法通过Class.newInstance()被调用
将 forName
与 newlnstance
配合起来使用, 可以根据存储在字符串中的类名创建一个对象
1 | String s = "java.util.Random"; |
动态加载
在JVM
执行Java
程序时,并不是一次性加载所有的类到内存,而是第一次要用到的时候才回去加载
使用这个特性我们可以在运行期根据条件来控制加载class
。
参考资料
廖雪峰官方网站
官方API
《Java核心技术卷一》