虚拟机类加载机制

概述:

如果需要使用某一个类型,虚拟机把描述类的数据从class文件中加载到运行内存,并 对数据进行校验,转换解析和初始化,最终形成可以被java虚拟机直接使用的类型, 这就是虚拟机的类加载机制。

反射机制 - 图1

加载机制的的过程

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

  • 加载:就是指将class文件中的信息读入内存,并为之创建一个Class对象
  • 注意:任何类被使用时系统都会建立一个Class对象
  • 连接:验证是字节码对象是否有正确的内部结构,检验是否符合官方制定的class 文件规范,以及检查字节码对象中的数据是否会对虚拟机造成危害
  • 初始化:负责为类的静态成员分配内存,并设置默认初始化值

类加载机制

  • 创建类的实例 创建对象时
  • 类的静态成员使用 类名.访问静态方法或者属性时
  • 使用反射方式来访问类型时
  • 初始化某个类的子类 Stu extends Person new Stu();
  • 直接使用java.exe命令来运行某个主类时

类加载器

概述:类加载器是负责加载类的对象。

将class文件中的数据加载到运行内存中,并为之生成对应的字节码对象。

分类

  • Bootstrap ClassLoader 引导类加载器也被称为根类加载器,负责Java核心类的加载。
  • Extension ClassLoader 扩展类加载器
  • Application ClassLoader 系统类加载器
    负责在JVM启动时加载来自java命令的class文件

类加载器之间的继承关系

-Bootstrap ClassLoader

  1. -Extension ClassLoader
  2. -Application ClassLoader

类加载器加载类型的双亲委派机制

概述:双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器,父类加载器再将该请求委派给父类的父类加载器,如果该在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载

举例:String类型需要加载,只能将加载请求给Application加载器加载,该加载器并不会直接加载,直接将加载请求给父类加载器加载,Extension加载器收到子类的加载请求之后,该加载器将该请求委派给他的父类加载Bootstrap 加载器收到类的加载请求之后,该类加载器在收到请求之后会先检查这个类型它能否加载,如果可以加载,就直接家藏完成。

举例:如果需要加载Person类型,将加载请求给Application加载,直接将该请求给父类加载器Extension 加载器加载,Extension 收到子类的请求之后,一样将请求委派给父类Bootstrap 加载,当Bootstrap 加载器收到请求之后,先检查该类能否加载,Person类型Bootstrap 加载器无法进行加载,只能将请求返回Extension 去加载,Extension 加载器也无法加载给类型,就只能将请求再返回给子类Application ,Application 加载器收到父类返回的请求后,发现该类型可以被加载,所以就加载了该类型。

反射

public static void main(String[] args) throws ClassNotFoundException {
    //获取Person类的字节码对象

    //使用类的静态属性class
    Class<?> c = Person.class;
    //使用对象的方法
    Class<?> c1 = new Person().getClass();
    //使用Class静态类的方法 : forname()
    Class<?> c2 = Class.forName("note.method_.Person");
    //虽然参数是一个字符串
    // 但是底层会根据字符串去查找有没有相同的类名--注意字符串应该是全类名
    // 如果有相同类名就直接加载了,没有就不加载并抛出异常
    //  特点:虽然第三种不是最简单的,但是扩展性是最强的
}

反射获取字节码对象中的构造方法并实例化

概述:根据类的字节码对象获取该类的构造方法,并创造该类对象

获取构造方法的方式

  • getConstructors():返回所有公共的构造方法对象
  • getDeclaredConstructors():返回所有构造方法对象
  • getConstructor():返回空参构造对象
  • getConstructor(Class<?>… parameterTypes):返回单个指定参数的公共有参构造方法对象
  • getDeclaredConstructor(Class<?>…parameterTypes):返回单个指定参数的有参构造方法对象

通过构造方法对象,创建类的实例

  • newInstance(Object…initargs)
  • 如果是空参构造,就不需要给出实参
  • 如果是有参构造对象,就需要给出对应的实际参数

代码

Person类

public class Person {
    private String name;
    private int age;
    public String hobby;

    public Person() {
    }

    public Person(String name, int age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }

    private Person(String name, int age){
        this.name = name;
        this.age = 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 +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //获取字节码对象
    Class<?> c = Class.forName("note.constructor_.Person");


    System.out.println("1、-------------------------------------");
    //---c.getConstructors()、返回所有的构造方法,包括父类中的
    for (Constructor<?> cons : c.getConstructors()) {
        System.out.println(cons);
    }

    System.out.println("2、-------------------------------------");
    //---c.getDeclaredConstructors()、返回所有的本类中的构造方法,包括私有的
    for (Constructor<?> dcons : c.getDeclaredConstructors()) {
        System.out.println(dcons);
    }

    System.out.println("3、-------------------------------------");
    //返回无参构造: getConstructor()
    Constructor<?> con1 = c.getConstructor();
    System.out.println(con1);

    System.out.println("4、-------------------------------------");
    //返回有参构造: c.getConstructor\c.getDeclaredConstructor
    Constructor<?> con2 = c.getConstructor(String.class, int.class, String.class);
    System.out.println(con2);
    Constructor<?> con3 = c.getDeclaredConstructor(String.class, int.class);
    System.out.println(con3);

    System.out.println("5、-------------------------------------");
    //通过构造方法,创建类的实例----通过无参构造创建的实例没有参数
    Object o = con1.newInstance();
    System.out.println(o);

    System.out.println("6、-------------------------------------");
    //通过构造方法,创建类的实例-----通过有参构造创建的必须和对应构造参数一直
    Object o1 = con2.newInstance("张三", 23, "抽烟");
    System.out.println(o1);
    //私有构造可以返回,但是不能给其赋值构造--所以下面运行报错:IllegalAccessException异常
    Object o2 = con3.newInstance("李四", 24);
    System.out.println(o2);

}
/*
1、-------------------------------------
public note.constructor_.Person(java.lang.String,int,java.lang.String)
public note.constructor_.Person()
2、-------------------------------------
private note.constructor_.Person(java.lang.String,int)
public note.constructor_.Person(java.lang.String,int,java.lang.String)
public note.constructor_.Person()
3、-------------------------------------
public note.constructor_.Person()
4、-------------------------------------
public note.constructor_.Person(java.lang.String,int,java.lang.String)
private note.constructor_.Person(java.lang.String,int)
5、-------------------------------------
Person{name='null', age=0, hobby='null'}
6、-------------------------------------
Person{name='张三', age=23, hobby='抽烟'}
Exception in thread "main" java.lang.IllegalAccessException: Class note.constructor_.Constructor_test can not access a member of class note.constructor_.Person with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:413)
    at note.constructor_.Constructor_test.main(Constructor_test.java:46)

Process finished with exit code 1
*/

反射获取成员变量并实例化

获取方法:

  • getFields():返回所有公共成员变量对象
  • getDeclaredFields():返回所有成员变量对象
  • getField(String name):返回指定的公共成员变量对象
  • getDeclaredField(String name):返回单个成员变量对象

访问成员属性的方法

set(Object obj,Object value): 用于给obj对象中的该成员变量赋value值

get(Object obj):用于获取obj对象的指定成员变量值

暴力反射

概述:如果类型中的某些属性是私有化的,那么就不能直接使用方法访问该属性所以只能使用暴力反射来强制访问。

相关方法

  • isAccessible():判断当前属性对象是否需要参与虚拟机的检测
  • setAccessible(boolean flag):将当前对象设置为不需要被虚拟机检测

代码

Person类

public class Person {
    private String name;
    private int age;
    public String hobby;

    public Person() {
    }

    public Person(String name, int age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }

    private Person(String name, int age){
        this.name = name;
        this.age = 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 +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    //获取字节码对象
    Class<?> c = Class.forName("note.field_.Person");

    System.out.println("1、-------------------------------------");
    //获取全部属性--c.getFields()
    for (Field f : c.getFields()) {
        System.out.println(f);
    }

    System.out.println("2、-------------------------------------");
    //获取全部属性:包括私有---c.getDeclaredFields()
    for (Field f : c.getDeclaredFields()) {
        System.out.println(f);
    }

    System.out.println("3、-------------------------------------");
    //获取单个属性
    Field hobby = c.getField("hobby");
    System.out.println(hobby);

    System.out.println("4、-------------------------------------");
    //获取单个属性--私有
    Field name = c.getDeclaredField("name");
    System.out.println(name);

    System.out.println("5、-------------------------------------");
    Constructor<?> con = c.getConstructor(String.class,int.class,String.class);
    Object o = con.newInstance("张三", 23, "抽烟");
    System.out.println(hobby.get(o));
    hobby.set(o,"喝酒");     //将hobby改成 喝酒
    System.out.println(hobby.get(o));

    System.out.println("6、-------------------------------------");
    //.isAccessible() 是用来判断属性被使用前,有没有被虚拟机检测的;
    // 返回值都为false说明都被检测了,只不过共有和私有的属性被分开,私有不能使用
    System.out.println(name.isAccessible());
    System.out.println(hobby.isAccessible());

    System.out.println("7、-------------------------------------");
    //  System.out.println(name.get(o));       -------私有的属性不可以赋值【除非暴力反射】
    name.setAccessible(true);         //让name属性被使用前不用检测
    System.out.println(name.get(o));
    name.set(o,"李四");               //暴力反射后--值可以被修改
    System.out.println(name.get(o));
}
/*
1、-------------------------------------
public java.lang.String note.field_.Person.hobby
2、-------------------------------------
private java.lang.String note.field_.Person.name
private int note.field_.Person.age
public java.lang.String note.field_.Person.hobby
3、-------------------------------------
public java.lang.String note.field_.Person.hobby
4、-------------------------------------
private java.lang.String note.field_.Person.name
5、-------------------------------------
抽烟
喝酒
6、-------------------------------------
false
false
7、-------------------------------------
张三
李四
*/

反射获取成员方法并使用

方法

  • getMethods():返回所有公共成员方法对象
  • getDeclaredMethods():返回所有成员方法对象
  • getMethod(String methodName, Class<?>…parameterTypes):返回指定的公共成员方法对象
  • getDeclaredMethod(String methodName, Class<?>…parameterTypes):返回指定的成员方法对象

执行方法的方式

invoke(Object o,Class…paramsTypre)

  • 如果方法是已经非静态方法,需要传递一个对象执行
  • 如果方法是一个静态方法,不需要传入对象,直接传入Null

代码

Person类

public class Person {
    private String name;
    private int age;
    public String hobby;
    public Person() {
    }
    public Person(String name, int age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }
    private Person(String name, int age){
        this.name = name;
        this.age = 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 +
                ", hobby='" + hobby + '\'' +
                '}';
    }
    public void show(){
        System.out.println("Person类中的公共方法");
    }
    public void show(String s ){
        System.out.println("Person类中的带参方法:"+s);
    }
    private void private_(){
        System.out.println("Person类中的私有方法");
    }
    public int getNum(){
        System.out.println("Person类中的带返回值的方法");
        return 100;
    }
    public static void static_(){
        System.out.println("Person类中的静态方法");
    }
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
    //获取字节码对象
    Class<?> c = Class.forName("note.method_.Person");
    //通过无参构造con 来实例对象o
    Constructor<?> con = c.getConstructor();
    Object o = con.newInstance();

    System.out.println("1、-------------------------------------");
    //除私有方法外获取所有方法--包括父类中继承的
    for (Method mm : c.getMethods()) {
        System.out.println(mm);
    }

    System.out.println("2、-------------------------------------");
    //获取 Person类中的所有方法,包括私有方法,继承的没有重写的除外
    for (Method mm : c.getDeclaredMethods()) {
        System.out.println(mm);
    }

    System.out.println("3、-------------------------------------");
    //获取无参方法
    Method m = c.getMethod("show");
    m.invoke(o);

    System.out.println("4、-------------------------------------");
    //获取私有方法---获取前先暴力反射
    Method m1 = c.getDeclaredMethod("private_");
    m1.setAccessible(true);
    m1.invoke(o);

    System.out.println("4、-------------------------------------");
    //获取有参方法
    Method m2 = c.getMethod("show", String.class);
    m2.invoke(o,"张三");

    System.out.println("5、-------------------------------------");
    //获取带有返回值的方法
    Method m3 = c.getMethod("getNum");
    m3.invoke(o);

    System.out.println("6、-------------------------------------");
    //获取静态方法
    Method m4 = c.getMethod("static_");
    m4.invoke(o);
    m4.invoke(null);         //----静态方法可以用类名直接调用,在反射中可以不传递对象,用null代替
}
/*
1、-------------------------------------
public java.lang.String note.method_.Person.toString()
public java.lang.String note.method_.Person.getName()
public void note.method_.Person.setName(java.lang.String)
public static void note.method_.Person.static_()
public int note.method_.Person.getNum()
public void note.method_.Person.show()
public void note.method_.Person.show(java.lang.String)
public void note.method_.Person.setAge(int)
public int note.method_.Person.getAge()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
2、-------------------------------------
public java.lang.String note.method_.Person.toString()
public java.lang.String note.method_.Person.getName()
public void note.method_.Person.setName(java.lang.String)
public static void note.method_.Person.static_()
public int note.method_.Person.getNum()
private void note.method_.Person.private_()
public void note.method_.Person.show()
public void note.method_.Person.show(java.lang.String)
public void note.method_.Person.setAge(int)
public int note.method_.Person.getAge()
3、-------------------------------------
Person类中的公共方法
4、-------------------------------------
Person类中的私有方法
4、-------------------------------------
Person类中的带参方法:张三
5、-------------------------------------
Person类中的带返回值的方法
6、-------------------------------------
Person类中的静态方法
Person类中的静态方法
*/

案例

需求:

定义一个List集合(泛型定义为Integer),如:ArrayList list = new ArrayList<>();

要求利用反射的原理,在集合中添加若干字符串并且不报错,最终输出集合中的内容

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(888);
    list.add(118);
    list.add(998);
    //转字节码
    Class<?> c =list.getClass();
    //获取无参构造con
    Constructor<?> con = c.getConstructor();

    //获取add方法
    //--泛型擦除:当某个类型的方法定义的是泛型的时候,在使用反射调用该方法时,参数的类型是Object类型,而不能是泛型
    Method add = c.getMethod("add",Object.class);
    add.invoke(list,"aaa");
    add.invoke(list,"bbb");

    System.out.println(list);

}
//[888, 118, 998, aaa, bbb]

1、自定义一个Stu类型

属性:String name; int age (私有化,定义公共的访问方式)

方法:toString(展示当前属性的信息)show( )

2、手动将这个类型的全类名写在文件中的第一行

3、读取文件中的类名,并创建一个该类对象(通过有参构造创建)

4、使用toString\show()方法展示对象属性

5、使用公共的访问方法给属性改值,并再次调用show方法展示属性值

Stu类

public class Stu {
    private String name;
    private int age;
    public Stu(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public Stu() {
    }
    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 "Stu{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    private void show(){
        System.out.println(name+age);
    }
}
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //读取 a.txt 中的第一行数据--全类名
        BufferedReader br = new BufferedReader(new FileReader("22day/src/exercise02/a.txt"));
        String s = br.readLine();
        //获取字节码对象,有参实例化
        Class<?> c = Class.forName(s);
        Constructor<?> con = c.getConstructor(String.class, int.class);
        Object o = con.newInstance("张三", 23);
        //通过show方法打印,因为show方法为私有,所以需要暴力反射
        Method show = c.getDeclaredMethod("show");
        show.setAccessible(true);
        show.invoke(o);
        //通过toString方法打印
        Method toString = c.getMethod("toString");
        System.out.println(toString.invoke(o));
        //更改属性内容--属性为私有所以需要暴力反射
        Field name = c.getDeclaredField("name");
        Field age = c.getDeclaredField("age");
        name.setAccessible(true);
        age.setAccessible(true);
        name.set(o,"李四");
        age.set(o,99);
        //输出更改后的内容
        System.out.println(toString.invoke(o));
        show.invoke(o);
    }
}
/* 
张三23
Stu{name='张三', age=23}
Stu{name='李四', age=99}
李四99
*/
 show = c.getDeclaredMethod("show");
        show.setAccessible(true);
        show.invoke(o);
        //通过toString方法打印
        Method toString = c.getMethod("toString");
        System.out.println(toString.invoke(o));
        //更改属性内容--属性为私有所以需要暴力反射
        Field name = c.getDeclaredField("name");
        Field age = c.getDeclaredField("age");
        name.setAccessible(true);
        age.setAccessible(true);
        name.set(o,"李四");
        age.set(o,99);
        //输出更改后的内容
        System.out.println(toString.invoke(o));
        show.invoke(o);
    }
}
/* 
张三23
Stu{name='张三', age=23}
Stu{name='李四', age=99}
李四99
*/