类加载器
(1)概述:将 编译生成的 .class 文件 加载到内存当中
(2)加载时机:某个类,需要被使用的时候,就会被加载( .class文件 —> 内存 虚拟机 ) “用到被加载,不用不加载”
(3)类加载器步骤
a) 加载
aa) 通过 包名称和类名称找到这个字节码文件(class文件)
bb) 通过IO流, 将字节码文件(class文件) 读取到内存当中
cc) 在内存当中, 创建一个 Class 字节码的对象 (包含着 成员变量、成员方法、构造方法 等信息)
b) 链接
aa) 验证: 检查代码当中有没有语法性错误(少了一个分号,多了一个大括号…)(语法分析)
bb) 准备: 对于静态(static)内容可以先设置 默认值
cc) 解析: class Student{ String name;} —> class Student{ &&& name;} —> class Student{ 0x666 name}; 符号引用替换成为直接引用
c) 初始化
aa) 就是给成员变量或者是静态变量,进行赋值的操作。
bb) 成员变量,可能是创建对象的时候,通过构造方法进行赋值。
cc) 静态变量,可能是直接赋值的过程
(4)类加载器的分类
a) 启动类加载器
aa) 单词: BootstrapClassLoader
bb)作用: 虚拟机内置的类加载器
b) 平台类加载器
aa) 单词: PlatformClassLoader
bb) 作用: 负责加载 JDK 当中的一些特殊的模块
c) 系统类加载器
aa) 单词: SystemClassLoader
bb) 作用: 负责加载 用户类路径上所指定的类库
(5)双亲委派模型
(6)案例代码
(properties文件必须要放在src目录下面)
//系统类加载器的两个重要方法!
public class Test01 {
//强调: 如果是采用系统类加载器,加载问题,文件必须在 src 下面!!!!!
public static void main(String[] args) throws IOException {
// (1). 获取到系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// (2). 通过系统类加载器,得到字节输入流对象
//解释: 因为字节流是万能的,但是如果遇到了乱码问题,还是需要通过转换流得到字符流的
InputStream is = systemClassLoader.getResourceAsStream("xixi.properties");
InputStreamReader reader = new InputStreamReader(is,"GBK");
// (3). 准备 properties 的对象,用于加载数据
Properties pp = new Properties();
pp.load(reader);
// (4). 展示结果
Set<String> keySet = pp.stringPropertyNames();
for (String key : keySet) {
//通过key获取值
String value = pp.getProperty(key);
System.out.println(key + ":" + value);
}
}
}
反射
(1)概述:动态加载数据,动态调用方法,可以无视权限修饰符(例如 private )
(2)获取字节码对象
a) 三种方式
aa) 方式一: Class.forName(“包名称.类名称”);(如果别人什么都没有给你, 建议采用第一种方式。配置文件采用的是第一种方式)
bb) 方式二: 类名称.class(如果别人给你的是一个类,采用第二种方式)
cc) 方式三: 对象名称.getClass();(如果别人给你的是一个对象,采用第三种方式)
(3)获取构造方法
a) API
方法名 | 备注 |
---|---|
Constructor<?>[] getConstructors() | 获取构造方法数组, 构造方法是 public 修饰的 |
Constructor<?>[] getDeclaredConstructors() | 获取构造方法数组, 构造方法是 任意权限修饰符 |
Constructor getConstructor(Class… parameterTypes) | 返回单个构造方法对象, 构造方法是 public 修饰的 |
Constructor getDeclaredConstructor(Class… parameterTypes) | 返回单个构造方法对象, 构造方法是 任意权限修饰符 |
b) 创建对象的方法
T newInstance(Object… initargs) //根据指定的构造方法创建对象
方式1: clazz.newInstance(); //省略了获取构造方法 Constructor 对象的操作,直接使用 默认无参数构造方法创建对象。
方式2: constructor.newInstance(); //使用构造方法创建对象, 采用的是无参数构造方法。
方式3: constructor.newInstance(“zhangsan”,23); //使用构造方法创建对象, 采用的带参数构造方法。 参数1:String类型、参数2:int类型
c) 取消权限修饰符(暴力反射)
setAccessible(boolean) //默认情况下是 false, 如果设置为 true 任意权限修饰符都可以访问并且修改
(4)获取成员变量
a) API
方法 | 备注 |
---|---|
Field[] getFields() | 获取成员变量数组, 成员变量是 public 修饰的 |
Field[] getDeclaredFields() | 获取成员变量数组, 成员变量是 任意权限修饰符 |
Field getField(String name) | 返回单个成员变量对象, 成员变量是 public 修饰的 |
Field getDeclaredField(String name) | 返回单个成员变量对象, 成员变量是 任意权限修饰符 |
b) 取消权限检查
setAccessible(boolean) //默认情况下是 false, 如果设置为 true 任意权限修饰符都可以访问并且修改
c) 存取值
方法 | 备注 |
---|---|
void set(Object obj,Object value) | 给obj对象的成员变量赋值为 value |
Object get(Object obj) | 返回由该 Field 表示的字段在指定对象上的值 |
(5)获取成员方法
a) API
方法 | 备注 |
---|---|
Method[] getMethods() | 获取成员方法数组, 成员方法是 public 修饰的 |
Method[] getDeclaredMethods() | 获取成员方法数组, 成员方法是 任意权限修饰符 |
Method getMethod(String name,Class<?>… parameterTypes) | 返回单个成员方法对象, 成员方法是 public 修饰的 |
Method getDeclaredMethod(String name,Class<?>… parameterTypes) | 返回单个成员方法对象, 成员方法是 任意权限修饰符 |
b) 取消权限检查
setAccessible(boolean) //默认情况下是 false, 如果设置为 true 任意权限修饰符都可以访问并且修改
c) 调用方法
Object invoke(Object o,Object… args)
例如: Object result = method.invoke(student,“张三”); //result表示方法返回值, method表示方法的对象, student表示学生对象, "张三"表示方法参数。
(6)案例一
a) 获取成员变量
//学生类
public class Student {
private String name;
private int age;
//无参数构造方法
public Student() {
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//测试类
public class Test {
//给 Student类的成员变量进行赋值,取值的操作 name="zhangsan"
public static void main(String[] args) throws Exception{
// (1). 获取到字节码的对象
Class<?> clazz = Class.forName("com.itheima03.Student");
// (2). 获取到构造方法,并且创建对象. 因为: 成员变量,需要使用到对象
Constructor<?> c = clazz.getDeclaredConstructor();
c.setAccessible(true);
Object o = c.newInstance(); //这里的o就是 学生类 Student 的对象
System.out.println("o = " + o);
// (3). 获取到成员变量,并且设置访问权限
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
// (4). 存值取值等操作
nameField.set(o,"zhangsan");
Object value = nameField.get(o);
System.out.println("value = " + value);
System.out.println("o = " + o);
}
}
b) 获取成员方法
// 学生类
// String getName()
// void setName(String)
public class Student {
public Student() {
}
//定义,无参数,无返回值方法
public void show() {
System.out.println("学生类,无参数无返回值show");
}
//定义,有参数,有返回值方法
public char eat(String str, int index) {
System.out.println("学生类,有参数有返回值eat");
char cc = str.charAt(index);
return cc;
}
}
//测试类
public class Test {
public static void main(String[] args) throws Exception{
// (1). 获取到字节码的对象
Class<?> clazz = Class.forName("com.itheima04.Student");
// (2). 获取到构造方法对象,通过构造方法,创建对象
Constructor<?> c = clazz.getDeclaredConstructor();
c.setAccessible(true);
Object o = c.newInstance(); //也就是学生类 Student 对象
// (3). 获取成员方法对象,取消权限检查
Method showMethod = clazz.getDeclaredMethod("show");
showMethod.setAccessible(true);
Method eatMethod = clazz.getDeclaredMethod("eat", String.class, int.class);
eatMethod.setAccessible(true);
// (4). 调用方法
showMethod.invoke(o);
Object fanHuiZhi = eatMethod.invoke(o, "hello", 1);
System.out.println("fanHuiZhi = " + fanHuiZhi);
}
}
(7)案例二(仿框架)
a) 配置文件内容
className=com.it.Pig
methodName=killed
b) 代码
package com.it;
//猫类
public class Cat {
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
package com.it;
//狗类
public class Dog {
public void lookHouse(){
System.out.println("狗看家...");
}
}
package com.it;
//猪类
public class Pig {
public void killed(){
System.out.println("猪被杀..");
}
}
//可以创建任何一个类的对象,调用对应的方法
public class Test {
public static void main(String[] args) throws Exception {
// (1). 加载数据,获取到 全类名 和 方法名称
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
InputStream is = systemClassLoader.getResourceAsStream("config.properties");
Properties pp = new Properties();
pp.load(is);
is.close();
String className = pp.getProperty("className");
String methodName = pp.getProperty("methodName");
// --------
// (2). 采用反射,去调用方法
Class<?> clazz = Class.forName(className);
Constructor<?> c = clazz.getDeclaredConstructor();
c.setAccessible(true);
Object o = c.newInstance();
Method m = clazz.getDeclaredMethod(methodName);
m.setAccessible(true);
m.invoke(o);
}
}