1.1类加载【理解】
- 类加载的描述
- 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
- 类的加载
- 就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
- 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
- 类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
- 类的初始化
- 在该阶段,主要就是对类变量进行初始化
- 类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
- 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
- 类的初始化时机
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
1.2
类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类就称为运行时类,此运行时类,就作为Class的一个实例。
换句话说,Class的实例就对应着一个运行时类。
加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
哪些类型可以有class对象?
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive Type:基本数据类型
void
package reflect;
2.1反射的概述【理解】
-
是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
2.2获取Class类对象的三种方式【应用】
2.2.1三种方式分类
-
类名.class属性
-
对象名.getClass()方法
-
Class.forName(全类名)方法
public class test01 {
//前三种方式掌握,最后一种了解,用的不多,方式三用的比较多,更能够体现动态性,编译时先不去确定
@Test
public void test1() throws ClassNotFoundException {
//方式一:调用运行时类的属性.class
Class clazz = Person.class;//Class带泛型,因为是Person给它赋的值,所以具体操作的是Person
System.out.println(clazz);//class test.Person,不加泛型不会影响输出的效果,加上泛型可以避免后面的操作进行强转
//方式二:通过运行时类的对象,调用getClass方法
Person p1=new Person();
Class clazz2 = p1.getClass();//得到这个对象是哪个类造的,此方法在Object类中进行声明,得到它所属的类本身,再赋给Class
System.out.println(clazz2);//class test.Person
//方式三:调用Class的静态方法:forName(String classPath)
//不要写成Class.forName("Person");因为同一个module不同的包下可能都有Person,要说清楚是哪个包下的Person
Class clazz3 = Class.forName("test.Person");//包含包名在内的类的全名,test是包名
System.out.println(clazz3);//class test.Person
// clazz3=Class.forName("java.lang.String");//自己定义的类和API定义的类都可以作为Class的实例
// System.out.println(clazz3);//class java.lang.String
System.out.println(clazz==clazz2);//true
System.out.println(clazz==clazz3);//true
//虽然方式不同,但获取的是内存中的同一个运行时类
//方式四:使用类的加载器:ClassLoader
//因为这个测试方法是写在test01这个类中的
ClassLoader classLoader = test01.class.getClassLoader();
//得到之后,显式地load class,即显式的去加载某一个类
Class clazz4 = classLoader.loadClass("test.Person");
System.out.println(clazz4);//class test.Person
System.out.println(clazz==clazz4);//true
}
//注意:如果Person类是写在当前module的src下面,直接"Person"即可,不要写成"src.Person"或者"src\\Person"
}