1. Class 的使用
- 掌握:Class的理解
- 掌握:获取Class的实例(前三种)
/*
* 获取Class实例的几种方式(掌握前三种)
* */
@Test
public void test1() throws ClassNotFoundException {
//1.调用类的静态属性(.class)
Class clazz1 = User.class;
System.out.println(clazz1);
//2. 通过对象调用getClass()
User u1 = new User();
Class clazz2 = u1.getClass();
System.out.println(clazz2);
System.out.println(clazz1 == clazz2); //true
//3. 调用Class的静态方法forName(String className)
Class clazz3 = Class.forName("com.atguigu02._class.User");
System.out.println(clazz1 == clazz3);//true
//4. (了解)使用类的加载器,调用loadClass(String className)
Class clazz4 = ClassLoader.getSystemClassLoader().loadClass("com.atguigu02._class.User");
System.out.println(clazz1 == clazz4);
}
- 熟悉:Class可以指向哪些具体的结构?
- 类、接口、注解、枚举、基本数据类型、数组、void等
2. 类的加载与类的加载器
- 类的加载过程:
过程1:装载(Loading)
将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。
过程2:链接(Linking)
①验证Verify:确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。
②准备Prepare:正式为类变量(static)分配内存并`设置类变量默认初始值`的阶段,这些内存都将在方法区中进行分配。
③解析Resolve:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
过程3:初始化(Initialization)
- 执行`类构造器<clinit>()方法`的过程。`类构造器<clinit>()方法`是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个`类的<clinit>()方法`在多线程环境中被正确加锁和同步。
- 了解:类的加载器
1. 常见的类的加载器
> 引导类加载器(Bootstrap ClassLoader):负责系统核心api的加载。
> 扩展类加载器(ExtClassLoader):jdk目录下的jre/lib/ext目录下的api
> 应用程序类加载器(或系统类加载器 AppClassLoader):用户自定义的类的默认的类的加载器
2. 相互之间的关系:不是继承关系。
但是我们经常说:应用程序类加载器的父类加载器是扩展类加载器;扩展类加载器的父类加载器是引导类加载器。
为什么习惯这样称谓呢?涉及到类的加载机制:双亲委派机制。
class SystemClassLoader{
ExtClassLoader loader;
public SystemClassLoader(ExtClassLoader loader){
this.loader = loader;
}
}
3.(掌握)使用类的加载器获取流,并读取配置文件信息
/*
* (掌握)使用类的加载器获取流,并读取配置文件信息
* */
@Test
public void test3() throws Exception {
Properties pros = new Properties();
//方式1:默认的文件所在当前module下
// FileInputStream is = new FileInputStream(new File("jdbc.properties"));
FileInputStream is = new FileInputStream(new File("src/jdbc1.properties"));
//方式2:默认的文件所在当前module的src下
// ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// InputStream is = systemClassLoader.getResourceAsStream("jdbc1.properties");
pros.load(is);
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println(name + ":" + password);
}
3. 反射的应用1:创建运行时类的对象(掌握)
- jdk8及之前:Class的newInstance()
1.1 如何实现?
调用Class的方法:newInstance(),返回一个运行时类的实例。
1.2 要想创建对象成功,需要满足:
> 对应的运行时类中必须提供一个空参的构造器
> 对应的运行时类的空参的构造器的访问权限必须够
1.3 回忆:JavaBean中要求给当前类提供一个公共的空参的构造器。有什么用?
> 子类继承父类以后,子类的构造器中在没有显式声明this(形参列表)或super(形参列表)的情况下,默认调用父类空参的构造器
> 提供空参的构造器,便于通过反射的方式动态的创建指定类的对象。
1.4 在jdk9中标识为过时,替换成什么结构:
clazz.getDeclaredConstructor().newInstance()进行替换。
- jdk8之后:调用指定的构造器,设置构造器的访问权限,创建对象
4. 反射的应用2:获取运行时类的完整结构
- 了解:获取所有的属性、所有的方法、所有的构造器、声明的注解等。
- 熟悉:获取运行时类的父类、实现的接口们、所在的包、带泛型的父类、父类的泛型等
5. 反射的应用3:调用指定的属性、方法、构造器
(掌握)反射的应用3:调用指定的结构:指定的属性、方法、构造器
1.1 调用指定的属性:
步骤1:获取指定名称的属性:调用Class类中的getDeclaredField(String fieldName)
步骤2:确保此属性是可访问的:调用Field类中的setAccessible(true);
步骤3:获取此属性的值:调用Field类的get(Object obj)
设置此属性的值:调用Field类的set(Object obj,Object fieldValue)
1.2 调用指定的方法
步骤1:获取运行时类中指明的方法:调用Class类的getDeclaredMethod(String methodName,Class ... paramTypes)
步骤2:确保此方法是可访问的:调用Method类中的setAccessible(true);
步骤3:调用此方法:调用Method类的invoke()方法,此方法的返回值即为invoke方法调用者对应的方法的返回值。
1.3 调用指定的构造器
步骤1:获取指定的构造器:调用Class类的getDeclaredConstructor(Class ... paramTypes)
步骤2:确保此构造器是可访问的:调用Constructor类中的setAccessible(true);
步骤3:调用此构造器,创建运行时类的对象:调用Constructor类中的newInstance(Object ... values);
6. 反射的应用4:注解的使用(了解)
略。为了后期学习框架做准备