Java学习Day16------类加载器、反射

类加载器、反射

类加载器

(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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值