一、类加载器

1、类的加载:

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

 

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

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即可)

类加载器与反射 随笔 第1张
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张

 

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

 

类加载器与反射 随笔 第3张
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 + "]";
    }
    
    
}
类加载器与反射 随笔 第4张

 

 

 

获得构造方法并运行:

类加载器与反射 随笔 第5张
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("张三");
        
    }

}
类加载器与反射 随笔 第6张

 

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

类加载器与反射 随笔 第7张
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);
    }

}
类加载器与反射 随笔 第8张

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

类加载器与反射 随笔 第9张
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);
                
    }

}
类加载器与反射 随笔 第10张

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 对象表示的成员变量的值

 

类加载器与反射 随笔 第11张
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);
    }

}
类加载器与反射 随笔 第12张

 

 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() 获取本类中所有的方法(包含私有的)

类加载器与反射 随笔 第13张
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);
    }

}
类加载器与反射 随笔 第14张

获取有参方法并运行:

类加载器与反射 随笔 第15张
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(), "韩凯");
    }
}
类加载器与反射 随笔 第16张

 

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

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

1. 获取Class对象

2. 获取构造方法

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

4. 获取指定的方法

5. 执行找到的方法

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

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

类加载器与反射 随笔 第17张
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(), "韩凯");
    }
}
类加载器与反射 随笔 第18张

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

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

1. 获取Class对象

2. 获取构造方法

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

4. 获取指定的方法

5. 开启暴力访问

6. 执行找到的方法

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

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

7、擦除泛型,添加元素

 

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

 

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

 

类加载器与反射 随笔 第19张
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);
        }

    }

}
类加载器与反射 随笔 第20张

 

8、反射配置文件:

类加载器与反射 随笔 第21张
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);    //执行方法
    }

}
类加载器与反射 随笔 第22张

自定义Person类:

类加载器与反射 随笔 第23张
package com.oracle.Demo03;

public class Person {
    public void eat(){
        System.out.println("人在吃饭");
    }
}
类加载器与反射 随笔 第24张

 

自定义Student类:

类加载器与反射 随笔 第25张
package com.oracle.Demo03;

public class Student {
    public void study(){
        System.out.println("学生在学习");
    }
}
类加载器与反射 随笔 第26张

 

自定义Worker类:

类加载器与反射 随笔 第27张
package com.oracle.Demo03;

public class Worker {
    public void work(){
        System.out.println("工人在工作");
    }
}
类加载器与反射 随笔 第28张

 

练习题:

自定义类:

Person类

类加载器与反射 随笔 第29张
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;
    }
}
类加载器与反射 随笔 第30张

 

Two类

类加载器与反射 随笔 第31张
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 + "]";
    }
    
}
类加载器与反射 随笔 第32张

DamoClass自定义类:

类加载器与反射 随笔 第33张
package com.oracle.Demo04;

public class DemoClass {
    public void run(){
        System.out.println("welcome to oracle");
    }
}
类加载器与反射 随笔 第34张

 

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

类加载器与反射 随笔 第35张
//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);
        }
    }

}
类加载器与反射 随笔 第36张

 

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

类加载器与反射 随笔 第37张
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);
    }

}
类加载器与反射 随笔 第38张

 

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

类加载器与反射 随笔 第39张
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);
    }

}
类加载器与反射 随笔 第40张

 

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

类加载器与反射 随笔 第41张
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);
    }

}
类加载器与反射 随笔 第42张

 

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

类加载器与反射 随笔 第43张
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);

    }

}
类加载器与反射 随笔 第44张
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄