18_类加载

文章目录

  • 类加载器
    • 类加载时机
    • Java代码的3个阶段
  • 反射
    • 关于Class
    • 配置文件(.properties)
      • Properties类
      • 通过反射获取构造方法(Constructor)
      • 通过反射获取成员变量(Field)
      • 通过反射获取成员方法(Method)
  • 其他API
  • 自定义类加载器
  • 反射的应用

类加载器

分类

  • Bootstrap ClassLoader 根类加载器

    • 负责Java运行时核心类的加载,JDK中JRE的lib目录下rt.jar
  • Extension ClassLoader 扩展类加载器

    • 负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
  • Sysetm(App) ClassLoader 系统类加载器/应用加载器

    • 负责加载自己定义的Java类

逻辑上的父子关系

在这里插入图片描述

类加载时机

  • 创建类的实例(首次创建该类对象)

  • 访问类的静态变量(首次)

  • 调用类的静态方法(首次)

  • 加载某个类的子类,会先触发父类的加载

  • 直接使用java.exe命令来运行某个主类,也就是执行了某个类的main()方法

  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

Java代码的3个阶段

在这里插入图片描述


反射

获取运行时类信息的一种手段,反射的起点是字节码文件对象

获取字节码文件对象的几种方式

  • 对象.getClass()
  • 类名.class
  • Class.forName(String className) 全限定名
  • ClassLoader里的loadClass(String className)

无论通过什么方式获取的字节码文件对象,都是同一个

eg:


public class Demo {

    public static void main(String[] args) throws ClassNotFoundException {

        // 对象.getClass()
        A a = new A();
        Class<? extends A> c1 = a.getClass();

        // 类名.class
        Class<A> c2 = A.class;

        System.out.println(c1 == c2);

        // Class.forName(String className)  全限定名
        Class<?> c3 = Class.forName("com.cskaoyan.Demo0112.A");

        System.out.println(c1 == c3);

        // ClassLoader里的loadClass(String className)
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        Class<?> c4 = systemClassLoader.loadClass("com.cskaoyan.Demo0112.A");
        System.out.println(c1 == c4);
    }
}



class A{

}

  • 类名.class 没有执行静态代码块
  • Class.forName() 执行静态代码块

eg:


@Test
    public void test1() throws ClassNotFoundException {
        // 类名.class
        // 没有执行静态代码块
        Class<B> b1 = B.class;

        // Class.forName()
        // 执行静态代码块
        Class<?> b2 = Class.forName("com.cskaoyan.Demo0112.B");
    }


class B{
    static{
        System.out.println("this is static");
    }
}

关于Class

  • Class 类的实例表示正在运行的 Java 应用程序中的类和接口

  • Class 没有公共构造方法Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

配置文件(.properties)

  • 配置文件的几种格式.properties.xml.yml

  • 配置文件的作用: 放配置信息的 (数据库的, 第三方服务的配置信息)

  • .properties的格式:

    • 键值对(key-value)
    • key=value
    • key不能重复的
    • 注释#
    • 文件里面全是String

Properties类

  • Properties 类表示了一个持久的属性集
  • Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串

文件 ---> 资源包创建可得

eg:


# 数据库的配置信息
url=jdbc:mysql://localhost:3306/test
host=localhost
port=3306
user= root
password=1234567

构造方法


Properties()    // 创建一个无默认值的空属性列表。

成员方法

在这里插入图片描述

在这里插入图片描述

eg:


public class Demo {
    public static void main(String[] args) throws IOException {
        // 1. 创建Properties对象
        Properties properties = new Properties();
        // 2. load
        properties.load(new FileInputStream("People.properties"));

        // 3. 获取属性值
        // getProperty(String key)
        String port = properties.getProperty("port");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String host = properties.getProperty("host");
        System.out.println(port);
        System.out.println(password);
        System.out.println(user);
        System.out.println(host);

    }
}


public class Demo {
    public static void main(String[] args) throws IOException {
        // 1. 创建Properties对象
        Properties properties = new Properties();

        // 2. 通过类加载器
        URL systemResource = ClassLoader.getSystemResource("");
        System.out.println(systemResource); 
        // "" 在这里面,把"People.properties"应该放这里输出的路径下面

        InputStream in = ClassLoader.getSystemResourceAsStream("People.properties");

        // 3. load
        properties.load(in);

        // 4. 获取属性值
        // getProperty(String key)
        String port = properties.getProperty("port");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String host = properties.getProperty("host");
        System.out.println(port);
        System.out.println(password);
        System.out.println(user);
        System.out.println(host);

    }
}


输出中文的情况


public class Demo {
    public static void main(String[] args) throws IOException {
        // 创建Properties对象
        Properties properties = new Properties();

        // load
        properties.load(
                new InputStreamReader(
                        new FileInputStream("People.properties"),"GBK"));

        // 获取属性
        String user = properties.getProperty("user");
        System.out.println(user);
    }
}

通过反射获取构造方法(Constructor)

通过反射获取所有构造方法

Constructor[] getConstructors() // 获取的是public的构造方法

Constructor[] getDeclaredConstructors() // 获取所有的构造方法

获取指定构造方法

Constructor<T> getConstructor(Class<?>... parameterTypes)

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

使用Constructor创建对象

Person p = new Person("zs",20,true)

newInstance(参数列表)

暴力破解

上面的方法创建不了private的对象,这里可以

setAccessible(true) // 忽略java语法检查

先定义一个Person类


public class Person {
    public String name;
    private int age;
    boolean gender;

    public Person(String name, int age, boolean gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name) {
        this.name = name;
    }

    public Person() {
    }

    public void eat() {
        System.out.println("eat food");
    }

    private void eat(String food) {
        System.out.println("eat" + food);
    }

    private String sleep() {
        return "sleep";
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}

eg:


public class Demo {
    public static void main(String[] args)
            throws ClassNotFoundException, NoSuchMethodException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1. 获取字节码文件对象
        Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");

        // 2. 获取所有的构造方法
        // Constructor[] getConstructors()
        Constructor<?>[] constructors = a1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }

        // Constructor[] getDeclaredConstructors()
        Constructor<?>[] declaredConstructors = a1.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }

        // 3. 获取指定构造方法
        // Constructor<T> getConstructor(Class<?>... parameterTypes)
        Constructor<?> constructor = a1.getConstructor
                (String.class, int.class, boolean.class);
        System.out.println(constructor);

        // Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        Constructor<?> declaredConstructor = a1.getDeclaredConstructor
                (String.class, int.class);
        System.out.println(declaredConstructor);

        // 4. 使用Constructor创建对象

        // 通过构造方法对象创建对象
        //newInstance(参数列表)
        Object o = constructor.newInstance("zs", 20, true);
        System.out.println(o);

        // 5. setAccessible(true)
        declaredConstructor.setAccessible(true);
        Object o1 = declaredConstructor.newInstance("ww", 18);
        System.out.println(o1);
    }
}


通过反射获取成员变量(Field)

通过反射获取所有成员变量

Field[] getFields()

Field[] getDeclaredFields()

获取指定成员变量

Field getField(String name) // 获取的是public权限

Field getDeclaredField(String name) // 获取的是所有权限

通过Field读写对象的成员变量(可暴力破解)

Object get(Object obj)  // 获取值,传入对象

void set(Object obj, Object value)  // 赋值,传入对象

eg:


public class Demo {
    public static void main(String[] args) 
            throws ClassNotFoundException, NoSuchFieldException, 
            NoSuchMethodException, InvocationTargetException, 
            InstantiationException, IllegalAccessException {
        // 1. 拿到字节码文件对象
        Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");

        // 2. 通过反射获取所有的成员变量
        // Field[] getFields()
        Field[] fields = a1.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        // Field[] getDeclaredFields()
        Field[] declaredFields = a1.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        // 3. 获取指定成员变量
        // Field getField(String name)
        Field nameField = a1.getField("name");
        System.out.println(nameField);

        // Field getDeclaredField(String name)
        Field ageField = a1.getDeclaredField("age");
        System.out.println(ageField);

        // 4. 通过Field读写对象的成员变量(可暴力破解)
        // void set(Object obj, Object value):赋值,传入对象
        Constructor<?> declaredConstructor = a1.getDeclaredConstructor();
        Object o = declaredConstructor.newInstance();
        nameField.set(o,"zs");
        System.out.println(o);

        // Object get(Object obj):获取值,传入对象
        Object o1 = nameField.get(o);
        System.out.println(o1);
    }
}

通过反射获取成员方法(Method)

获取所有成员方法

Method[] getMethods()// 父类的也能获取到,只能获取public的方法

Method[] getDeclaredMethods() // 能够获取所有的方法

获取指定的成员方法

Method getMethod(String name, Class<?>... parameterTypes)

Method getDeclaredMethod(String name, Class<?>... parameterTypes)

利用Method调用对象的方法

Object invoke(Object obj, Object... args)

eg:


public class Demo {
    public static void main(String[] args) 
            throws ClassNotFoundException, NoSuchMethodException, 
            InvocationTargetException, InstantiationException, 
            IllegalAccessException {
        // 1. 获取字节码文件对象
        Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");

        // 2. 获取所有成员方法
        // Method[] getMethods()// 父类的也能获取到
        Method[] methods = a1.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        // Method[] getDeclaredMethods()
        Method[] declaredMethods = a1.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        // 3. 获取指定的成员方法
        // Method getMethod(String name, Class<?>... parameterTypes)
        Method eatMethod = a1.getMethod("eat");
        System.out.println(eatMethod);

        // Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        Method eatDeclaredMethod = a1.getDeclaredMethod("eat", String.class);
        System.out.println(eatDeclaredMethod);

        // 利用Method调用对象的方法
        // Object invoke(Object obj, Object... args)
        Constructor<?> declaredConstructor = a1.getDeclaredConstructor();
        Object o = declaredConstructor.newInstance();

        eatMethod.invoke(o);
    }
}


其他API

  • 在这里插入图片描述
    :可以通过Class直接实例化 , 但是要有一个无参构造方法

eg:


public class Demo {
    public static void main(String[] args)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.A");

        // 通过class对象直接实例化对象
        Object o = a1.newInstance();
        System.out.println(o);
    }
}

class A {
    int a;

    public A(int a) {
        this.a = a;
    }

    public A() {

    }
}

  • 在这里插入图片描述

eg:


public class Demo {
    public static void main(String[] args)
            throws ClassNotFoundException, NoSuchFieldException,
            NoSuchMethodException {
        // 1. 先获取字节码文件对象
        Class<?> a1 = Class.forName("com.cskaoyan.Demo0112.Person");
        Class<?> a2 = Class.forName("java.io.OutputStream");

        // 2. 获取类名
        System.out.println(a1.getName());

        // 3. 获取简单名
        System.out.println(a1.getSimpleName());

        // 4. 获取父类
        System.out.println(a1.getSuperclass());
        System.out.println(a1.getSuperclass().getSimpleName());

        // 5. 获取实现的接口
        Class<?>[] interfaces = a2.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }

        // 6. 获取成员变量
        Field nameField = a1.getDeclaredField("name");
        // 获取成员变量的类型
        System.out.println(nameField.getType());
        int modifiers = nameField.getModifiers();
        System.out.println(modifiers); // 1 就是public
        System.out.println(Modifier.toString(modifiers));

        // 7. 获取method对象
        Method eatMethod = a1.getDeclaredMethod("eat", String.class);
        // 方法的返回值类型
        System.out.println(eatMethod.getReturnType());
        // 方法的参数类型
        Class<?>[] parameterTypes = eatMethod.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println(parameterType);
        }

    }
}


自定义类加载器

步骤

  • 继承ClassLoader
  • 重写findClass方法

eg:


MyClassLoader自定义类加载器:
public class MyClassLoader extends ClassLoader {
    // 成员
    String classPath;

    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> aClass = null;

        // 读取.class文件
        try {
            byte[] data = getData();

            // defineClass(String name, byte[] b, int off, int len)
            // 将一个 byte 数组转换为 Class 类的实例。
            aClass = defineClass(name, data, 0, data.length);

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 最终要返回一个Class对象
        return aClass;
    }

    private byte[] getData() throws IOException {
        // 读取字节码文件
        // 创建FileInputStream
        FileInputStream in = new FileInputStream(classPath);

        // 使用ByteArrayOutputStream
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        int readCount;
        byte[] bytes = new byte[1024];
        while ((readCount = in.read(bytes)) != -1 ){
            outputStream.write(bytes,0,readCount);
        }

        /*
        byte[] toByteArray()
        创建一个新分配的byte数组
         */
        byte[] bytes1 = outputStream.toByteArray();

        // 返回字节码文件里的数据
        return bytes1;

    }
}



-----------------------------------------------------

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException,
            NoSuchMethodException, InstantiationException,
            IllegalAccessException, InvocationTargetException {
        // 定义加载路径
        String classPath =
                "D:\\Test.class";

        // 创建自定义类加载器
        MyClassLoader myClassLoader = new MyClassLoader(classPath);

        // 通过loadClass方法加载类
        Class<?> testLc = myClassLoader.loadClass("Test");

        // 得到字节码文件对象

        // 拿到Method对象
        Method funcMethod = testLc.getDeclaredMethod("func");

        // invoke
        Object o = testLc.newInstance();
        funcMethod.invoke(o);

    }
}


反射的应用

  • 通过反射获取注解信息

  • 动态代理

  • ORM(Object Relational Mapping)框架, 数据库框架

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/313707.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio中项目添加链接文件

这个需求在VS里面使用还真不多见&#xff0c;只是最近在做项目的版本编号的时候遇到一个头大的问题&#xff0c;我一个解决方案下面有几十个类库&#xff0c;再发布的时候这几十个类库的版本号必须要统一&#xff0c;之前我们都是在单个的AssemblyInfo.cs里面去改相关的信息&am…

轻量级图床Imagewheel本地部署并结合内网穿透实现远程访问

文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

Echarts的常用API,以及常用的写法

ECharts是一款基于JavaScript的开源可视化库&#xff0c;用于构建交互式的图表和可视化数据。它提供了丰富的API用于定制图表和处理数据。下面是一些常用的ECharts API和写法的简介&#xff1a; 初始化图表容器&#xff1a; var myChart echarts.init(document.getElementBy…

win11更改桌面默认存储路径

打开文件资源管理器 右击桌面点击属性 在属性中找到位置选项卡&#xff0c;在里面有一个移动&#xff0c;点击它选择你想要的位置 选好位置后点击应用&#xff0c;随后会出现一个进度条&#xff0c;跑完后点击确认 到这里就完成了桌面默认位置的转移

在windows11系统上利用docker搭建linux记录

我的windows11系统上&#xff0c;之前已经安装好了window版本的docker&#xff0c;没有安装的小伙伴需要去安装一下。 下面直接记录安装linux的步骤&#xff1a; 一、创建linux容器 1、拉取镜像 docker pull ubuntu 2、查看镜像 docker images 3、创建容器 docker run --…

2024 年 1 月安全更新修补了 58 个漏洞(Android )

谷歌发布了针对 Android 平台 58 个漏洞的补丁&#xff0c;并修复了 Pixel 设备中的 3 个安全漏洞&#xff0c;拉开了 2024 年的序幕。 Android 2024 年 1 月更新的第一部分以 2024 年 1 月 1 日安全补丁级别发布在设备上&#xff0c;解决了框架和系统组件中的 10 个安全漏洞&…

高级分布式系统-第6讲 分布式系统的容错性--故障/错误/失效/异常

分布式系统容错性的概念 分布式系统的容错性&#xff1a; 当发生故障时&#xff0c; 分布式系统应当在进行恢复的同时继续以可接受的方式进行操作&#xff0c; 并且可以从部分失效中自动恢复&#xff0c; 且不会严重影响整体性能。 具体包括以下4个方面的内容&#xff1a; 可…

redis — redis cluster集群模式下如何实现批量可重入锁?

一、redis cluster 集群版 在Redis 3.0版本以后,Redis发布了Redis Cluster。该集群主要支持搞并发和海量数据处理等优势,当 Redis 在集群模式下运行时,它处理数据存储的方式与作为单个实例运行时不同。这是因为它应该准备好跨多个节点分发数据,从而实现水平可扩展性。具体能…

【科研技巧】如何判断某个期刊是什么类别及影响因子?是否是顶会?如何期刊内检索?AI写综述?AI做PPT?

相关链接 查找和免费下载文献的方式汇总国内外各大期刊关系、如何查看期刊等级以及查看某篇论文属于哪个期刊登录和访问EI(Engineering Village)数据库查找文献 1 如何判断某个期刊是什么类别及影响因子 https://sci.justscience.cn/ IFold是影响因子 期刊类别为SCIE、查看…

在线ai扩图是什么?有什么工具?分享3个好用的工具。

在线ai扩图是什么&#xff1f;有什么工具&#xff1f;分享3个好用的工具。 在当今数字化的时代&#xff0c;图像处理成为了我们日常生活和工作中不可或缺的一部分。有时候&#xff0c;我们需要将图像放大以获取更多的细节&#xff0c;但传统的方法往往会导致图像质量的损失。幸…

Nginx服务配置文件

在Nginx服务器的主配置文件/usr/local/nginx/conf/nginx.conf 中&#xff0c;包括全局配置、I/O事件配置 和HTTP配置这三大块内容&#xff0c;配置语句的格式为“关键字 值&#xff1a;”&#xff08;末尾以分号表示结束&#xff09;&#xff0c;以“#” 开始的部分表示注释。 …

小手也能用的高性能鼠标,自定义空间还挺高,雷柏VT9Pro mini上手

今年搭载PAW3395传感器的电竞鼠标很受欢迎&#xff0c;雷柏就出了不少型号&#xff0c;满足各种喜好的玩家选择&#xff0c;像是近期新出的搭载3395高定版的VT9Pro和VT9Pro mini&#xff0c;就在轻量化的基础上&#xff0c;满足了各种手型的玩家的使用需要&#xff0c;而且价格…

2024年美妆品牌如何突破营销困境,强势突围?

随着人们消费观念的升级&#xff0c;美妆护肤几乎成为人们的日常标配&#xff0c;不仅仅女性还有男性也开始注重管理&#xff0c;美妆产品的目标消费群体在不断扩大&#xff0c;对产品的要求也逐渐多元化&#xff0c;在这一趋势下&#xff0c;2024年美妆品牌怎么做才能突破营销…

《MCtalk·CEO对话》正式上线!首期对话高成资本

2015 年 10 月&#xff0c;网易智企发布第一款产品&#xff0c;正式踏上了 ToB 商业化之路。从那以后&#xff0c;我们每年举办不同主题的科技峰会&#xff0c;分享最新的行业体感和洞察&#xff1b;访谈各界企业领导者&#xff0c;记录他们的创新与创业经历&#xff1b;走过大…

从车联网到智慧城市:智慧交通的革新之路

一、引言 1、智慧城市的概念和发展背景 智慧城市&#xff08;Smart City&#xff09;是指以信息技术为基础&#xff0c;运用信息与通信等手段&#xff0c;对城市各个核心系统各项关键数据进行感测、分析、整合和利用&#xff0c;实现对城市生活环境的感知、资源的调控&#x…

web3d-three.js场景设计器-sprite广告牌

three.js使用Sprite精灵实现文字或者图片广告牌1.将文字绘制到Canvas&#xff0c;调整对应宽高。2.作为Cavans材质绑定到Sprite3.加载到场景调整适当的scale function createLabel({ text, fontSize, textColor, color, imageUrl }) { return new Promise((resolve, reject) &…

linux下can调试工具canutils编译安装

命令安装只需要 sudo apt-get install canutils 一、下载源码 下载canutils和libsocketcan libsocketcan地址&#xff1a;https://public.pengutronix.de/software/libsocketcan/libsocketcan-0.0.11.tar.bz2 #0.0.11版本 canutils地址&#xff1a;https://public.pengutronix…

数据分析师面试必备,数据分析面试题集锦(六)

经常会被问到&#xff0c;“数据分析需要学习什么技能&#xff1f;”&#xff0c;“针对实际的业务场景&#xff0c;如何使用数据分析工具去分析&#xff1f;”基于此作者总结数据分析面试常用的问题&#xff0c;面试内容包括技能应用篇&#xff1a;EXCEL、SQL、Python、BI工具…

基于springboot+vue的网上花卉商城系统(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

VS Code 配置 Vue3 模板 详细步骤

1、打开 VS Code &#xff0c;在页面左下角找到这个设置图标&#xff0c;然后找到 “用户代码片段” 2、接着点击 “新建全局代码片段文件” 3、在输入框中输入你要设置的模板名&#xff0c;然后回车确认 4、接下来配置自己想要模板代码&#xff0c;或者也可以借鉴我写的这个&…
最新文章