文章目录
- 1.类加载器
- 1.1类加载器【理解】
- 1.2类加载的过程【理解】
- 1.3类加载的分类【理解】
- 1.4双亲委派模型【理解】
- 1.5ClassLoader 中的两个方法【应用】
- 2.反射
- 2.1反射的概述【理解】
- 2.2获取Class类对象的三种方式【应用】 第1步:获取类的Class对象
- 2.3反射获取构造方法并使用【应用】第2.1步:利用Class类获取对象方法
- 2.3.1Class类获取构造方法对象的方法
- 2.3.2Constructor类用于创建对象的方法 (构造方法获取到了,接下来第3步:利用构造方法创建对象了)
- 2.3.3小结
- 2.4反射获取成员变量并使用【应用】
- 2.4.1Class类获取成员变量对象的方法
- 小结:反射根据配置文件动态创建对象并执行方法
1.类加载器
1.1类加载器【理解】
-
作用
负责将.class文件(存储的物理文件)加载在到内存中
1.2类加载的过程【理解】
-
类加载时机
- 创建类的实例(对象)
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
-
类加载过程
-
加载
- 通过包名 + 类名,获取这个类,准备用流进行传输
- 将这个类加载到内存中
- 加载完毕创建一个class对象 类在内存中不能随便乱放,得为每个类创建一个Class对象来存放这个类的信息
-
-
链接
-
验证
确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全
(文件中的信息是否符合虚拟机规范有没有安全隐患)
-
准备
负责为类的类变量(被static修饰的变量)分配内存,并设置默认初始化值
(初始化静态变量) 静态变量有默认的初始化值 开辟空间 赋值为null 注意只为static修饰的静态变量赋初值
-
解析
将类的二进制数据流中的符号引用替换为直接引用
(本类中如果用到了其他类,此时就需要找到对应的类)
-
“将类的二进制数据流中的符号引用替换为直接引用”:
加载好了类Student,但是其成员name是引用类型,对应的String类到底有没有被加载呀,加载了在哪呀? 所以String先是符号引用
----内存中找到String类------ 就变成了 直接引用(String类型)
-
初始化
根据程序员通过程序制定的主观计划去初始化类变量和其他资源
(静态变量赋值以及初始化其他资源)
-
小结
- 当一个类被使用的时候,才会加载到内存
- 类加载的过程: 加载、验证、准备、解析、初始化
1.3类加载的分类【理解】
-
分类
- Bootstrap class loader:虚拟机的内置类加载器,通常表示为null ,并且没有父null
- Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块
- System class loader:系统类加载器(应用程序类加载器),负责加载用户类路径上所指定的类库
-
类加载器的继承关系
- System的父加载器为Platform
- Platform的父加载器为Bootstrap
1.4双亲委派模型【理解】
-
介绍
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
是逻辑上的父子关系,不是extends的继承父子关系。 就是把类加载任务委托给‘父亲’去加载
某个父亲加载不了就返回,直到最后没办法,自己才去加载
1.5ClassLoader 中的两个方法【应用】
-
方法介绍
方法名 说明 public static ClassLoader getSystemClassLoader() 获取系统类加载器 public InputStream getResourceAsStream(String name) 加载某一个资源文件 public class ClassLoaderDemo1 { public static void main(String[] args) { //获得系统类加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); //获得系统类加载器的父加载器 --- 平台类加载器 ClassLoader platformClassLoader = systemClassLoader.getParent(); //获得平台类加载器的父加载器 --- 启动类加载器 ClassLoader bootstrapClassLoader = platformClassLoader.getParent(); System.out.println("系统类加载器"+systemClassLoader); System.out.println("平台类加载器"+platformClassLoader); System.out.println("启动类加载器"+bootstrapClassLoader); } }
-
LX 读取properties文件
- prop.properties
name=zhangsan age=13
- ClassLoaderDemo2.java
public class ClassLoaderDemo2 { public static void main(String[] args) throws IOException { //static ClassLoader getSystemClassLoader() 获取系统类加载器 //InputStream getResourceAsStream(String name) 加载某一个资源文件 //获取系统类加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); //利用加载器去加载一个指定的文件 //参数:文件的路径(放在src的根目录下,默认去那里加载) //返回值:字节流。 InputStream is = systemClassLoader.getResourceAsStream("prop.properties"); Properties prop = new Properties(); prop.load(is); System.out.println(prop); is.close(); } }
- ClassLoaderDemo2.java 整理后很简单
public class ClassLoaderDemo2_1 { public static void main(String[] args) throws IOException { // 获得系统类加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); // 利用加载器加载一个指定文件 返回字节流 InputStream is = systemClassLoader.getResourceAsStream("prop.properties"); Properties prop = new Properties(); prop.load(is); System.out.println("prop = " + prop); is.close(); } }
2.反射
2.1反射的概述【理解】
-
反射机制
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意属性和方法;
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
2.2获取Class类对象的三种方式【应用】 第1步:获取类的Class对象
-
三种方式分类
-
类名.class属性
-
对象名.getClass()方法
-
Class.forName(全类名)方法
-
-
示例代码
Student.java
package cn.whu.myreflect1;
public class Student {
private String name;
private int age;
//空参构造,全参构造,toString,所有get/set
public void study(){
System.out.println("学生在学习");
}
}
ReflectDemo1.java
/**
* 各种获取类的Class对象的方法
*/
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//方法1.Class类中的静态方法forName("全类名") //所谓全类名: 包名.类名
Class clazz = Class.forName("cn.whu.myreflect1.Student");
System.out.println(clazz);//class cn.whu.myreflect1.Student
//方法2.通过class属性来获取
Class clazz2 = Student.class;
System.out.println(clazz2);//class cn.whu.myreflect1.Student
//方法3.利用对象的getClass方法来获取class对象
//getClass方法是定义在Object类中的,所以任何对象都有
Student stu = new Student();
Class clazz3 = stu.getClass();
System.out.println(clazz3);//class cn.whu.myreflect1.Student
System.out.println(clazz == clazz2);//true
System.out.println(clazz2 == clazz3);//true
}
}
无视修饰符: 无视private还是public
有了Class对象可以干嘛,可以获取类的所有信息,主要也就三类:构造器、普通方法、成员变量
Class对象组成如下:
2.3反射获取构造方法并使用【应用】第2.1步:利用Class类获取对象方法
2.3.1Class类获取构造方法对象的方法
-
方法介绍
方法名 说明 Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组 Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组 Constructor getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象 Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造方法对象
- Student.java
package cn.whu.myreflect2;
public class Student {
private String name;
private int age;
//私有的有参构造
private Student(String name){
System.out.println("name值为:"+name);
System.out.println("private Student 有参构造方法");
}
//公共无参构造
public Student() {
System.out.println("public Student 无参构造方法");
}
//公共有参构造
public Student(String name,int age){
System.out.println("name值为:"+name+" age值为:"+age);
System.out.println("public Student 有参构造方法");
}
}
- Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
public static void main(String[] args) throws ClassNotFoundException {
//1.不管那种获取方法 第一步都得先获取Class对象啊
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2. Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
}
- Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
public static void main(String[] args) throws ClassNotFoundException {
//1.不管那种获取方法 第一步都得先获取Class对象啊
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2. Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
Constructor<?>[] declaredAnnotations = clazz.getDeclaredConstructors();
for (Constructor declaredAnnotation : declaredAnnotations) {
System.out.println(declaredAnnotation);
}
- Constructor getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象
参数就写构造方法的形式参数类型对应的Class类
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.不管那种获取方法 第一步都得先获取Class对象啊
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2. Constructor getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象
Constructor<?> constructor1 = clazz.getConstructor();
System.out.println(constructor1);//public cn.whu.myreflect2.Student() 公共空参构造
Constructor<?> constructor2 = clazz.getConstructor(String.class, int.class);//参数类型就写构造方法的形参类型和顺序
System.out.println(constructor2);//public cn.whu.myreflect2.Student(java.lang.String,int) //string,int参构造方法
//Constructor<?> constructor3 = clazz.getConstructor(int.class);//报错 并没有一个参数为(int)的构造方法
}
- Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造方法对象
私有的也可以获取到
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.不管那种获取方法 第一步都得先获取Class对象啊
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2. Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造方法对象
// 私有的也可以获取到
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
}
2.3.2Constructor类用于创建对象的方法 (构造方法获取到了,接下来第3步:利用构造方法创建对象了)
-
方法介绍
方法名 说明 T newInstance(Object…initargs) 根据指定的构造方法创建对象 setAccessible(boolean flag) 设置为true,表示取消访问检查
- T newInstance(Object…initargs) 根据指定的构造方法创建对象
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1. 第一步永远都是先获取Class对象
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2.获取构造方法对象
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
//3.利用newInstance 创建Student对象
Student student = (Student) constructor.newInstance("张三",23); // 当然创建对象时的实参值得这时候传递了
// 此时,构造方法会自动执行
System.out.println("student = " + student);
}
类似的调用空参构造
public static void main(String[] args) throws Exception {
//1. 第一步永远都是先获取Class对象
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2.获取构造方法对象
Constructor<?> constructor = clazz.getConstructor();
//3.利用newInstance 创建Student对象
Student student = (Student) constructor.newInstance(); // 当然创建对象时的实参值得这时候传递了
// 此时,构造方法会自动执行
System.out.println("student = " + student);
}
空参构造还有个简写方式(过时方法 了解即可)
public static void main(String[] args) throws Exception {
// 反射调用空参创建对象简写格式
//1. 第一步永远都是先获取Class对象
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2. 在Class类中,有一个newInstance方法,可以利用空参直接创建一个对象 (直接跳过了获得构造对象那一步)
Student student = (Student) clazz.newInstance(); // 该方法已经过时 了解即可
System.out.println(student);
}
- setAccessible(boolean flag) 设置为true,表示取消访问检查(可以临时使用私有成员了)
★获取一个私有的构造方法,创建对象★
多一步,临时取消访问检查
public static void main(String[] args) throws Exception {
//1. 第一步永远都是先获取Class对象
Class<?> clazz = Class.forName("cn.whu.myreflect2.Student");
//2.获取构造私有方法对象
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
//直接创建对象会报错 毕竟私有,直接调用私有构造方法肯定不妥
//也即:被private修饰的成员,不能直接使用
//如果用反射强行获取并使用,需要临时取消访问检查
constructor.setAccessible(true);//临时取消(私有)访问检查
//3.利用newInstance 创建Student对象
Student student = (Student) constructor.newInstance("张三"); // 当然创建对象时的实参值得这时候传递了
// 此时,构造方法会自动执行
System.out.println("student = " + student);
}
反射可以强行获取私有方法,并强行用其获取对象
2.3.3小结
-
获取class对象
三种方式: Class.forName(“全类名”), 类名.class, 对象名.getClass()
-
获取里面的构造方法对象
getConstructor (Class<?>... parameterTypes) getDeclaredConstructor (Class<?>… parameterTypes)
-
如果是public的,直接创建对象
newInstance(Object… initargs)
-
如果是非public的,需要临时取消检查,然后再创建对象
setAccessible(boolean) 暴力反射
2.4反射获取成员变量并使用【应用】
2.4.1Class类获取成员变量对象的方法
-
方法分类
方法名 说明 Field[] getFields() 返回所有公共成员变量对象的数组 Field[] getDeclaredFields() 返回所有成员变量对象的数组 Field getField(String name) 返回单个公共成员变量对象 Field getDeclaredField(String name) 返回单个成员变量对象
公共成员变量:仅仅public修饰的成员变量
所有成员变量:public、private、protect… 修饰的成员变量
小结:反射根据配置文件动态创建对象并执行方法
小结:
1、获取Class对象
Class clazz = Class.forName("cn.whu.myreflect1.Student");
Class clazz2 = Student.class;
Class clazz3 = new Student().getClass();
2、通过Class对象获取三类成员
Constructor constructor = clazz.getConstructor(); // 获取构造器
Method method = clazz.getMethod("name"); //获取方法
Field field clazz.getField(String name) # 获取变量名
3、通过获取的构造器创建对象
Object o = constructor.newInstance();
4、通过对象和方法,执行对应方法
method.invoke(o);//学生在学习
反射可以根据配置文件,动态创建对象,并调用对象的方法
ReflectDemo2.java
package cn.whu.myreflect1;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 根据配置文件创建对象,并执行对象方法
*/
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, IOException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
//获取系统类加载器,加载prop.properties文件
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("prop.properties");
//得先创建一个Properties集合
Properties prop = new Properties();
//将文件中的数据读取到集合中
prop.load(is);
is.close();
//---- 下面利用【反射】创建对象并调用方法 ----
// 死步骤 能看懂 最好是记住
//获取字节码文件对象
Class clazz = Class.forName(prop.getProperty("className"));//配置文件里className配置好的全路径名
//(通过类Class对象)获取构造器对象
Constructor constructor = clazz.getConstructor();
//利用构造器创建一个对象
Object o = constructor.newInstance();
//获取方法对象
Method method = clazz.getMethod(prop.getProperty("methodName"));//配置文件里配置好的方法名
//运行方法
method.invoke(o);//学生在学习
}
}
prop.properties
className=cn.whu.myreflect1.Worker
methodName=work