• Java第三阶段学习(九、类加载器、反射)


    一、类加载器

    1、类的加载:

    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载连接初始化三步来实现对这个类进行初始化

    1.1 加载:

    就是指将class文件读入内存,并为之自动创建一个Class对象(字节码对象)。

    任何类被使用时系统都会建立一个Class对象。

    1.2 连接

      验证 是否有正确的内部结构,并和其他类协调一致

      准备 负责为类的静态成员分配内存,并设置默认初始化值

      解析 将类的二进制数据中的符号引用替换为直接引用

    1.3 初始化

      就是初始化步骤

    2、类初始化时机(也就是什么时候类会初始化):

    1. 创建类的实例

    2. 类的静态变量,或者为静态变量赋值

    3. 类的静态方法

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

    5. 初始化某个类的子类

    6. 直接使用java.exe命令来运行某个主类

    3、类加载器:

     作用: 负责将.class文件加载到内存中,并为之生成对应的Class对象。

      虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行

    4、类加载器的组成

    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包和类路径。

    二、反射

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(包含私有化的属性);对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制

    要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

    1、class类  字节码文件类

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

     获得字节码文件的三个方法:

    方式一: 通过Object类中的getObject()方法

    方式二:通过   类名.class获得

    方式三:通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)

    package com.oracle.Demo02;
    
    public class Demo01 {
    //反射:目的就是不通过new 对象();得到所有属性和方法
        public static void main(String[] args) throws ClassNotFoundException {
            // 获取字节码对象的三个方式
            //1.通过对象获取
            Person p=new Person();
            Class c=p.getClass();
            System.out.println(c);
            //2.通过类名获取(不管是引用类型还是基本数据类型都具备)
            Class c1=Person.class;
            System.out.println(c1);
            //都为true  因为指向的字节码文件都是同一个
            System.out.println(c==c1);   //true
            System.out.println(c.equals(c1)); //true
            //3.通过Class类中的静态方法forName(完整的包名.类名);获取
            Class c2=Class.forName("com.oracle.Demo02.Person");
            System.out.println(c2);
        }
    
    }

    2、通过反射获得构造方法并使用

    在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法。

    2.1 返回一个构造方法:

     public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法

     public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)

    2.2 返回多个构造方法:

     public Constructor<?>[ ] getConstructors() 获取所有的public 修饰的构造方法

     public Constructor<?>[ ] getDeclaredConstructors() 获取所有的构造方法(包含私有的)

    示例代码:

    自定义Person类:

     

    package com.oracle.Demo02;
    
    public class Person {
        public String name;
        private int age;
        static{
            System.out.println("静态代码块");
        }
        public Person(){
            System.out.println("空参构造");
        }
        public Person(String name,int age){
            this.name=name;
            this.age=age;
            System.out.println("有参构造");
        }
        private Person(int age,String name){
            this.name=name;
            this.age=age;
            System.out.println("私有构造");
        }
        public void eat(){
            System.out.println("吃饭");
        }
        public void work(String name){
            System.out.println(name+"工作");
        }
        private void run(String name){
            System.out.println(name+"跑步");
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
        
        
    }

     

     

    获得构造方法并运行:

    package com.oracle.Demo02;
    
    import java.lang.reflect.Constructor;
    
    public class Demo02 {
    
        public static void main(String[] args) throws Exception {
            //反射获取空参构造方法并运行:.getConstructor();
            Class c=Class.forName("com.oracle.Demo02.Person");
            //获取该类中所有的公共构造方法数组
    //        Constructor[] con=c.getConstructors();   本方法还需要循环数组,比较麻烦
    //        for(Constructor c1:con){
    //            System.out.println(c1);
    //        }
            Constructor con=c.getConstructor();
            //调用空参构造方法   .newInstance();
    //        Object obj=con.newInstance();
    //        System.out.println(obj);
            //调用work()方法
    //        Person p=(Person)con.newInstance();
    //        p.work("张三");
            
        }
    
    }

    通过反射获取有参构造方法并运行:

    package com.oracle.Demo02;
    
    import java.lang.reflect.Constructor;
    
    public class Demo03 {
    
        public static void main(String[] args) throws Exception {
            // 通过反射获取有参构造方法并运行
            Class c=Class.forName("com.oracle.Demo02.Person");
            Constructor con=c.getConstructor(String.class,int.class);
            Object obj=con.newInstance("张胜男",16);
            System.out.println(obj);
        }
    
    }

    快速获取空参并创建对象:

    package com.oracle.Demo02;
    
    import java.lang.reflect.Constructor;
    
    public class Demo04 {
    
        public static void main(String[] args) throws Exception {
            // 快速获取空参并创建对象的方式
            //1.类必须有空参构造,2.构造方法必须是public
    //        Class c=Class.forName("com.oracle.Demo02.Person");
    //        Object obj=c.newInstance();
    //        System.out.println(obj);
            //获取私有的构造方法(不推荐)
            //暴力反射,破坏了程序的封装性和安全性
            Class c=Class.forName("com.oracle.Demo02.Person");
            //获取所有的构造方法.getDeclaredConstructors();
    //        Constructor[] con=c.getDeclaredConstructors();
    //        for(Constructor cc:con){
    //            System.out.println(cc);
    //        }
            //调用私有化构造方法
            Constructor con=c.getDeclaredConstructor(int.class,String.class);
            con.setAccessible(true);  //暴力反射的开关,为true的时候,可以得到私有的成员   
            Object obj=con.newInstance(18,"张三");
            System.out.println(obj);
                    
        }
    
    }

    3、通过反射获取成员变量并使用

    在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:

    3.1 返回一个成员变量

      public Field getField(String name) 获取指定的 public修饰的变量(不包含私有)

      public Field getDeclaredField(String name) 获取指定的任意变量(包含私有)

    3.2 返回多个成员变量

      public Field [ ] getFields() 获取所有public 修饰的变量 (不包含私有)

      public Field [ ] getDeclaredFields() 获取所有的 变量 (包含私有)

     

    获取成员变量步骤如下:

     

    1. 获取Class对象

     

    2. 获取构造方法

     

    3. 通过构造方法,创建对象

     

    4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)

     

    5. 通过方法,给指定对象的指定成员变量赋值或者获取值

     

        public void set(Object obj, Object value)

     

        在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值

     

        public Object get(Object obj)

     

        返回指定对象obj中,此 Field 对象表示的成员变量的值

     

    package com.oracle.Demo02;
    
    import java.lang.reflect.Field;
    
    public class Demo05 {
    
        public static void main(String[] args) throws Exception {
            // 通过反射获取成员变量并改值
            Class c=Class.forName("com.oracle.Demo02.Person");
            Object obj=c.newInstance();  //反射获得的类对象
            //获取所有的公共的成员变量
            Field[] fields=c.getFields();
            //遍历
    //        for(Field f:fields){
    //            System.out.println(f);
    //        }
            //通过名字获取公共的成员变量
            Field field=c.getField("name");
            //给成员变量赋值
            field.set(obj, "张三");
            System.out.println(obj);
        }
    
    }

     4、通过反射获得成员方法并使用:

    在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:

    4.1 返回获取一个方法:

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

      获取public 修饰的方法

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

      获取任意的方法,包含私有的

    参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型

    4.2 返回获取多个方法:

    public Method[ ] getMethods() 获取本类与父类中所有public 修饰的方法

    public Method[ ] getDeclaredMethods() 获取本类中所有的方法(包含私有的)

    package com.oracle.Demo02;
    
    import java.lang.reflect.Method;
    
    public class Demo06 {
    
        public static void main(String[] args) throws Exception {
            Class c=Class.forName("com.oracle.Demo02.Person");
            Object obj=c.newInstance();
            //获取所有公共的方法(包括从父类继承来的)
    //        Method[] method=c.getMethods();
    //        for(Method m:method){
    //            System.out.println(m);
    //        }
            //获取空参成员方法并运行,如果是空参方法,只需要写方法名就可以
            Method method01=c.getMethod("eat");
            //调用方法
            method01.invoke(obj);
            //如果是空参方法,只需要写方法名就可以
            System.out.println(method01);
        }
    
    }

    获取有参方法并运行:

    package com.oracle.Demo02;
    
    import java.lang.reflect.Method;
    
    public class Demo07 {
        public static void main(String[] args) throws Exception {
            //获取有参方法并运行
            Class c=Class.forName("com.oracle.Demo02.Person");
            Method method=c.getMethod("work", String.class);
            //添加   .invoke
            method.invoke(c.newInstance(), "韩凯");
        }
    }

    5、通过反射,创建对象,调用指定的方法   重点

    获取成员方法,步骤如下:

    1. 获取Class对象

    2. 获取构造方法

    3. 通过构造方法,创建对象

    4. 获取指定的方法

    5. 执行找到的方法

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

    执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

    package com.oracle.Demo02;
    
    import java.lang.reflect.Method;
    
    public class Demo07 {
        public static void main(String[] args) throws Exception {
            //获取有参方法并运行
            Class c=Class.forName("com.oracle.Demo02.Person");
            Method method=c.getMethod("work", String.class);
            //添加   .invoke
            method.invoke(c.newInstance(), "韩凯");
        }
    }

     6、通过反射,创建对象,调用指定的private 方法

    获取私有成员方法,步骤如下:

    1. 获取Class对象

    2. 获取构造方法

    3. 通过构造方法,创建对象

    4. 获取指定的方法

    5. 开启暴力访问

    6. 执行找到的方法

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

           执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

    7、擦除泛型,添加元素

    将已存在的ArrayList<Integer>集合中添加一个字符串数据,如何实现呢?

    我来告诉大家,其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素

    package com.oracle.Demo02;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    
    public class Demo08 {
        //有一个ArrayList<String>  list
        //然后往里面添加int类型数据
        public static void main(String[] args) throws Exception {
            ArrayList<String> arr=new ArrayList<String>();
            arr.add("aaa");
            //获取集合的字节码对象
            Class c=arr.getClass();
            //用反射获取 add的方法,Object.calss无关泛型
            Method addd=c.getMethod("add", Object.class);
            //添加int类型的元素
            addd.invoke(arr, 1);
            for(Object obj:arr){
                System.out.println(obj);
            }
    
        }
    
    }

    8、反射配置文件:

    package com.oracle.Demo03;
    
    import java.io.FileReader;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    public class Demo {
    //反射配置文件
        public static void main(String[] args) throws IOException, Exception {
            //类不清楚,方法也不清楚
            //通过配置文件实现,运行的类名和方法名以键值对的形式
            //保存到properrties中,具体运行哪个类里面的方法通过该配置文件去设置
            //步骤:
            //1.准备配置文件,写好键值对
            //2.IO读取配置文件Reader
            //3.文件中的键值对存储到集合中,集合中保存的键值对就是类和方法名
            //4.反射获取指定类的class文件对象
            //5.class文件对象获取指定方法
            //6.运行方法
    //        Person p=new Person();
    //        p.eat();
            FileReader fr=new FileReader("config.properties");//读取配置好的配置文件
            Properties pro=new Properties();   //创建个集合,用来存键值对的
            pro.load(fr); //将文件中的键值对存到集合中
            fr.close();   //记得关闭
            String className=pro.getProperty("className");  //获得类名和方法名
            String methodName=pro.getProperty("methodName");
            Class c=Class.forName(className);   //反射获取字节码文件对象
            Object obj=c.newInstance();    //创建类对象
            Method method=c.getMethod(methodName);  //利用反射获得方法
            method.invoke(obj);    //执行方法
        }
    
    }

    自定义Person类:

    package com.oracle.Demo03;
    
    public class Person {
        public void eat(){
            System.out.println("人在吃饭");
        }
    }

    自定义Student类:

    package com.oracle.Demo03;
    
    public class Student {
        public void study(){
            System.out.println("学生在学习");
        }
    }

    自定义Worker类:

    package com.oracle.Demo03;
    
    public class Worker {
        public void work(){
            System.out.println("工人在工作");
        }
    }

    练习题:

    自定义类:

    Person类

    package com.oracle.Demo04;
    
    public class Person {
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
        public Person(){
            
        }
        public Person(String name,int age){
            this.name=name;
            this.age=age;
        }
    }

    Two类

    package com.oracle.Demo04;
    
    public class Two {
        private String name;
        public Two(){
            System.out.println(name);
        }
        public Two(String name) {
            super();
            this.name = name;
            System.out.println(name);
        }
        public void Work(){
            System.out.println("键盘敲烂,月薪过万");
        }
        @Override
        public String toString() {
            return "Two [name=" + name + "]";
        }
        
    }

    DamoClass自定义类:

    package com.oracle.Demo04;
    
    public class DemoClass {
        public void run(){
            System.out.println("welcome to oracle");
        }
    }

    1、ArrayList<Integer> list = new ArrayList<Integer>(); //这个泛型为Integer的ArrayList中存放一个String类型的对象

    //1.ArrayList<Integer> list = new ArrayList<Integer>(); 
    //这个泛型为Integer的ArrayList中存放一个String类型的对象
    public class Demo01 {
    
        public static void main(String[] args) throws Exception{
            ArrayList<Integer> list=new ArrayList<Integer>();
            list.add(666);
            //获取集合的字节码对象
            Class c=list.getClass();
            //获取集合类中的添加元素方法
            Method addd=c.getDeclaredMethod("add", Object.class);
            //调用方法,并添加元素
            addd.invoke(list, "abc");
            for(Object a:list){
                System.out.println(a);
            }
        }
    
    }

    2、用反射去创建一个对象,有2种方式,尽量用代码去体现

    package com.oracle.Demo04;
    
    import java.lang.reflect.Constructor;
    
    public class Demo02 {
    //2.用反射去创建一个对象,有2种方式,尽量用代码去体现
        public static void main(String[] args) throws Exception {
            //创建一个关于Two自定义类的字节码对象  要有完整的包名
            Class c=Class.forName("com.oracle.Demo04.Two");
            //调用.newInstance();空参构造方法 
            Object obj=c.newInstance();
            System.out.println(obj);
            //调用有参构造方法     Constructor构造器类
            Constructor con=c.getConstructor(String.class);
            Object obj01=con.newInstance("王五");
            System.out.println(obj01);
        }
    
    }

    3、利用反射创建一个类对象,获取指定的方法并执行

    package com.oracle.Demo04;
    
    import java.lang.reflect.Method;
    
    public class Demo03 {
    
        public static void main(String[] args) throws Exception {
            // 创建一个字节码对象
            Class c=Class.forName("com.oracle.Demo04.Two");
            //利用反射创建一个类对象
            Object obj=c.newInstance();
            //通过反射获得方法名为“work”的方法
            Method m=c.getMethod("Work");
            //调用obj类中的m方法
            m.invoke(obj);
        }
    
    }

    4、定义一个标准的JavaBean,名叫Person,包含属性name、age。 使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置, 不使用setAge方法直接使用反射方式对age赋值。

    package com.oracle.Demo04;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class Demo4 {
    //    定义一个标准的JavaBean,名叫Person,包含属性name、age。
    //    使用反射的方式创建一个实例、调用构造函数初始化name、age,使用反射方式调用setName方法对名称进行设置,
    //    不使用setAge方法直接使用反射方式对age赋值。
        public static void main(String[] args) throws Exception {
            //创建字节码对象
            Class c=Class.forName("com.oracle.Demo04.Person");
            //获取构造方法
            Constructor con=c.getConstructor(String.class,int.class);
            //使用构造方法   创建类对象
            Object obj=con.newInstance("张三",16);
            Method m=c.getMethod("setName", String.class);
            m.invoke(obj, "李四");
            //通过反射与成员变量名称,获得成员变量对象
            Field ageField=c.getDeclaredField("age");
            //因为是私有化的成员变量,所以需要暴力访问
            ageField.setAccessible(true);
            //给年龄赋值
            ageField.set(obj, 60);
            System.out.println(obj);
        }
    
    }

    5、从文件中通过反射读取键值对

    package com.oracle.Demo04;
    
    import java.io.FileReader;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    public class Demo05 {
    
        public static void main(String[] args) throws Exception {
            //从文件中读取键值对
            FileReader fr=new FileReader("democlass.properties");
            //创建一个集合存放键值对
            Properties pro=new Properties();
            pro.load(fr);
            fr.close();
            //通过键获取值的字符串
            String name=pro.getProperty("name");
            String method=pro.getProperty("method");
            //创建字节码对象
            Class c=Class.forName(name);
            //创建类对象
            Object obj=c.newInstance();
            //获得方法
            Method m=c.getMethod(method);
            //调用方法
            m.invoke(obj);
    
        }
    
    }
  • 相关阅读:
    Spring Boot 2.x基础教程:配置元数据的应用
    目前用下来最溜的MacOS微信多开工具!
    在IDEA中通过Module管理多个项目
    JAR冲突问题的解决以及运行状态下如何查看加载的类
    完美解决方案-雪花算法ID到前端之后精度丢失问题
    精讲响应式WebClient第5篇-请求超时设置与异常处理
    精讲响应式WebClient第4篇-文件上传与下载
    精讲响应式WebClient第3篇-POST、DELETE、PUT方法使用
    精讲响应式WebClient第2篇-GET请求阻塞与非阻塞调用方法详解
    精讲响应式webclient第1篇-响应式非阻塞IO与基础用法
  • 原文地址:https://www.cnblogs.com/0328dongbin/p/9294443.html
Copyright © 2020-2023  润新知