Day22反射

Java.
当我们运行java.exe命令执行某个Java程序时,由于Java程序本身以.class字节码的形式存在,它不是一个可执行文件,所以需要JVM将类文件加载到内存中。
类的加载由类加载器完成。JVM本身包含了一个类加载器,称为根类加载器(Bootstrap ClassLoader)。和JVM一样,根类加载器是用本地代码实现的,它负责加载核心Java类(即所有java.*开头的类)。
另外,JVM还会提供两个类加载器,它们都是用Java语言编写的,由根类加载器加载。其中,扩展类加载器(Extension ClassLoader)负责加载扩展的Java类,包括所有javax.*开头的类和存放在JRE的扩展目录下(JAVA_HOME/jre/lib/ext)中JAR的类包;系统类加载器(Application ClassLoader)负责加载应用程序自身的类。
此外,Java API中还提供了一个ClassLoader抽象类,开发者还可以通过继承ClassLoader基类来创建自定义的类加载器。
当我们运行Java.exe命令执行一个Java程序时,程序最基本的加载流程如下:
java.exe程序搜索jre目录,寻找JVM.dll,并启动JVM。
JVM运行根类加载器,该根类加载器加载Java核心API。
根类加载器运行后,它会自动加载扩展类加载器和系统类加载器,并将扩展类加载器的父类设置为根类加载器,将应用加载器的父类设置为扩展类加载器。
扩展类加载器加载搜索JAVA_HOME/jre/lib/ext目录,加载扩展API。
应用加载器搜索CLASSPATH目录,加载我们要运行的类。
类的class文件读入内存后,就会创建一个java.lang.Class对象。也就是说,当Java程序中使用任何类时,系统都会为之创建一个java.lang.Class对象。一旦某个类被载入JVM中,同一个类就不会再次被载入。
通过使用不同的类加载器,可以从不同来源加载类的二进制数据。通常有如下几种来源:
从本地文件系统加载class文件,这是我们前面绝大部分示例程序的类加载方式。
从JAR包中加载class文件,这种方式也很常见。例如,我们后面要学习的JDBC数据库编程要用到的数据库驱动类就是放在JAR文件中,JVM可以从JAR文件中直接加载该class文件。
通过网络加载class文件。
把一个Java源文件动态编译,并执行加载。
2)连接
当类被加载后,系统就为之创建一个对应的Class对象,接着就会进入连接阶段。连接阶段会负责把类的二进制数据合并到JRE中。类连接又可以分为如下三个阶段:
验证:检验被加载的类是否有正确的内部结构,并和其它类协调一致。
准备:负责为类的静态属性分配内存,并设置默认初始值。
解析:将类的二进制数据中的符号引用替换成直接引用。
3)初始化
随后,进行类初始化阶段。JVM负责对类进行初始化,也就是对静态属性进行初始化。在Java类中,对静态属性指定初始值的方式有两种:(1)声明静态属性时指定初始值;(2)使用静态初始化块为静态属性指定初始值。这两种方式我们在前面章节中都已经讲述。
JVM初始化一个类,一般包含如下几个步骤:
1)假如这个类还没有被加载和连接,程序先加载并连接该类。
2)假如该类的直接父类还没有被初始化,则先初始化其直接父类。
3)假如该类中有初始化语句,则系统依次执行这些初始化语句。
当执行第二步时,系统对直接父类的初始化步骤也遵循这三个步骤。如果该直接父类又有直接父类,系统再次重复这三个步骤,以此类推。所以,JVM最先初始化的总是java.lang.Object类。当程序主动使用任何一个类时,系统会保证该类以及它的所有父类都会被初始化。
当Java程序首次通过下面六种方式来使用某个类或者接口时,系统就会初始化该类或者接口:
创建类的实例。
调用某个类的静态方法。
访问某个类或接口的静态属性,或者为静态属性赋值。
使用反射方式强制创建某个类或接口对应的java.lang.Class对象。
初始化某个类的子类。
直接使用java.exe命令运行某个主类。
程序需要在运行时发现对象和类的真实信息。在Java中,为了解决此问题,提供了两种方法:
第一种是假设在编译和运行时都完全知道类型的具体信息,在这种情况下,我们可以直接使用instanceof运算符进行判断,再利用强制类型转换将其转换成运行时类型的变量即可。这种方式在《多态与抽象》一章中已经介绍。
第二种是编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。
所谓反射,是指在Java中,可以在运行期载入、探知和使用编译期完全未知的类。换句话说,Java程序可以装载一个运行期才得到名称的类,获取其完整结构,并创建对象、或者对类的成员变量设定值、或者调用其方法。这种“看透”类的能力,被称为反省、内省或自审。
在Java API中的java.lang包和java.lang.reflect包中,提供了Class类、Field类、Method类、Constructor类、Array类等用于实现反射机制。
下面我们分别详细讲述Java中如何使用反射机制,获取类的信息,并动态创建对象、设定成员变量的值、调用方法以及动态创建和访问数组。
通过Class对象获取某个类的属性、方法和构造器后,程序就可以通过Constructor对象调用相应的构造器创建对象,通过Field对象访问对象并修改对象的属性,通过Method对象来执行对象相应的方法。此外,Class类还有一个newInstance()方法用于创建此Class对象所表示的类的一个新实例。
使用Class对象的newInstance()方法来创建该Class对象对应类的实例。这种方式要求Class对象的对应类有默认构造器,执行newInstance()方法时,实际上是调用默认的构造器来创建实例。用这种方式创建对象比较常见,后面我们要学习到很多JavaEE开源框架都是采用这种方式创建对象。
先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值