反射

类的加载与ClassLoader

Java内存分析

  • Java内存
      • 存放基本变量类型(会包含这个基本类型的具体数值)
      • 引用对象的变量(会存放这个引用在堆里面的具体地址)
      • 存放new的对象和数组
      • 可以被所有的线程共享,不会存放别的对象引用
    • 方法区(特殊的堆)
      • 可以被所有的线程共享
      • 包含了所有的class和static变量、方法

了解:类的加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

  1. 类的加载(Load)
    • 将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。
  2. 类的链接(Link)
    • 将类的二进制数据合并到 JRE 中。
  3. 类的初始化(Initialize)
    • JVM负责对类进行初始化

类的加载与ClassLoader的理解

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。
  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题。
    • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化:
    • 执行类构造器()方法的过程。类构造器()方法时由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器时构造类信息的,不是构造该类对象的构造器)。
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
    • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。

什么时候会发生类初始化?

  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化main方法所在的类
    • new一个类的对象
    • 调用类的静态成员(除了final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类引用,不会触发此类的初始化
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

类加载器的作用

  • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
  • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制(gc,garbage collection)可以回收这些Class对象。

类加载器作用是用来吧类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。

  1. 引导类加载器(Bootstrap Classloader):用C++编写的,时JVM自带的类加载器,负责Java平台核心库(如rt.jar包),用来装载核心类库。该加载器无法直接获取。

  2. 扩展类加载器(Extension Classloader):负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库。

  3. 系统类加载器(System Classloader):负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。

    自底向上检查类是否已装载,自顶向下尝试加载类

创建运行时类的对象

获取运行时类的完整结构

  • Field
  • Method
  • Constructor
  • Superclass
  • Interface
  • Annotation

调用运行时类的指定结构

invoke()

setAccessible(true) -- 禁用访问安全检查

利用反射实现的map和obj转换方法

/**
 * @Author: supjh22
 * @Date: 24/9/2021 下午 3:53
 * @Description: map和obj类型转换
 */
public class MapObjectSwitch {
    public static <T> T map2Obj(Map<String,Object> map, Class<T> clz) throws Exception{
        T obj = clz.newInstance();
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for(Field field:declaredFields){
            int mod = field.getModifiers();
            if(Modifier.isStatic(mod)||Modifier.isFinal(mod)){
                continue;
            }
            field.setAccessible(true);
            if(field.getType()==Long.class){
                field.set(obj,Long.valueOf(map.get(field.getName())+""));
                continue;
            }
            field.set(obj,map.get(field.getName()));
        }
        return obj;
    }

    public static Map<String,Object> obj2Map(Object obj) throws Exception{
        Map<String,Object> map=new HashMap<String, Object>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for(Field field:fields){
            field.setAccessible(true);
            map.put(field.getName(), field.get(obj));
        }
        return map;
    }

}

反射操作泛型

  • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。

  • 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

  • ParameterizedType:表示一种参数化类型,比如Collection

  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型

  • TypeVariable:是各种类型变量的公共父接口

  • WildcardType:代表一种通配符类型表达式

反射操作注解

练习:ORM

  • 了解什么是ORM?

    • Object relationship Mapping --> 对象关系映射
    class Student{
    	int id;
    	String name;
    	int age;
    }
    
    id name age
    001 SuperJH 22
    002 Kiko 2
    • 类和表结构对应
    • 属性和字段对应
    • 对象和记录对应
  • 要求:利用注解和反射完成类和表结构的映射关系

/**
 * @Author: supjh22
 * @Date: 25/9/2021 下午 2:52
 * @Description: 练习反射操作注解
 */
public class ReflectionTest {

    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("com.supjh.reflection.Student");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //获得注解的value值
        TableSuper tableSuper = (TableSuper)c1.getAnnotation(TableSuper.class);
        String value = tableSuper.value();
        System.out.println();

        //获得类的指定注解
        Field f = c1.getDeclaredField("name");
        FieldSuper fieldSuper = f.getAnnotation(FieldSuper.class);
        System.out.println(fieldSuper.colunmName());
        System.out.println(fieldSuper.type());
        System.out.println(fieldSuper.length());

    }
}
@TableSuper("db_student")
class Student{
    @FieldSuper(colunmName = "stu_id", type = "int", length = 10)
    private int id;
    @FieldSuper(colunmName = "stu_name", type = "varchar", length = 10)
    private String name;
    @FieldSuper(colunmName = "stu_age", type = "int", length = 10)
    private int age;

    public Student() {
    }

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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 "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableSuper{
    String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldSuper{
    String colunmName();
    String type();
    int length();
}