23.类加载器
类加载器
类加载
当程序需要使用某个类, 如果该类还未被加载到内存中, 则系统会通过类的加载, 类的连接, 类的初始化这三个步骤来对类进行初始化. 这三个步骤统称为类加载或者类初始化
类的加载:
- 将class文件读入内存, 并为之创建一个java.lang.class对象
- 任何类被使用时, 系统都会为之创建一个java.lang.Class对象
类的连接:
- 验证阶段: 用于检验被加载的类是否有正确的内部结构, 并和其他类协调一致
- 准备阶段: 负责为类的类变量分配内存, 并设置默认初始化值
- 解析阶段: 将类的二进制数据中的符号引用替换为直接引用
类的初始化:
- 主要是对类变量进行初始化
- 假如该类未被加载和连接, 程序会先加载并连接该类
- 假如该类的直接父类还未被初始化, 则先初始化其直接父类
- 假如类中有初始化语句, 则系统依次执行这些初始化语句
注意: 在执行第2个步骤的时候, 系统对直接父类的初始化步骤也遵循初始化步骤1-3
类初始化的时机:
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量, 或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个类
类加载器
负责将.class文件加载到内存中, 并为之生成对应的java.lang.class对象
我们不需要过分关心类加载机制, 但是了解机制能更好的理解程序的运行
JVM类加载机制
-
全盘负责: 当一个类加载器负责加载某个Class时, 该Class所依赖的和引用的其他Class也有改类加载器负载再入, 除非显示使用另外一个类加载器载入
-
父类委托: 当一个类加载器负责加载某个Class时, 先让父类加载器视图加载改Class, 只有在父类加载器无法加载该类时,才尝试从自己的类路径中加载该类.
-
缓存机制: 保证所有加载过的类都会被缓存, 当需要使用某个Class对象时, 类加载器先从缓存中搜索该Class, 只有当缓存中不存在时, 系统才会读取该类对应的二进制数据, 并将其转换为Class对象, 存储到缓存区
classLoder: 负责加载类的对象
java运行时具有以下内置类加载器
- Bootstrap class loader: 虚拟机内置加载器, 通常表示为null, 并且没有null
- Platform class loader: 平台类加载器可以看到所有平台类, 平台类包括有平台类加载器或其祖先定义的java SE平台api, 其实现类和JDK特定的运行时类
- System class loader: 应用程序类加载器, 与平台类加载器不同. 通常用于定义应用程序类路径, 模块路径和JDK特定工具上的类.
注意: System的父类加载器为Platform, 而Platform的父类加载器为Bootstrap
ClassLoader中的两个方法:
- static ClassLoader getSystemClassLoader(): 返回用于委派的系统类加载器
- ClassLoader getParent(): 返回父类加载器进行委派
package reflect.classLoader;
public class ClassLoaderDemo {
public static void main(String[] args) {
// static ClassLoader getSystemClassLoader(): 返回用于委派的系统类加载器
ClassLoader c = ClassLoader.getSystemClassLoader();
System.out.println(c); // AppClassLoader@512ddf17
// ClassLoader getParent(): 返回父类加载器进行委派
ClassLoader c2 = c.getParent();
System.out.println(c2); // PlatformClassLoader@4c203ea1
ClassLoader c3 = c2.getParent();
System.out.println(c3); // null
}
}
反射
反射概述
反射机制: 运行时去获取一个类的变量和方法信息. 然后通过获取到的信息来创建对象, 调用方法的一种机制.
这种动态性, 可以极大的增强程序的灵活性, 程序不同在编译期就完成确定, 在运行期仍可以扩展
获取Class类对象
通过反射使用一个类, 需要获取到该类的字节码文件对象, 也就是Class类型的对象
获取Class对象的方式
- 使用类的class属性获取该类对应的Class对象: Student.class
- 调用对象的getClass()方法, 返回改对象所属类对应的Class对象(该方法是Object类的方法,所有对象都可以调用)
- 使用Class类中的静态方法forName(String className), 该方法需要传入字符串参数, 该字符串参数的值是某个类的全路径, 也就是完整包名的路径
package reflect.reflect;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 使用类的class属性获取
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);
System.out.println("-------");
// 调用对象的getClass()方法, 返回该对象所属类对应的class对象
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
System.out.println("-------");
// 使用Class类中的静态方法forName(String className)
Class<?> c4 = Class.forName("reflect.reflect.Student");
System.out.println(c1 == c4);
}
}
反射获取构造方法并使用
Class类中用于获取构造方法的方法
- Constructor<?>[] getConstructors (): 返回所有公共构造方法对象的数组
- Constructor<?>[] getDeclaredConstructors (): 返回所有构造方法对象的数组
- Constructor
getConstructor(Class<?>... parameterTypes): 返回单个公共构造方法对象 - Constructor
getDeclaredConstructor(Class<?>... parameterTypes): 返回单个构造方法对象
package reflect.reflect;
import file.IOStream.SystemOutDemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取Class对象
Class<?> c = Class.forName("reflect.reflect.Student");
// Constructor<?>[] getConstructors (): 返回一个包含Constructor对象的数组, Constructor对象反映了由该class对象表示的类的所有公共构造函数
Constructor<?>[] cons = c.getConstructors();
for(Constructor con: cons) {
System.out.println(con);
}
// public reflect.reflect.Student()
// public reflect.reflect.Student(java.lang.String,int,java.lang.String)
// Constructor<?>[] getDeclaredConstructors (): 返回反应由该class对象表示的类的所有构造函数的Constructor对象的数组
Constructor<?>[] cons1 = c.getDeclaredConstructors();
for(Constructor con: cons1){
System.out.println(con);
}
System.out.println("-------");
// Constructor<T> getConstructor(Class<?>... parameterTypes): 返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数
// Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes): 返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
// 参数: 你要获取的构造方法的参数的个数, 和 数据类型对应的字节码文件对象
Constructor<?> con = c.getConstructor();
System.out.println(con);
// Constructor: 提供有关类的单个构造函数的信息和访问权限
// T newInstance(Object... initargs): 使用此Constructor对象表示的构造方法,使用指定的初始化参数创建和初始化构造函数声明类的新实例
Object obj = con.newInstance();
System.out.println(obj);
// 练习: 通过反射获取构造方法, 创建对象
Class<?> cl = Class.forName("reflect.reflect.Student");
// public Student(String name, int age, String address)
// Constructor<T> getConstructor (Class<?> ... parameterTypes)
Constructor<?> constructor = c.getConstructor(String.class, int.class, String.class);
// J基本数据类型也可以通过.class获取对应的Class类型
Object stu = constructor.newInstance("林青霞", 30, "西安");
System.out.println(stu);
// 私有构造方法
Constructor<?> constructor1 = c.getDeclaredConstructor(String.class);
// 暴力反射
// public void setAccessible(boolean flag): 值为true, 取消访问检查
constructor1.setAccessible(true);
Object obj2 = constructor1.newInstance("林青霞");
System.out.println(obj2);
}
}
注意:
- 基本数据类型也可以用.class获取对应的Class类型
- public void setAccessible(boolean flag): 值为true, 取消访问检查, 私有构造方法可以来创建对象
反射获取成员变量使用
Class中用于获取成员变量的方法
- Field[] getFields(): 返回一个数组, 包含该Class对象表示类或接口中的所有公共成员变量
- Field[] getDeclaredFields(): 返回一个数组, 包含该Class对象表示类或接口中的所有成员变量
- Field getField(String name): 返回一个Field对象, 反应该Class对象表示类或接口中的指定公共成员字段
- Field getDeclaredField(String name): 返回一个Field对象, 反应该Class对象表示类或接口中的指定成员字段
Field类中用于给成员变量赋值的方法
- void set(Object obj, Object value): 给obj对象的成员变量赋值为value
package reflect.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取Class对象
Class<?> c = Class.forName("reflect.reflect.Student");
// Field[] getFields(): 返回一个数组, 包含该Class对象表示类或接口中的所有公共成员变量
// Field[] getDeclaredFields(): 返回一个数组, 包含该Class对象表示类或接口中的所有成员变量
// Field[] fields = c.getFields();
Field[] fields = c.getDeclaredFields();
for(Field field: fields) {
System.out.println(field);
}
System.out.println("--------");
// Field getField(String name): 返回一个Field对象, 反应该Class对象表示类或接口中的指定公共成员字段
// Field getDeclaredField(String name): 返回一个Field对象, 反应该Class对象表示类或接口中的指定成员字段
Field address = c.getField("address");
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
address.set(obj, "西安"); // 给obj的成员变量address赋值为西安
/*
Student s = new Student();
s.address = "西安";
System.out.println(s.address);
*/
}
}
练习
package reflect.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
// 获取class对象
Class<?> c = Class.forName("reflect.reflect.Student");
// Student s = new Student();
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
// s.name = "林青霞"
// Field nameField = c.getField("name"); // NoSuchFieldException: name
Field nameField = c.getDeclaredField("name"); // IllegalAccessException
nameField.setAccessible(true);
nameField.set(obj, "林青霞");
System.out.println(obj);
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 30);
System.out.println(obj);
Field addressField = c.getDeclaredField("address");
addressField.setAccessible(true);
addressField.set(obj, "西安");
System.out.println(obj);
}
}
反射获取成员方法使用
Class类中用于获取成员方法的方法
-
Method[] getMethods(): 返回数组, 包含由该Class对象表示的类或接口的所有公共方法(包括继承父类的)
-
Method[] getDeclaredMethods(): 返回数组, 包含由该Class对象表示的类或接口的所有方法(不包含继承的)
-
Method getMethod(String name, class<?>... parameterTypes): 返回由该Class对象表示的类或接口指定的公共方法
-
Method getDeclaredMethod(String name, class<?>... parameterTypes): 返回由该Class对象表示的类或接口指定的任意方法
Method类中用于调用成员方法的方法
- Object invoke(Object obj, Object ... args): 调用obj对象的成员方法, 参数是args, 返回值是object类型
package reflect.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 获取Class对象
Class<?> c = Class.forName("reflect.reflect.Student");
// Method[] getMethods(): 返回数组, 包含由该Class对象表示的类或接口的所有公共方法(包括继承父类的)
// Method[] getDeclaredMethods(): 返回数组, 包含由该Class对象表示的类或接口的所有方法(不包含继承的)
// Method[] methods = c.getMethods();
Method[] methods = c.getDeclaredMethods();
for(Method method: methods) {
System.out.println(method);
}
System.out.println("-------");
// Method getMethod(String name, class<?>... parameterTypes): 返回由该Class对象表示的类或接口指定的公共方法
// Method getDeclaredMethod(String name, class<?>... parameterTypes): 返回由该Class对象表示的类或接口指定的任意方法
Method method = c.getMethod("method1");
// 获取无参构造方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// obj.m(); 无法调用
// 类或接口上提供有关单一方法的信息和访问权限
// Object invoke(Object obj, Object ... args): 在具有指定参数的指定对象上调用此方法表示的基础方法
// Object: 返回值类型
// obj: 调用方法的对象
// args: 方法需要的参数
method.invoke(obj); // 调用该对象的方法
/*
Student s = new Student();
s.method1();
*/
}
}
方法
package reflect.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 创建Class对象
Class<?> c = Class.forName("reflect.reflect.Student");
// 获取无参构造方法
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// s.method1
Method m1 = c.getMethod("method1");
m1.invoke(obj);
// s.method2("林青霞")
Method m2 = c.getMethod("method2", String.class);
m2.invoke(obj, "林青霞");
// s.method3("林青霞", 30)
Method m3 = c.getMethod("method3", String.class, int.class);
Object o = m3.invoke(obj, "林青霞", 30);
System.out.println(o);
// s.function()
// Method m4 = c.getMethod("function");
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(obj);
}
}
练习1: 实现往ArrayList
package reflect.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/*
往ArrayList<Integer>集合中添加字符串数据
*/
public class ReflectDemo7 {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// 创建集合
ArrayList<Integer> array = new ArrayList<Integer>();
/*
array.add(10);
array.add(20);
array.add("hello"); // 不可行
*/
Class<? extends ArrayList> c = array.getClass();
Method m = c.getMethod("add", Object.class);
m.invoke(array,"hello");
m.invoke(array,"java");
m.invoke(array,"world");
// 通过反射调用方法可以跳过泛型检查
System.out.println(array);
}
}
练习2: 通过配置文件运行类中的方法
class.txt
className=reflect.reflect.fileReflect.Student
methodName=study
Student
package reflect.reflect.fileReflect;
public class Student {
public void study() {
System.out.println("好好学习");
}
}
ReflectDemo
package reflect.reflect.fileReflect;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/*
通过配置文件运行类中的方法
*/
public class ReflectDemo8 {
public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
/*
Student s = new Student();
s.study();
Teacher t = new Teacher();
t.teach();
// 如果我们要切换两个类方法调用, 需要来回修改代码, 很不方便
*/
// 通过配置文件调用类方法
/*
class.txt
className=xxx
methodName=xxx
*/
// 加载数据
Properties prop = new Properties();
FileReader fr = new FileReader("learn_java/src/reflect/reflect/fileReflect/class.txt");
prop.load(fr);
fr.close();
// 获取方法名和类名
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
// 通过反射使用
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 调用study方法
Method m = c.getMethod("study");
m.invoke(obj);
}
}