18_类加载

文章目录

类加载器

分类

  • 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:

java 复制代码
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:

java 复制代码
@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:

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

构造方法

java 复制代码
Properties()    // 创建一个无默认值的空属性列表。

成员方法

eg:

java 复制代码
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);

    }
}

java 复制代码
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);

    }
}

输出中文的情况

java 复制代码
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)

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

java 复制代码
Constructor[] getConstructors() // 获取的是public的构造方法

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

获取指定构造方法

java 复制代码
Constructor<T> getConstructor(Class<?>... parameterTypes)

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

使用Constructor创建对象

java 复制代码
Person p = new Person("zs",20,true)

newInstance(参数列表)

暴力破解

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

java 复制代码
setAccessible(true) // 忽略java语法检查

先定义一个Person类

java 复制代码
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:

java 复制代码
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)

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

java 复制代码
Field[] getFields()

Field[] getDeclaredFields()

获取指定成员变量

java 复制代码
Field getField(String name) // 获取的是public权限

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

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

java 复制代码
Object get(Object obj)  // 获取值,传入对象

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

eg:

java 复制代码
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)

获取所有成员方法

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

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

获取指定的成员方法

java 复制代码
Method getMethod(String name, Class<?>... parameterTypes)

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

利用Method调用对象的方法

java 复制代码
Object invoke(Object obj, Object... args)

eg:

java 复制代码
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:

java 复制代码
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:

java 复制代码
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:

java 复制代码
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)框架, 数据库框架

相关推荐
用户9047066835717 分钟前
如何使用 Spring MVC 实现 RESTful API 接口
java·后端
刘某某.18 分钟前
数组和小于等于k的最长子数组长度b
java·数据结构·算法
程序员飞哥23 分钟前
真正使用的超时关单策略是什么?
java·后端·面试
用户9047066835725 分钟前
SpringBoot 多环境配置与启动 banner 修改
java·后端
小old弟1 小时前
后端三层架构
java·后端
花花鱼1 小时前
spring boot 2.x 与 spring boot 3.x 及对应Tomcat、Jetty、Undertow版本的选择(理论)
java·后端
温柔一只鬼.1 小时前
Docker快速入门——第二章Docker基本概念
java·docker·容器
要争气1 小时前
5 二分查找算法应用
java·数据结构·算法
郑..方..醒1 小时前
java实现ofd转pdf
java·pdf
道可到1 小时前
阿里面试原题 java面试直接过06 | 集合底层——HashMap、ConcurrentHashMap、CopyOnWriteArrayList
java·后端·面试