Java反射

目录

 

一,反射机制

1.Java Reflect

2.Java反射机制原理示意图

3.反射机制的功能

 4.反射优缺点

二,Class类

 1.基本介绍

 2.Class类的常用方法

3.获取Class类对象的方法

 4.有Class对象的所有类型

 三,类加载

1.基本说明

 2.类加载的时间

3.类加载流程图 

4.加载过程详解

 1)加载阶段 

2)连接阶段

3)初始化

四,通过反射获取类的结构信息

1.Class

2.Field

3.Method

​4.Constructor 

五,通过反射创建对象

六,通过反射访问类中的成员

1.访问属性

2.访问方法


 

一,反射机制

1.Java Reflect

  • 反射机制允许程序在执行期借助reflect api取得任何类的内部信息(成员变量,成员方法,构造器等),并能操作对象的属性及方法。反射是设计模式和框架底层的基础。
  • 加载完类后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。Class对象就像是一面镜子,形象称为:反射

2.Java反射机制原理示意图

Java程序在计算机中加载的三个阶段 

Java反射_第1张图片

3.反射机制的功能

  • 判断任意一个对象所属的类
  • 构造任意一个类的对象
  • 获取任意一个类所具有的变量和方法
  • 调用任意一个对象的成员变量和方法
  • 生成动态代理

 4.反射优缺点

  • 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,反射机制是框架技术的底层支撑。
  • 缺点:使用反射基本是解释执行,对执行速度有影响。
  • 反射调用优化—关闭访问检查

     

      Java反射_第2张图片

  1. Method和Filed,Constructor对象都有setAccessible()方法
  2. 该方法可启动和禁用访问安全检查的开关
  3. 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。

二,Class类

Java反射_第3张图片

 1.基本介绍

  • Class类也是一种类,因此也继承Object类
  • Class类对象不是new出来的,而是系统创建的
  • 对于某个类的Class类对象,类只加载一次,因此内存中只有一份
  • 每个类的实例都会记录自己时哪个Class实例生成的
  • 通过Class对象和API可以得到一个类的完整结构
  • Class对象存放在堆,类的字节码二进制文件存放在方法区。

 2.Class类的常用方法

Java反射_第4张图片

3.获取Class类对象的方法

package com.reflection;

/**
 * Date:2023/5/4  11:18
 * Description:获取类对象的方法
 *
 * @author Leon
 * @version 1.0
 */

public class GetClass_ {
    public static void main(String[] args) throws Exception {

        String classFullPath="com.reflection.Cat";
        //已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取
        Class cls1 = Class.forName(classFullPath);
        //多用于配置文件,读取类全路径,加载类

        //已知具体的类,通过类的class获取,该方法最为安全可靠,程序性能最高
        Class cls2 = Cat.class;
        //多用于参数传递,比如通过反射得到对应的构造器对象

        //已知某个类的实例,调用该实例的getClass()方法获取
        Object o = cls1.newInstance();
        Class cls3 = o.getClass();
        //用于通过创建好的对象获取对应Class类对象

        Cat cat = cls2.newInstance();
        ClassLoader classLoader = cat.getClass().getClassLoader();
        Class cls4 = classLoader.loadClass(classFullPath);
        //通过类加载器获得

        //基本数据类型按这种方法获得
        Class cls5 = int.class;

        //基本数据类型对应包装类按照这种方法获得
        Class cls6 = Integer.TYPE;


        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
    }
}

输出:  Java反射_第5张图片

 4.有Class对象的所有类型

public class AllTypeClass {
    public static void main(String[] args) {

        Class cls1 = String.class;//外部类
        Class cls2 = Serializable.class;//接口
        Class cls3 = long.class;//基本数据类型
        Class cls4 = Integer[].class;//数组
        Class cls5 = float[][].class;//二维数组
        Class cls6 = Deprecated.class;//注解
        Class cls7 = Thread.State.class;//枚举
        Class cls8 = void.class;//void类型
        Class cls9 = Class.class;//Class类

        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);

    }
}

输出:Java反射_第6张图片

 三,类加载

1.基本说明

反射机制是Java实现动态语言的关键,也就是利用反射实现类的动态加载

  • 静态加载:编译时加载所有类,如果未找到相关类,则报错,依赖性太强
  • 动态加载:运行时记载需要的类,如果运行时未使用该类,即使该类不存在,也不会报错,提高了灵活性

 2.类加载的时间

创建(new)对象时 //静态加载

子类被加载时父类也加载 //静态加载

调用类中的静态成员 //静态加载

通过反射创建 //动态加载

3.类加载流程图 

Java反射_第7张图片

4.加载过程详解

Java反射_第8张图片

1)加载阶段 

JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件,jar包,网络)转换为二进制字节流加载到内存中,并生成一个代表该类的Class对象

2)连接阶段

验证:

  • 目的:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全
  • 包括:文件格式验证(是否以魔数oxcafebabe开头),元数据验证,字节码验证和符号引用验证
  • 可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间

准备:

JVM会在该阶段对静态变量分配内存并默认初始化。这些变量所使用的内存将在方法区中进行分配

public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;

//1. n1 是实例属性, 不是静态变量,因此在准备阶段,不会分配内存
//2. n2 是静态变量,分配内存 n2 默认初始化为 0 ,而不是 20
//3. n3 是 static final 是常量, 他和静态变量不一样,,因为一旦赋值就不变
//                        在准备阶段直接赋值为30

解析:

虚拟机将常量池内的符号引用替换为直接引用,符号引用是一种抽象的引用方式,例如方法名、参数类型等,而直接引用则是具体的指向内存地址的引用。

3)初始化

  1. 到初始化阶段,开始真正执行类中定义的Java程序代码,此阶段是执行()方法的过程
  2. ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并
    clinit() {
    System.out.println("B 静态代码块被执行");
    //num = 300;
    num = 100;
    }
    合并: num =100;
  3. 虚拟机会保证一个类的()方法在多线程环境中被正确的加载,同步,如果多个线程同时初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待

四,通过反射获取类的结构信息

1.Class

Java反射_第9张图片

2.Field

Java反射_第10张图片

3.Method

Java反射_第11张图片

4.Constructor 

Java反射_第12张图片

五,通过反射创建对象

Java反射_第13张图片

public class get {
    public static void main(String[] args) throws Exception {
        //1. 先获取到 User 类的 Class 对象
        Class userClass = Class.forName("com.hspedu.reflection.User");
        
        //2. 通过 public 的无参构造器创建实例
        Object o = userClass.newInstance();
        System.out.println(o);
        
        //3. 通过 public 的有参构造器创建实例
        /*
        constructor 对象就是
        public User(String name) {
        this.name = name;
        }
        */
        //3.1 先得到对应构造器
        Constructor constructor = userClass.getConstructor(String.class);
        //3.2 创建实例,并传入实参
        Object us = constructor.newInstance("us");
        System.out.println("user=" + us);
        
        //4. 通过非 public 的有参构造器创建实例
        //4.1 得到 private 的构造器对象
        Constructor constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
        //4.2 创建实例
        //暴破【暴力破解】 , 使用反射可以访问 private 构造器/方法/属性, 反射面前,都是纸老虎
        constructor1.setAccessible(true);
        Object user2 = constructor1.newInstance(100, "张三丰");
        System.out.println("user2=" + user2);
    }
}

class User { //User 类
    private int age = 10;
    private String name = "yonghu";

    public User() {//无参 public
    }

    public User(String name) {//public 的有参构造器
        this.name = name;
    }

    private User(int age, String name) {//private 有参构造器
        this.age = age;
        this.name = name;
    }

    public String toString() {
        return "User [age=" + age + ", name=" + name + "]";
    }
}

六,通过反射访问类中的成员

1.访问属性

Java反射_第14张图片

public class get {
    public static void main(String[] args) throws Exception {
        
        //1. 得到 Student 类对应的 Class 对象
        Class stuClass = Class.forName("com.reflection.Student");
        //2. 创建对象
        Object o = stuClass.newInstance();//o 的运行类型就是 Student
        System.out.println(o.getClass());//Student

        //3. 使用反射得到 age 属性对象
        Field age = stuClass.getField("age");
        age.set(o, 88);//通过反射来操作属性
        System.out.println(o);
        System.out.println(age.get(o));//返回 age 属性的值
        
        //4. 使用反射操作 name 属性
        Field name = stuClass.getDeclaredField("name");
        //对 name 进行暴破, 可以操作 private 属性
        name.setAccessible(true);
        //name.set(o, "张三");
        name.set(null, "李四");//因为 name 是 static 属性,因此 o 也可以写出 null
        System.out.println(o);
        System.out.println(name.get(o)); //获取属性值
        System.out.println(name.get(null));//获取属性值, 要求 name 是 static
    }
}

class Student {//类
    public int age;
    private static String name;

    public Student() {//构造器
    }

    public String toString() {
        return "Student [age=" + age + ", name=" + name + "]";
    }
}

2.访问方法

Java反射_第15张图片

public class get {
    public static void main(String[] args) throws Exception {

        //1. 得到 Boss 类对应的 Class 对象
        Class bossCls = Class.forName("com.reflection.Boss");
        
        //2. 创建对象
        Object o = bossCls.newInstance();
        
        //3. 调用 public 的 hi 方法
        //Method hi = bossCls.getMethod("hi", String.class);//OK
        //3.1 得到 hi 方法对象
        Method hi = bossCls.getDeclaredMethod("hi", String.class);//OK
        //3.2 调用
        hi.invoke(o, "hello~");
        
        //4. 调用 private static 方法
        //4.1 得到 say 方法对象
        Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
        //4.2 因为 say 方法是 private, 所以需要暴破,原理和前面讲的构造器和属性一样
        say.setAccessible(true);
        System.out.println(say.invoke(o, 100, "张三", '男'));
        //4.3 因为 say 方法是 static 的,还可以这样调用 ,可以传入 null
        System.out.println(say.invoke(null, 200, "李四", '女'));
        
        //5. 在反射中,如果方法有返回值,统一返回 Object , 但是他运行类型和方法定义的返回类型一致
        Object reVal = say.invoke(null, 300, "王五", '男');
        System.out.println("reVal 的运行类型=" + reVal.getClass());//String

    }
}

class Boss {//类
    public int age;
    private static String name;

    public Boss() {//构造器
    }
    
    private static String say(int n, String s, char c) {//静态方法
        return n + " " + s + " " + c;
    }

    public void hi(String s) {//普通 public
        System.out.println("hi " + s);
    }
}

 

你可能感兴趣的:(Java,java,jvm,开发语言)