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
    }
}

反射

反射概述

image-20201027170636014

反射机制: 运行时去获取一个类的变量和方法信息. 然后通过获取到的信息来创建对象, 调用方法的一种机制.

这种动态性, 可以极大的增强程序的灵活性, 程序不同在编译期就完成确定, 在运行期仍可以扩展

获取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);
    }
}
原文地址:https://www.cnblogs.com/ryxiong-blog/p/13890631.html