zoukankan      html  css  js  c++  java
  • 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);
        }
    }
    
  • 相关阅读:
    一张图帮你分清scroll、offset、client
    js两种显示日期的方法
    理解js的全局变量和局部变量
    中文输入+英文标点+快速编辑Markdown文本+Sublime+Snippet
    Markdown 使用方法
    get和post的区别
    js对象属性方法大总结(收集)
    bfc (收集的)
    客户端网页编程知识总结
    html学习总结
  • 原文地址:https://www.cnblogs.com/ryxiong-blog/p/13890631.html
Copyright © 2011-2022 走看看