• 类加载器&反射


    1.类加载器

    1.1类的加载

    概述:

    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类的初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化。

    加载:

    • 就是指将class文件读入内存,并为之创建一个Class对象
    • 任何类被使用时系统都会建立一个Class对象

    连接:

    • 验证:是否有正确的内部结构,并和其他类协调一致
    • 准备:负责为类的静态成员分配内存,并设置默认初始值
    • 解析:将类的二进制数据中的符号引用替换为直接引用

    初始化:

    • 像默认初始化,显示初始化,构造初始化等

    类初始化的时机

    • 1.创建类的实例
    • 2.访问类的静态变量,或者为静态变量赋值
    • 3.调用类的静态方法
    • 4.使用反射方式来强调创建某个类或接口对应的java.lang.Class对象
    • 5.初始化某个类的子类
    • 6.直接使用java.exe命令来运行某个主类

    1.2类加载器

    概述:类加载器负责将.class文件加载到内存中,并为之生成对应的Class对象,虽然我们不需要关心类加载器机制,但是了解这个机制我们就能更好的理解程序的运行。

    类加载器的组成:

    • Bootstrap ClassLoader:根类加载器
      • 也被成为引导类加载器,负责java核心类的加载,比如System,String等,在JDK中JRE的lib目录下的rt.jar文件中
    • Extension ClassLoader:扩展类加载器
      • 负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下的ext目录
    • System ClassLoader:系统类加载器
      • 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

    2.反射

    概述:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展。

    2.1获取Class类对象的三种方式

    获取class文件对象的三种方式

    • 1.类名.class
    • 2.对象.getClass()
    • 3.Class:forName("类名"):这里的类名是包括包名的全称类名(推荐使用)

    代码示例:

    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException {
            //使用类的class属性来获取该类对应的Class对象
            Class<Student> c1 = Student.class;
            System.out.println(c1);
            Class<Student> c2 = Student.class;
            System.out.println(c1 == c2);//true
            System.out.println("--------");
            //调用对象的getClass()方法,返回该对象所属类对应的Class对象
            Student s = new Student();
            Class<? extends Student> c3 = s.getClass();
            System.out.println(c1 == c3);//true
            System.out.println("--------");
            //使用Class类中的静态方法forName(String className)
            Class<?> c4 = Class.forName("com.classtest.Student");
            System.out.println(c1 == c4);//true
        }
    }

    结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

    2.2通过反射获取成员变量,构造方法,成员方法

    2.2.1Class类获取构造方法对象的方法

    • public Constructor[] getConstructors():获得所有的公共构造方法
    • public Constructor[] getDeclaredConstructors():获取所有的构造方法
    • public Constructor getConstructor(Class<?>... parameterTypes):获取单个公共的构造方法,参数表示的是你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
    • public Constructor getDeclaredConstructor(Class<?>... parameterTypes):获取单个构造方法(包括私有构造方法),参数表示的是你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException,NoSuchMethodException, IllegalAccessException, InvocationTargetException,InstantiationException {
            //获取Class对象
            Class<?> c = Class.forName("com.classtest.Student");
            //Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组,Constructor对象反映了由该 Class对象表示的类的所有公共构造函数
            // Constructor<?>[] cons = c.getConstructors();
            //Constructor<?>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组
            Constructor<?>[] cons = c.getDeclaredConstructors();
            for(Constructor con : cons) {
                System.out.println(con);
            }
            System.out.println("--------");
            //Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数
            //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数
            //参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
            Constructor<?> con = c.getConstructor();
            //Constructor提供了一个类的单个构造函数的信息和访问权限
            //T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例
            Object obj = con.newInstance();
            System.out.println(obj);
            // Student s = new Student();
            // System.out.println(s);
        }
    }

    2.2.2Constructor类用于创建对象的方法

    • public T newInstance(Object... instargs):使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例

    2.2.3反射获取成员变量并使用

    • public Field[] getFields():获取所有公共变量
    • public Field[] getDeclaredFields():获取所有变量
    • public Field getField(String name):获取单个指定的变量(不能为私有的变量)
    • public Field getDeclaredField(String name):获取单个变量(可以是私有变量)
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException,NoSuchFieldException, NoSuchMethodException, IllegalAccessException,InvocationTargetException, InstantiationException {
            //获取Class对象
            Class<?> c = Class.forName("com.classtest.Student");
            //Field[] getFields() 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段
            //Field[] getDeclaredFields() 返回一个 Field对象的数组,反映了由该 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 addressField = c.getField("address");
            //获取无参构造方法创建对象
            Constructor<?> con = c.getConstructor();
            Object obj = con.newInstance();
            // obj.addressField = "西安";
            //Field提供有关类或接口的单个字段的信息和动态访问
            //void set(Object obj, Object value) 将指定的对象参数中由此 Field对象表示的字段设置为指定的新值
            addressField.set(obj,"西安"); //给obj的成员变量addressField赋值为西安
            System.out.println(obj);
            // Student s = new Student();
            // s.address = "西安";
            // System.out.println(s);
        }
    }

    2.2.4Field类用于给成员变量赋值的方法

    • 字段对象.set(对象, “该字段的值”):设置某个字段(成员变量)的值
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            //获取Class对象
            Class<?> c = Class.forName("com.itheima_02.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");
            nameField.setAccessible(true);
            nameField.set(obj, "林青霞");
            System.out.println(obj);
            //s.age = 30;
            Field ageField = c.getDeclaredField("age");
            ageField.setAccessible(true);
            ageField.set(obj,30);
            System.out.println(obj);
            //s.address = "西安";
            Field addressField = c.getDeclaredField("address");
            addressField.setAccessible(true);
            addressField.set(obj,"西安");
            System.out.println(obj);
        }
    }

    2.2.5反射获取成员方法并使用

    • public Method[] getMethods():获取自己的包括父亲的成员方法
    • public Method[] getDeclaredMethods():获取自己的方法
    • public Method getMethod(String name, Class<?>... parameterTypes):获取单个方法(不可获取私有的方法)
    • public Method getDeclaredMethod(String name, Class<?>... parameterTypes):获取单个方法(可获得私有的方法)
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            //获取Class对象
            Class<?> c = Class.forName("com.classtest.Student");
            //Method[] getMethods() 返回一个包含方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类
            //Method[] getDeclaredMethods() 返回一个包含方法对象的数组, 方法对象反映由Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法
            // 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对象
            //public void method1()
            Method m = c.getMethod("method1");
            //获取无参构造方法创建对象
            Constructor<?> con = c.getConstructor();
            Object obj = con.newInstance();
            // obj.m();
            //在类或接口上提供有关单一方法的信息和访问权限
            //Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此方法对象表示的基础方法
            //Object:返回值类型
            //obj:调用方法的对象
            //args:方法需要的参数
            m.invoke(obj);
            // Student s = new Student();
            // s.method1();
        }
    }

    2.2.6Method类用于执行方法的方法

    • public Object invoke(Object obj, Object... args):返回值是object接受,第一个参数表示对象是谁,第二个参数表示调用该方法的实际参数
    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            //获取Class对象
            Class<?> c = Class.forName("com.classtest.Student");
            //Student s = new 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,"林青霞");
            // String ss = s.method3("林青霞",30);
            // System.out.println(ss);
            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"); //NoSuchMethodException:com.itheima_02.Student.function()
            Method m4 = c.getDeclaredMethod("function");
            m4.setAccessible(true);
            m4.invoke(obj);
        }
    }

    2.3反射案例

    className=cn.itcast.domain.Student
    methodName=sleep
    /**
     * 框架类
     */
    public class ReflectTest {
        public static void main(String[] args) throws Exception {
            //可以创建任意类的对象,可以执行任意方法
    
            /*
                前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
             */
          /*  Person p = new Person();
            p.eat();*/
    /*
            Student stu = new Student();
            stu.sleep();*/
    
            //1.加载配置文件
            //1.1创建Properties对象
            Properties pro = new Properties();
            //1.2加载配置文件,转换为一个集合
            //1.2.1获取class目录下的配置文件
            ClassLoader classLoader = ReflectTest.class.getClassLoader();
            InputStream is = classLoader.getResourceAsStream("pro.properties");
            pro.load(is);
    
            //2.获取配置文件中定义的数据
            String className = pro.getProperty("className");
            String methodName = pro.getProperty("methodName");
    
    
            //3.加载该类进内存
            Class cls = Class.forName(className);
            //4.创建对象
            Object obj = cls.newInstance();
            //5.获取方法对象
            Method method = cls.getMethod(methodName);
            //6.执行方法
            method.invoke(obj);
    
    
        }
    }
  • 相关阅读:
    【3006】统计数字
    【5001】n皇后问题
    【7001】n阶法雷序列
    【9402】倒序数
    【9705】&&【a801】细胞
    【9802】闭合曲线面积
    【a803】营救
    【9112】求2的n次方的精确值
    V8引擎实现标准ECMA-262(三)
    仔细看看Javascript中的逻辑与(&&)和逻辑或(||)
  • 原文地址:https://www.cnblogs.com/zhuzhaoli/p/13909446.html
Copyright © 2020-2023  润新知