1 类加载
1.1 过程
-
加载
通过类加载器(ClassLoader)加载.class文件,读取到内存
在这个过程中,生成这个类所对应的字节码文件对象(java.lang.Class) -
链接
- 验证: 对字节码文件格式的验证(aced babe 咖啡宝贝 魔法数字)
- 准备: 给类的静态成员分配内存并赋予默认初始值
- 解析: 把符号引用(用一组符号来描述被引用的目标)转化为直接引用(真实的地址)
-
初始化
给静态成员赋真实的值, 并且执行静态代码块中的内容
1.2 类加载器
1.2.1 分类
-
Bootstrap ClassLoader 根类加载器
负责Java运行时核心类的加载,JDK中JRE的lib目录下rt.jar -
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录 -
Sysetm(App) ClassLoader 系统类加载器/应用加载器
负责加载自己定义的Java类
1.2.2 查看类加载器的方法
- 系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
- 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
- 根类加载器
//null 不是java写的
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
- 查看加载路径
System.out.println(System.getProperty("java.class.path") .replace(";", System.lineSeparator()));
System.out.println(System.getProperty("java.ext.dirs") .replace(";", System.lineSeparator()));
1.2.3 双亲委派模型
Java的双亲委派模型是一种类加载机制,用于确保Java类的安全性和一致性。这一模型是通过层次结构的类加载器来实现的,主要分为以下几个层次:
-
Bootstrap Class Loader(启动类加载器): 这是Java虚拟机的最顶层的加载器,它负责加载Java的核心库,通常是JVM自带的
rt.jar
等。由于它是用C++语言实现的,无法在Java中直接获得引用。 -
Extension Class Loader(扩展类加载器): 扩展类加载器是用来加载Java的扩展库,通常位于
jre/lib/ext
目录下。它是sun.misc.Launcher$ExtClassLoader
类的实例。 -
Application Class Loader(应用程序类加载器): 也叫系统类加载器,是用来加载应用程序的类路径上的类,即
classpath
。它是sun.misc.Launcher$AppClassLoader
类的实例。
这三个加载器构成了双亲委派模型的层次结构。其核心思想是,当一个类加载请求到达某个加载器时,该加载器首先检查是否能够加载这个类。如果能够加载,就加载完成;如果不能加载,加载器就会把请求委托给它的父加载器。这个过程一直递归进行,直到达到最顶层的启动类加载器。
双亲委派模型的工作过程:
- 当应用程序试图加载一个类时,应用类加载器会首先检查它已加载的类是否包含这个类。如果包含,则直接返回这个类的Class对象。
- 如果应用类加载器不能加载这个类,它就会委托给扩展类加载器,让扩展类加载器尝试加载。
- 扩展类加载器也是同样的过程,首先检查自己已加载的类,如果找到了就返回。如果没有找到,就委托给启动类加载器。
- 启动类加载器尝试加载,如果它找到了这个类,加载完成。如果仍然没有找到,就会抛出
ClassNotFoundException
。
这种层次结构的加载机制保证了类的一致性,即使是在不同的应用程序中,也可以确保同名的类是相同的。这同时提高了Java平台的安全性,因为核心库是由启动类加载器加载的,避免了恶意类库的注入。
总体来说,双亲委派模型是Java安全性和一致性的基石,确保了类的正确加载和防止类冲突。
![image-20221024094028570]](https://img-blog.csdnimg.cn/5de3d826ad3a49fa91190a7fd6c83dd1.png#pic_center)
1.3 类加载时机
创建类的实例(首次创建该类对象)
访问类的静态变量(首次)
调用类的静态方法(首次)
加载某个类的子类,会先触发父类的加载
直接使用java.exe命令来运行某个主类,也就是执行了某个类的main()方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
2 Java代码的3个阶段
3 反射
3.1 定义
获取运行时类信息的一种手段
反射的起点是字节码文件对象
3.2 获取字节码文件对象的几种方式
- 对象.getClass()
- 类名.class
- Class.forName(String className) 全限定名
- ClassLoader里的loadClass(String className)
注意:
无论通过什么方式获取的字节码文件对象 都是同一个
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
// - 对象.getClass()
Student student = new Student();
Class c1 = student.getClass();
//- 类名.class
Class c2 = Student.class;
System.out.println(c1 == c2);
//- Class.forName(String className) 全限定名
Class c3 = Class.forName("day22_reflect.com.cskayan._02cls.Student");
System.out.println(c1 == c3);
//- ClassLoader里的loadClass(String className)
Class c4 = ClassLoader.getSystemClassLoader().loadClass("day22_reflect.com.cskayan._02cls.Student");
System.out.println(c1 == c4);
}
}
class Student{
}
注意:
/**
* @package: _01code._03reflect._02class
* @description:
* @author: Yunyang
* @date: 2023/11/7 16:17
* @version:1.0
**/
/*
类加载时机:
1.首次new对象
2.执行main
3.访问类中的静态成员(静态成员变量 静态成员方法)
4.加载子类先进行父类加载
5.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
*/
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException {
// Class.forName() 完整的类加载
// 执行静态代码块
Class<?> c1 = Class.forName("_01code._03reflect._02class.AA");
// 类名.class 不完整的类加载
// 没有执行静态代码块
Class c2 = A.class;
}
}
class AA{
static {
System.out.println("AA static block");
}
}
3.3 关于Class
Class
类的实例表示正在运行的 Java 应用程序中的类和接口。
Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass
方法自动构造的。
3.4 配置文件
- 配置文件的几种格式:.properties .xml .yml
- 配置文件的作用: 放配置信息的 (数据库的, 第三方服务的配置信息)
- .properties文件的格式
- 键值对(key-value) key=value
- key是不能重复的
- 注释是#
- 文件里面全是String
- IDEA创建:src文件夹右击——>New——>Resoure Bundle
获取配置文件信息
Properties类
-
Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
-
构造方法
- Properties() 创建一个无默认值的空属性列表
-
成员方法
- load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
- load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
- getProperty(String key) 用指定的键在此属性列表中搜索属性。
-
步骤
- 创建Properties对象:
Properties properties = new Properties();
- Load:
load(InputStream in)/load(Reader in) properties.load(in);
- 获取属性值:
getProperty(String key) String port = properties.getProperty("port");
- 创建Properties对象:
/*
获取配置文件中的信息
*/
public class Demo {
public static void main(String[] args) throws IOException {
// 创建properties对象
Properties properties = new Properties();
// load(InputStream inStream)
// 输入流中读取属性列表(键和元素对)。
properties.load(new FileInputStream("_01code/config.properties"));
// String getProperty(String key)
// 用指定的键在此属性列表中搜索属性。
String port = properties.getProperty("port");
String host = properties.getProperty("host");
String user = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println("port = " + port);
System.out.println("user = " + user);
System.out.println("password = " + password);
System.out.println("host = " + host);
}
}
3.5 通过反射获取构造方法(Constructor)
通过反射获取所有构造方法
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
获取指定构造方法
Constructor getConstructor(Class<?>... parameterTypes) Constructor getDeclaredConstructor(Class<?>… parameterTypes)
使用Constructor创建对象
Person p = new Person(“zs”,20,true)
newInstance(参数列表)
暴力破解
setAccessible(true)
/**
* @package: _01code._03reflect._04api._01constructor
* @description:通过反射获取构造方法(Constructor)
* @author: Yunyang
* @date: 2023/11/7 17:19
* @version:1.0
**/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//反射的起点是字节码文件对象
// 获取字节码文件对象
Class<?> c = Class.forName("_01code._03reflect._04api.bean.Person");
System.out.println("获取所有public的构造方法--------");
Constructor<?>[] constructors = c.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("获取所有的构造方法----------");
Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("获取指定的public的构造方法---------");
Constructor<?> constructor = c.getConstructor(String.class, int.class, boolean.class);
// Constructor<?> constructor = c.getConstructor(String.class);
// java.lang.NoSuchMethodException
System.out.println(constructor);
System.out.println("获取指定的构造方法-----------");
Constructor<?> declaredConstructor = c.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor);
System.out.println("使用构造方法创建对象----------");
// newinstance
Object o = constructor.newInstance("zs", 20, true);
System.out.println(o);
// java.lang.IllegalAccessException
declaredConstructor.setAccessible(true);
Object o1 = declaredConstructor.newInstance("ls", 21);
System.out.println(o1);
}
}
3.6 通过反射获取成员变量(Field)
通过反射获取所有成员变量
Field[] getFields()
Field[] getDeclaredFields()
获取指定成员变量
Field getField(String name)
Field getDeclaredField(String name)
通过Field读写对象的成员变量(可暴力破解)
Object get(Object obj):获取值,传入对象
void set(Object obj, Object value):赋值,传入对象
/**
* @package: _01code._03reflect._04api._01constructor
* @description:通过反射获取成员变量(Field)
* @author: Yunyang
* @date: 2023/11/7 17:56
* @version:1.0
**/
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取字节码文件对象
Class<?> c2 = Class.forName("_01code._03reflect._04api.bean.Person");
System.out.println("获取所有public的成员变量----------------");
// Field[] getFields()
Field[] fields = c2.getFields();
for (Field field : fields) {
System.out.println(field);
}
//Field[] getDeclaredFields()
System.out.println("获取所有的成员变量--------------------");
Field[] declaredFields = c2.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
// Field getField(String name)
System.out.println("获取指定的public成员变量-------------------");
Field nameField = c2.getField("name");
System.out.println(nameField);
//Field getDeclaredField(String name)
System.out.println("获取指定的成员变量---------------");
Field ageFeild = c2.getDeclaredField("age");
System.out.println(ageFeild);
System.out.println("成员变量赋值--------------");
//给成员变量赋值 获取成员变量的值
//void set(Object obj, Object value):赋值,传入对象
Constructor<?> declaredConstructor =
c2.getDeclaredConstructor();
// 实例化对象
Object o = declaredConstructor.newInstance();
nameField.set(o,"zs");
System.out.println(o);
// java.lang.IllegalAccessException
ageFeild.setAccessible(true);
ageFeild.set(o,22);
System.out.println(o);
System.out.println("获取成员变量的值------------");
// Object get(Object obj):获取值,传入对象
Object o1 = nameField.get(o);
System.out.println(o1);
}
}
3.7 通过反射获取成员方法(Method)
获取所有成员方法
Method[] getMethods()// 父类的也能获取到
Method[] getDeclaredMethods()
获取指定的成员方法
Method getMethod(String name, Class<?>... parameterTypes) Method getDeclaredMethod(String name, Class<?>… parameterTypes)
利用Method调用对象的方法
Object invoke(Object obj, Object… args)
/**
* @package: _01code._03reflect._04api._03method
* @description:通过反射获取成员方法(Method)
* @author: Yunyang
* @date: 2023/11/7 20:05
* @version:1.0
**/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取字节码文件对象
Class<?> c3 = Class.forName("_01code._03reflect._04api.bean.Person");
System.out.println("获取所有public方法---------------");
// Method[] getMethods()
Method[] methods = c3.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("获取所有的方法-------------------");
//Method[] getDeclaredMethods()
Method[] declaredMethods = c3.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("获取指定的public方法--------------");
Method eatMethod1 = c3.getMethod("eat");
System.out.println(eatMethod1);
System.out.println("获取指定的方法-----------------");
//Method getDeclaredMethod(String name, Class<?>... parameterTypes)
Method eatMethod2 = c3.getDeclaredMethod("eat", String.class);
System.out.println(eatMethod2);
// Person p = new Person();
// p.eat();
System.out.println("反射调用方法-----------------------");
// 反射调用方法
// Object invoke(Object obj, Object... args)
Constructor<?> declaredConstructor = c3.getDeclaredConstructor();
Object o = declaredConstructor.newInstance();
Object invoke = eatMethod1.invoke(o);
System.out.println(invoke);
eatMethod2.setAccessible(true);
eatMethod2.invoke(o,"Apple");
}
}
4 补充
4.1 其它API
可以通过Class直接实例化 , 但是要有一个无参构造方法
/**
* @package: _01code._03reflect._04api._04add
* @description:可以通过Class直接实例化 , 但是要有一个无参构造方法
* @author: Yunyang
* @date: 2023/11/7 20:20
* @version:1.0
**/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> c4 = Class.forName("_01code._03reflect._04api.bean.Person");
// 通过class对象直接实例化对象
Object o = c4.newInstance();
System.out.println(o);
}
}
class A{
int a;
public A(int a) {
this.a = a;
}
public A() {
}
}
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
// 获取字节码文件对象
Class<?> c = Class.forName("_01code._03reflect._04api.bean.Person");
// 获取全限定类名
// _01code._03reflect._04api.bean.Person
System.out.println(c.getName());
// 获取简单名称
// Person
System.out.println(c.getSimpleName());
// 获取父类
Class<?> superclass = c.getSuperclass();
System.out.println("superclass = " + superclass);
// 获取实现的接口
Class<?>[] interfaces = c.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println(i);
}
// 获取类加载器
ClassLoader classLoader = c.getClassLoader();
System.out.println("classLoader = " + classLoader);
// 获取name这个成员变量对象
Field nameField = c.getDeclaredField("name");
// 获取权限修饰符
int modifiers = nameField.getModifiers();
System.out.println(modifiers);
// static String toString(int mod)
// 返回描述指定修饰符中的访问修饰符标志的字符串
// String s = Modifier.toString(modifiers);
// 获取eat(String s)方法对象
Method eatMethod = c.getDeclaredMethod("eat", String.class);
Class<?> returnType = eatMethod.getReturnType();
System.out.println("returnType = " + returnType);
Class<?>[] parameterTypes = eatMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType.getSimpleName());
}
}
}
4.2 自定义类加载器
- 继承ClassLoader
- 重写findClass方法
public class MyClassLoader extends ClassLoader {
private String path;
public MyClassLoader(String path) {
this.path = path;
}
//重写findClass方法
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 读取.class文件的内容
byte[] data = new byte[0];
try {
data = getData();
} catch (IOException e) {
e.printStackTrace();
}
// 想要得到class对象,利用defineClass方法
// protected Class<?> defineClass(String name, byte[] b, int off, int len)
//将一个 byte 数组转换为 Class 类的实例。
defineClass(name,data,0,data.length);
// 最终执行完,要返回Class对象
return super.findClass(name);
}
private byte[] getData() throws IOException {
//读取.class文件的内容
File file = new File(path);
long length = file.length();
byte[] bytes = new byte[(int) length];
//创建字节输入流
FileInputStream in = new FileInputStream(file);
in.read(bytes);
return bytes;
}
}
public class MyClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
String path = "D:\\wangdao_Java55\\55th-homework\\out\\production\\55th-homework\\_01code\\_03reflect\\_04api\\_04add\\Log.class";
MyClassLoader myClassLoader = new MyClassLoader(path);
Class<?> c = myClassLoader.loadClass("Log");
Method funcMethod = c.getDeclaredMethod("func");
Object o = c.newInstance();
funcMethod.invoke(o);
}
}
5 反射应用场景
通过反射获取注解信息
动态代理
ORM(Object Relational Mapping)框架, 数据库框架