天天看点

Java反射开启暴力反射及泛型擦除,以及底层原理和性能分析、优化

作者:CodingSir

一、类加载器

类加载器:用于将字节码文件加载到内存中
Java反射开启暴力反射及泛型擦除,以及底层原理和性能分析、优化

二、类加载过程

当我们的程序中要使用类的时候,如果类还没有被加载到内存中,那么类加载器会对该类进行加载、链接、初始化这三步加载进内存中
Java反射开启暴力反射及泛型擦除,以及底层原理和性能分析、优化

1、加载

  • 通过类的包名+类名获取该类的二进制字节流,准备用流进行传输
  • 将该类加载到运行时数据区
  • 在内存中创建一个Class对象,该对象作为这个类的各种数据的访问入口
Java反射开启暴力反射及泛型擦除,以及底层原理和性能分析、优化

2、链接

将类的二进制数据合并到JRE中
  • 验证:确保加载信息符合JVM规范,保证虚拟机安全
  • 准备:为类的静态变量在方法区分配内存,并设置类变量默认初始值
  • 解析:将虚拟机常量池内的符号引用替换为直接引用

3、初始化

初始化就需要由虚拟机控制,到了初始化阶段才真正执行java代码,类初始化的主要工作是为静态变量赋程序设定的初始值,也就是执行类构造器方法的过程

三、类的主动引用

  • 实例化对象
  • 调用静态变量,或者是给静态变量进行赋值
  • 调用静态方法
  • 运行器运行
  • 通过反射机制强制性的对类或接口的实现类进行实例化
  • 实例化子类对象

四、类加载器的分类

类加载器分类主要分为两类:

  1. JVM内置的类加载器:分别加载不同目录下的class文件 Bootstrap ClassLoader启动类/引导类/根类加载器:负责加载Java核心类,比如:String、Math、System等等。被加载的类都保存在/jre/lib/rt.jar中Extension ClassLoader扩展类加载器:负责jre的扩展目录中jar包的加载,被加载的类都保存在/jre/lib/ext目录下App ClassLoader应用类/系统类加载器:负责在jvm启动的时候加载来自java命令的class文件,以及classpath变量所指定的jar包和类路径,第三方jar包也会被加载
  2. 用户自定义的类加载器:负责的加载目录自己决定

五、双亲委派模式

双亲委派模式:如果一个类加载器收到了类加载请求,它不会先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,请求最终到达最顶层的启动类加载器,如果父类加载器可以完成加载任务,就成功返回,否则子加载器才会尝试自己加载

六、反射

1、反射概述

反射:在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法,这种动态获取程序信息以及动态调用对象的功能称之为Java语言的反射机制。 被private封装的资源只能类内部访问,外部不行,但反射能直接操作类私有属性,反射可以在运行时获取一个类的所有信息(成员变量、成员方法、构造方法等),并且可以操纵类的属性、方法、构造器等
想要通过反射机制获取操纵一个类的所有资源,需要利用类加载器加载class字节码文件时创建的对应的Class对象。

2、获取Class对象

获取Class对象三种方式:

  1. 类名.class
  2. 对象名.getClass()
  3. Class.forName(包名+类名)
Java反射开启暴力反射及泛型擦除,以及底层原理和性能分析、优化

2.1、Class.forName()

代码示例

public static void forNameHandler() throws Exception{

  // 1、通过Class对象中的静态方法获取User对应的Class对象
  Class clazz = Class.forName("cn.com.codingsir.reflect.User");
  
}
           

2.2、类名.class

代码示例

public static void classHandler(){

  // 1、通过类名获取class对象
  Class clazz = User.class;
  
}
           

2.3、对象.getClass()

代码示例

public static void getClassHandler(){
  
  // 1、实例化User对象
  User user = new User();
  
  // 2、获取Class对象
  Class clazz = user.getClass();
}
           

3、获取构造器

3.1、获取所有公共构造器

User.java

代码示例

package cn.com.example15;

public class User {

    public int id;
    public String userName;
    public String password;
    private String role;

    public User(){}

    public User(int id,String userName,String password,String role){
        this.id = id;
        this.userName = userName;
        this.password = password;
        this.role = role;
    }


    private User(String role){
        this.role = role;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }
    
    private void accessableHandler(){

        System.out.println("这是一个私有成员方法");
        
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", role='" + role + '\'' +
                '}';
    }
}


           

代码示例

/**
 * 获取所有构造器
 */
public static void findConstructors(){

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有的构造器不包含私有
    Constructor[] constructors = clazz.getConstructors();

    // 3、遍历
    for (Constructor constructor : constructors) {
        // public cn.com.example15.User()
        // public cn.com.example15.User(int,java.lang.String,java.lang.String)
        System.out.println(constructor);
    }

}
           

3.2、获取无参构造器

代码示例

/**
 * 获取无参构造器
 */
public static void findConstructorsNoParameter() throws Exception {

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有的构造器不包含私有
    Constructor constructor = clazz.getConstructor();

    // 3、运行构造器
    User user = (User)constructor.newInstance();

    System.out.println(user); // User{id=0, userName='null', password='null'}

}
           

3.3、获取有参构造器

代码示例

/**
 * 获取有参构造器
 */
public static void findConstructorParameter() throws Exception {

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有的构造器不包含私有
    // 必须指定参数类型对应的class,如果参数是int,那么必须是int.class
    Constructor constructor = clazz.getConstructor(int.class,String.class,String.class,String.class);

    // 3、运行构造器
    User user = (User)constructor.newInstance(1001,"admin","admin","user");

    System.out.println(user); // User{id=1001, userName='admin', password='admin', role='user'}

}
           

4、暴力反射

反射里的Constructor、Field、Method三个类都有一个getDeclaredXxx方法,可以不受限制的获取私有构造器、属性、方法调用。 通过该方法获取私有成员,会自动的访问类的isAccessable,默认是false,如果想获取,那么调用setAccessable设置为true,就可以对类中的私有成员操作了。

代码示例

/**
 * 获取所有构造器包含私有
 */
public static void findConstructors(){

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有的构造器
    Constructor[] constructors = clazz.getDeclaredConstructors();

    // 3、遍历
    for (Constructor constructor : constructors) {
        // private cn.com.example15.User(java.lang.String)
        // public cn.com.example15.User(int,java.lang.String,java.lang.String,java.lang.String)
        // public cn.com.example15.User()
        System.out.println(constructor);
    }

}


/**
 * 获取指定私有构造器
 */
public static void findConstructorParameter()throws Exception{

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有的构造器不包含私有
    Constructor constructor = clazz.getDeclaredConstructor(String.class);

    // 设置为true
    constructor.setAccessible(true);

    // 3、实例化对象
    System.out.println(constructor.newInstance("admin")); // User{id=0, userName='null', password='null', role='admin'}

}

           

5、实例化对象

在不手动调用构造器的前提下,利用反射机制直接实例化对象

代码示例

public static void newInstanceHandler() throws IllegalAccessException, InstantiationException {

      Class clazz = User.class;
      // 实例化对象,通过该方式,前提构造器访问修饰符必须是公共的
      User user = (User) clazz.newInstance();

      System.out.println(user); // User{id=0, userName='null', password='null', role='null'}

  }
           

6、成员变量

6.1、获取所有公共属性

代码示例

/**
 * 获取所有公共属性
 */
public static void findFields(){

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有公共的属性
    Field[] fields = clazz.getFields();

    for (Field field : fields) {
        /**
         * public int cn.com.example15.User.id
         * public java.lang.String cn.com.example15.User.userName
         * public java.lang.String cn.com.example15.User.password
         */
        System.out.println(field);
    }
}
           

6.2、获取所有属性

代码示例

/**
 * 获取所有属性
 */
public static void findFieldsAccessable(){

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有属性
    Field[] fields = clazz.getDeclaredFields();

    for (Field field : fields) {
        /**
         * public int cn.com.example15.User.id
         * public java.lang.String cn.com.example15.User.userName
         * public java.lang.String cn.com.example15.User.password
         * private java.lang.String cn.com.example15.User.role
         */
        System.out.println(field);
    }
}
           

6.3、获取指定属性

代码示例

/**
 * 获取指定属性
 */
public static void findField() throws Exception{

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取userName属性
    Field userName = clazz.getField("userName");

    // 3、实例化对象
    User user = (User)clazz.newInstance();

    // 4、设置属性值
    userName.set(user,"admin");

    System.out.println(user); // User{id=0, userName='admin', password='null', role='null'}

}
           

7、成员方法

7.1、获取所有公共成员方法

/**
 * 获取所有公共成员方法
 */
public static void findMethods(){

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有公共成员方法
    Method[] methods = clazz.getMethods();

    for (Method method : methods) {
        /**
         * public java.lang.String cn.com.example15.User.toString()
         * public void cn.com.example15.User.setRole(java.lang.String)
         * public java.lang.String cn.com.example15.User.getRole()
         * 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()
         */
        System.out.println(method);
    }

}
           

7.2、获取所有成员方法

/**
 * 获取所有成员方法
 */
public static void findMethodsAccessable(){

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有成员方法
    Method[] methods = clazz.getDeclaredMethods();

    for (Method method : methods) {
        /**
         * public java.lang.String cn.com.example15.User.toString()
         * public void cn.com.example15.User.setRole(java.lang.String)
         * public java.lang.String cn.com.example15.User.getRole()
         * private void cn.com.example15.User.accessableHandler()
         */
        System.out.println(method);
    }

}
           

7.3、获取无参成员方法

/**
 * 获取指定的无参成员方法
 */
public static void findMethodNoParameter()throws Exception{

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有公共成员方法
    Method method = clazz.getMethod("getRole");

    // 3、实例化对象
    User user = (User)clazz.newInstance();

    // 4、调用方法
    Object role = method.invoke(user);

    System.out.println(role);
}
           

7.4、获取有参成员方法

/**
 * 获取指定的有参成员方法
 */
public static void findMethod()throws Exception{

    // 1、获取Class对象
    Class clazz = User.class;

    // 2、获取所有公共成员方法
    Method method = clazz.getMethod("setRole",String.class);

    // 3、实例化对象
    User user = (User)clazz.newInstance();

    // 4、调用方法
    method.invoke(user,"admin");

}
           

8、泛型擦除

代码示例

/**
 * 泛型擦除
 * 定义一个String泛型类型的List集合,通过反射实现添加Integer类型的数据到集合中
 */
public static void method() throws Exception {

    // 1、创建List集合,指定泛型类型为String
    List<String> list = new ArrayList<>();

    // 2、添加数据
    list.add("admin");
    list.add("user");
    list.add("guest");

    // 3、获取List集合的Class对象
    Class clazz = list.getClass();

    // 4、获取方法
    Method add = clazz.getMethod("add",Object.class);

    // 5、运行方法
    add.invoke(list,24);

    // 6、输出集合
    System.out.println(list); // [admin, user, guest, 24]
}
           

9、配置文件

把一个类的类名和方法名添加到properties文件中,通过反射机制读取内容并调用方法

user.properties

className = cn.com.example15.User
methodName = toString
           

代码示例

public static void propertiesHandler() throws Exception {


      // 1、创建输入流
      FileReader fileReader = new FileReader("src/cn/com/codingsir/user.properties");

      // 2、实例化Properties对象
      Properties properties = new Properties();

      // 3、读取数据并保存
      properties.load(fileReader);

      // 4、获取配置文件内容
      String className = properties.getProperty("className");
      String methodName = properties.getProperty("methodName");

      // 5、获取Class对象
      Class clazz = Class.forName(className);

      // 6、实例化对象
      User user = (User)clazz.newInstance();

      // 7、获取Method对象
      Method method = clazz.getMethod(methodName);

      // 8、调用方法
      String str = (String)method.invoke(user);

      System.out.println(str); // User{id=0, userName='null', password='null', role='null'}

  }
           

继续阅读