当前位置: 首页 > news >正文

深入理解Java反射机制:从基础到高级应用

一、反射机制概述

Java 反射机制是 Java 语言的一个重要特性,它允许程序在运行时动态地获取类的信息,以及动态地调用对象的方法、修改属性等操作。这意味着程序员可以在运行期间检查和操作类、对象的各种元素,而不需要在编译时就知道这些信息。反射机制为 Java 程序带来了高度的灵活性和可扩展性,是许多高级框架和技术的基础。

       Java反射机制是指在运行时动态获取类的信息或动态调用对象的方法、修改属性等操作。主要核心就是Class类、Constructor类、Field类、Method类等API。

       反射机制主要应用于框架开发、动态代理、ORM框架、JDBC驱动等方面。通过反射机制,程序员能够获得在编译期间不被知晓的类、属性、方法等信息。

       但是反射机制的性能较低,常常被认为是一种牺牲性能换取灵活性的实现方式

二、Java中获取Class对象有以下三种方式

* 1. 反射机制是JDK中的一套类库,这套类库可以帮助我们操作/读取 class 字节码文件。
* 2. 后期学习的大量的java框架,底层都是基于反射机制实现的,所以必须掌握(要能够数量的使用反射机制中的方法)。
* 3. 反射机制可以让程序更加灵活。怎么灵活????
* 4. 反射机制最核心的几个类:
*      java.lang.Class:Class类型的实例代表硬盘上的某个class文件。或者说代表某一种类型。
*      java.lang.reflect.Filed:Filed类型的实例代表类中的属性/字段
*      java.lang.reflect.Constructor: Constructor类型的实例代表类中的构造方法
*      java.lang.reflect.Method: Method类型的实例代表类中的方法

User类

public class User {

    static {
        System.out.println("User类的静态代码块执行了!");
    }

    private String name;
    private int age;

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

    public User() {
        System.out.println("User类的无参数构造方法执行了!");
    }

    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;
    }
}

测试类:

/**
 * 1. 反射机制是JDK中的一套类库,这套类库可以帮助我们操作/读取 class 字节码文件。
 * 2. 后期学习的大量的java框架,底层都是基于反射机制实现的,所以必须掌握(要能够数量的使用反射机制中的方法)。
 * 3. 反射机制可以让程序更加灵活。怎么灵活????
 * 4. 反射机制最核心的几个类:
 *      java.lang.Class:Class类型的实例代表硬盘上的某个class文件。或者说代表某一种类型。
 *      java.lang.reflect.Filed:Filed类型的实例代表类中的属性/字段
 *      java.lang.reflect.Constructor: Constructor类型的实例代表类中的构造方法
 *      java.lang.reflect.Method: Method类型的实例代表类中的方法
 * 5. 在java语言中获取Class的三种方式:
 *      第一种方式:Class c = Class.forName("完整的全限定类名");
 *          注意:
 *              1.全限定类名是带有包名的。
 *              2.是lang包下的,java.lang也不能省略。
 *              3.这是个字符串参数。
 *              4.如果这个类根本不存在,运行时会报异常:java.lang.ClassNotFoundException
 *              5.这个方法的执行会导致类的加载动作的发生。
 *      第二种方式:Class c = obj.getClass();
 *          注意:这个方法是通过引用去调用的。
 *      第三种方式:在java语言中,任何一种类型,包括基本数据类型,都有 .class 属性。用这个属性可以获取Class实例。
 */
public class ReflectTest01 {
    public static void main(String[] args) throws ClassNotFoundException {

        // stringClass 就代表 String类型。
        // stringClass 就代表硬盘上的 String.class文件。
        Class stringClass = Class.forName("java.lang.String");

        // 获取 User 类型
        Class userClass = Class.forName("oop1.User");//把User加载到虚拟机里

        String s1 = "动力节点";
        Class stringClass2 = s1.getClass();

        // 某种类型的字节码文件在内存当中只有一份。
        // stringClass 和 stringClass2 都代表了同一种类型:String类型
        System.out.println(stringClass == stringClass2); // true

        User user = new User("zhangsan", 20);
        Class userClass2 = user.getClass();
        System.out.println(userClass2 == userClass); // true

        // intClass 代表的就是基本数据类型 int类型
        Class intClass = int.class;
        Class doubleClass = double.class;
        Class stringClass3 = String.class;
        Class userClass3 = User.class;

        System.out.println(stringClass3 == stringClass); // true
    }
}

运行结果:

三、反射作用的体现

1.通过反射机制实例化对象

* 获取到Class之后能干啥?
*      1. 至少可以实例化对象。
/**
 * 获取到Class之后能干啥?
 *      1. 至少可以实例化对象。
 */
public class ReflectTest02 {
    public static void main(String[] args) throws Exception{

        // 获取到Class类型的实例之后,可以实例化对象
        // 通过反射机制实例化对象
        Class userClass = Class.forName("oop1.User"); // userClass 代表的就是 User类型。


        //Instance是实例的意思
        // 通过userClass来实例化User类型的对象
        // 底层实现原理是:调用了User类的无参数构造方法完成了对象的实例化。
        // 要使用这个方法实例化对象的话,必须保证这个类中是存在无参数构造方法的。如果没有无参数构造方法,则出现异常:java.lang.InstantiationException
        User user = (User)userClass.newInstance();

        System.out.println(user);

        User user2 = (User)userClass.newInstance();
        //new了2个对象,所以为false
        System.out.println(user == user2); // false
    }
}

运行结果:

2.反射结合配置文件灵活的实例化对象

getBundle的作用

在 Java 中,getBundle 方法主要用于获取资源束(ResourceBundle)对象,资源束是一种方便进行国际化和本地化的机制,它允许你根据不同的语言环境和地域设置加载相应的资源文件。以下为你详细介绍其作用、使用场景和示例:

 作用

getBundle 方法是 ResourceBundle 类的静态方法,其主要作用是根据指定的基名、语言环境和类加载器来加载对应的资源束。资源束通常包含了一系列的键值对,用于存储不同语言或地区的文本信息,如菜单标签、提示信息、错误消息等。

通过 getBundle 方法,你可以根据用户的语言环境动态加载相应的资源文件,从而实现程序的国际化。

方法签名

ResourceBundle 类提供了多个重载的 getBundle 方法,常用的方法签名如下:

// 根据基名和默认语言环境获取资源束
public static ResourceBundle getBundle(String baseName)

// 根据基名和指定语言环境获取资源束
public static ResourceBundle getBundle(String baseName, Locale locale)

// 根据基名、指定语言环境和类加载器获取资源束
public static ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader)

baseName:资源束的基名,通常是资源文件的名称(不包含扩展名)。

locale:指定的语言环境,用于确定加载哪个地区的资源文件。

loader:指定的类加载器,用于加载资源文件。


* 读取属性配置文件,获取类名,通过反射机制实例化对象。
* 通过这个案例的演示就知道反射机制是灵活的。这个程序可以做到对象的动态创建。
* 只要修改属性配置文件就可以完成不同对象的实例化。

classInfo.properties

测试类:

import java.util.ResourceBundle;

/**
 * 读取属性配置文件,获取类名,通过反射机制实例化对象。
 * 通过这个案例的演示就知道反射机制是灵活的。这个程序可以做到对象的动态创建。
 * 只要修改属性配置文件就可以完成不同对象的实例化。
 */
public class ReflectTest03 {
    public static void main(String[] args) throws Exception {

        // 资源绑定器
        ResourceBundle bundle = ResourceBundle.getBundle("oop1.classInfo");

        // 通过key获取value
        String className = bundle.getString("className");

        // 通过反射机制实例化对象
        Class classObj = Class.forName(className);

        // 实例化
        Object obj = classObj.newInstance();

        System.out.println(obj);
    }
}

运行结果:

在属性配置文件中配置类名:classInfo.properties

className=java.util.Date

通过IO流读取属性配置文件,获取类名,再通过反射机制实例化对象。

如果要创建其他类的实例对象,只需要修改classInfo.properties配置文件即可。

这说明反射机制可以让程序变的更加灵活。在进行系统扩展时,可以达到OCP开闭原则。

四、反射Field

1.反射Class的Field

 关于反射机制中的 java.lang.reflect.Field(代表的是一个类中的字段/属性)

⑴.getFields()

在 Java 中,getFields() 是 Class 类的一个方法,它主要用于反射机制,允许你在运行时获取类的相关信息。下面将从方法的定义、作用、使用示例以及注意事项等方面进行详细介绍。

方法定义

public Field[] getFields() throws SecurityException

返回值:Field 数组,Field 类是 java.lang.reflect 包中的一个类,用于表示类或接口的字段(成员变量)。该数组包含了当前 Class 对象所表示的类或接口的所有可访问的公共(public)字段,包括从超类继承而来的公共字段。

异常:如果存在安全管理器并且其 checkMemberAccess 方法不允许对这些字段进行访问,则会抛出 SecurityException。

VIP类:

public class Vip {
    // Field
    public String name;

    private int age;

    protected String birth;

    boolean gender;

    public static String address = "北京海淀";

    public static final String GRADE = "金牌";
}

测试类:测试getFields()

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * 关于反射机制中的 java.lang.reflect.Field(代表的是一个类中的字段/属性)
 */
public class ReflectTest04 {
    public static void main(String[] args) throws Exception{
        // 获取Vip类
        Class vipClass = Class.forName("oop1.Vip");

        // 获取Vip类中所有 public 修饰的属性/字段
        Field[] fields = vipClass.getFields();
        System.out.println(fields.length);

        // 遍历数组
        for(Field field : fields){
            System.out.println(field.getName());
        }

    }
}

运行结果:

⑵.Class 类中的 getName() 方法

作用

Class 类的 getName() 方法用于返回该 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)的全限定名。全限定名包含了类所在的包名和类名。

/**
 * 有Class,能不能获取类型名?
 */
public class ReflectTest05 {
    public static void main(String[] args) {
        // c 代表 String 类型
        Class c = "动力节点".getClass();

        // 获取类型名
        System.out.println(c.getName()); // java.lang.String

        // 获取不带包名的简短类名
        System.out.println(c.getSimpleName());
    }
}

运行结果:

⑶.getType()

java.lang.reflect.Field 类中的 getType() 方法

作用

Field 类代表类或接口中的一个字段(成员变量),getType() 方法用于返回该字段所声明的类型,返回值是一个 Class 对象,这个 Class 对象可以用来进一步获取该类型的详细信息。

import java.lang.reflect.Field;

class MyClass {
    public int number;
    public String text;
}

public class FieldTypeExample {
    public static void main(String[] args) throws NoSuchFieldException {
        // 获取 MyClass 的 Class 对象
        Class<MyClass> myClass = MyClass.class;

        // 获取 number 字段
        Field numberField = myClass.getField("number");
        // 获取 number 字段的类型
        Class<?> numberFieldType = numberField.getType();
        System.out.println("Type of 'number' field: " + numberFieldType.getName());

        // 获取 text 字段
        Field textField = myClass.getField("text");
        // 获取 text 字段的类型
        Class<?> textFieldType = textField.getType();
        System.out.println("Type of 'text' field: " + textFieldType.getName());
    }
}

运行结果:

代码解释

首先定义了 MyClass 类,其中包含一个 int 类型的 number 字段和一个 String 类型的 text 字段。

通过 MyClass.class 获取 MyClass 的 Class 对象。

使用 getField 方法分别获取 number 和 text 字段的 Field 对象。

调用 getType() 方法获取字段的类型,返回的是 Class 对象,再调用 getName() 方法输出类型的全限定名。

⑷.getModifiers():获取属性的修饰符

Vip类:

public class Vip {
    // Field
    public String name;

    private int age;

    protected String birth;

    boolean gender;

    public static String address = "北京海淀";

    public static final String GRADE = "金牌";
}

测试类:

/**
 * 关于反射机制中的 java.lang.reflect.Field(代表的是一个类中的字段/属性)
 */
public class ReflectTest04 {
    public static void main(String[] args) throws Exception{
        // 获取Vip类
        Class vipClass = Class.forName("oop1.Vip");

        // 获取Vip类中所有的属性/字段,包括私有的
        Field[] fields1 = vipClass.getDeclaredFields();

        for(Field field : fields1){
            // 获取属性名
            System.out.println(field.getName());

            // 获取属性类型
            Class fieldType = field.getType();


            // 获取属性类型的简单名称
            System.out.println(fieldType.getSimpleName());


            // 获取属性的修饰符
            System.out.println(field.getModifiers());
            System.out.println(Modifier.toString(field.getModifiers()));
            System.out.println("=============================");
        }

    }
}

运行结果;

⑸.反编译类的字段

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
 * 反编译(反射) java.lang.String 类中所有的属性。
 */
public class ReflectTest06 {
    public static void main(String[] args) throws Exception{
        // 获取String类
        Class stringClass = Class.forName("java.lang.String");

        // 字符串拼接
        StringBuilder sb = new StringBuilder();

        // 获取类的修饰符
        sb.append(Modifier.toString(stringClass.getModifiers()));

        sb.append(" class ");

        //sb.append(stringClass.getSimpleName());
        sb.append(stringClass.getName());

        sb.append(" extends ");

        // 获取当前类的父类
        sb.append(stringClass.getSuperclass().getName());

        // 获取当前类的实现的所有接口
        Class[] interfaces = stringClass.getInterfaces();
        if(interfaces.length > 0){
            sb.append(" implements ");
            for (int i = 0; i < interfaces.length; i++) {
                Class interfaceClass = interfaces[i];
                sb.append(interfaceClass.getName());
                if(i != interfaces.length - 1){
                    sb.append(",");
                }
            }
        }

        sb.append("{\n");

        // 获取所有属性
        Field[] fields = stringClass.getDeclaredFields();
        for (Field field : fields){
            sb.append("\t");
            sb.append(Modifier.toString(field.getModifiers()));
            sb.append(" ");
            sb.append(field.getType().getName());
            sb.append(" ");
            sb.append(field.getName());
            sb.append(";\n");
        }

        sb.append("}");

        // 输出
        System.out.println(sb);
    }
}

运行结果:

⑹.通过反射为对象属性赋值

/**
 * 通过反射机制如何访问Field,如何给属性赋值,如何读取属性的值。
 */
public class ReflectTest07 {
    public static void main(String[] args) throws Exception {

        // ******************** 不使用反射机制访问对象属性 ********************
        // 创建一个 Customer 类的对象
        Customer customer = new Customer();

        // 修改属性的值(set动作)
        // 这里涉及三个要素:
        // 1. customer:要操作的对象
        // 2. name:要操作的属性
        // 3. "张三":要赋给属性的值
        customer.name = "张三";

        // 读取属性的值(get动作)
        // 这里是读取 customer 对象的 name 属性的值,并将其打印输出
        System.out.println(customer.name);


        // ******************** 使用反射机制访问对象属性 ********************
        // 获取 Customer 类的 Class 对象
        // 通过 Class.forName 方法,传入类的全限定名,获取该类的 Class 对象
        Class clazz = Class.forName("oop1.Customer");

        // 获取对应的 Field 对象
        // 使用 getDeclaredField 方法,传入属性名 "age",获取该属性对应的 Field 对象
        // getDeclaredField 可以获取类中所有声明的属性,包括私有属性
        Field ageField = clazz.getDeclaredField("age");

        // 调用方法打破封装
        // 由于 age 属性可能是私有属性,默认情况下不能直接访问
        // 调用 setAccessible(true) 方法可以绕过 Java 的访问控制检查,允许对私有属性进行操作
        ageField.setAccessible(true);

        // 修改属性的值
        // 给对象属性赋值同样涉及三个要素:
        // 1. customer:要操作的对象
        // 2. ageField:要操作的属性对应的 Field 对象
        // 3. 30:要赋给属性的值
        // 使用 Field 对象的 set 方法,将值 30 赋给 customer 对象的 age 属性
        ageField.set(customer, 30);

        // 读取属性的值
        // 使用 Field 对象的 get 方法,读取 customer 对象的 age 属性的值
        // 并将其打印输出,前面添加了提示信息 "年龄:"
        System.out.println("年龄:" + ageField.get(customer));


        // ******************** 通过反射机制操作 name 属性 ********************
        // 获取 name 属性对应的 Field 对象
        // 同样使用 getDeclaredField 方法,传入属性名 "name"
        Field nameField = clazz.getDeclaredField("name");
        // 修改属性 name 的值
        // 使用 nameField 的 set 方法,将值 "李四" 赋给 customer 对象的 name 属性
        nameField.set(customer, "李四");
        // 读取属性 name 的值
        // 使用 nameField 的 get 方法,读取 customer 对象的 name 属性的值,并将其打印输出
        System.out.println(nameField.get(customer));
    }
}

// 定义 Customer 类,用于测试反射操作
class Customer {
    // 定义一个公共的 name 属性
    public String name;
    // 定义一个私有的 age 属性
    private int age;
}

运行结果:

五.反射一个类的Method

UserService类:

public class UserService {

    /*public void login(){

    }*/

    /**
     * 登录系统的方法
     * @param username 用户名
     * @param password 密码
     * @return true表示登录成功,false表示失败
     */
    public boolean login(String username, String password){
        if("admin".equals(username) && "123456".equals(password)){
            return true;
        }
        return false;
     /*   return "admin".equals(username) && "123456".equals(password);*/
    }

/*    public String concat(String s1, String s2, String s3){
        return s1 + s2 + s3;
    }*/

    /**
     * 退出系统的方法
     */
    public void logout(){
        System.out.println("系统已安全退出!");
    }
}

测试类:

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

/**
 * 通过反射机制获取Method
 */
public class ReflectTest08 {
    public static void main(String[] args) throws Exception{
        // 获取类
        Class clazz = Class.forName("oop1.UserService");

        // 获取所有的方法,包含私有的方法
        Method[] methods = clazz.getDeclaredMethods();
        //System.out.println(methods.length);

        // 遍历数组
        for(Method method : methods){
            // 方法修饰符
            System.out.println(Modifier.toString(method.getModifiers()));
            // 方法返回值类型
            System.out.println(method.getReturnType().getName());
            // 方法名
            System.out.println(method.getName());
            // 方法的参数列表
            /*Class<?>[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes){
                System.out.println(parameterType.getName());
            }*/

            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters){
                System.out.println(parameter.getType().getName());
                System.out.println(parameter.getName());// arg0, arg1, arg2......
            }
        }
    }
}

运行结果:

1.反编译类的方法

UserService类:

public class UserService {

    /*public void login(){

    }*/

    /**
     * 登录系统的方法
     * @param username 用户名
     * @param password 密码
     * @return true表示登录成功,false表示失败
     */
    public boolean login(String username, String password){
        if("admin".equals(username) && "123456".equals(password)){
            return true;
        }
        return false;
     /*   return "admin".equals(username) && "123456".equals(password);*/
    }

/*    public String concat(String s1, String s2, String s3){
        return s1 + s2 + s3;
    }*/

    /**
     * 退出系统的方法
     */
    public void logout(){
        System.out.println("系统已安全退出!");
    }
}

测试类:

/**
 * 反射一个类中所有的方法,然后进行拼接字符串。
 */
public class ReflectTest09 {
    public static void main(String[] args) throws Exception{
        StringBuilder sb = new StringBuilder();
        Class stringClass = Class.forName("oop1.UserService");
        // 获取类的修饰符
        sb.append(Modifier.toString(stringClass.getModifiers()));
        sb.append(" class ");
        // 获取类名
        sb.append(stringClass.getName());
        // 获取父类名
        sb.append(" extends ");
        sb.append(stringClass.getSuperclass().getName());
        // 获取父接口名
        Class[] interfaces = stringClass.getInterfaces();
        if(interfaces.length > 0){
            sb.append(" implements ");
            for (int i = 0; i < interfaces.length; i++) {
                sb.append(interfaces[i].getName());
                if(i != interfaces.length - 1){
                    sb.append(",");
                }
            }
        }
        sb.append("{\n");

        // 类体
        // 获取所有的方法
        Method[] methods = stringClass.getDeclaredMethods();
        for(Method method : methods){
            sb.append("\t");
            // 追加修饰符
            sb.append(Modifier.toString(method.getModifiers()));
            // 追加返回值类型
            sb.append(" ");
            sb.append(method.getReturnType().getName());
            // 追加方法名
            sb.append(" ");
            sb.append(method.getName());
            sb.append("(");
            // 追加参数列表
            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                sb.append(parameter.getType().getName());
                sb.append(" ");
                sb.append(parameter.getName());
                if(i != parameters.length - 1){
                    sb.append(",");
                }
            }
            sb.append("){}\n");
        }

        sb.append("}");

        // 输出
        System.out.println(sb);

    }
}

运行结果:

2.通过反射机制调用方法

 UserService类:

public class UserService {

    /*public void login(){

    }*/

    /**
     * 登录系统的方法
     * @param username 用户名
     * @param password 密码
     * @return true表示登录成功,false表示失败
     */
    public boolean login(String username, String password){
        if("admin".equals(username) && "123456".equals(password)){
            return true;
        }
        return false;
     /*   return "admin".equals(username) && "123456".equals(password);*/
    }

/*    public String concat(String s1, String s2, String s3){
        return s1 + s2 + s3;
    }*/

    /**
     * 退出系统的方法
     */
    public void logout(){
        System.out.println("系统已安全退出!");
    }
}

测试类:

/**
 * 通过反射机制调用Method
 */
public class ReflectTest10 {
    public static void main(String[] args) throws Exception {
        // 不使用反射机制怎么调用方法?
        // 创建对象
        UserService userService = new UserService();

        // 调用方法
        // 分析:调用一个方法需要几个要素?四要素
        // 调用哪个对象的哪个方法,传什么参数,返回什么值
        boolean isSuccess = userService.login("admin", "123456");
        System.out.println(isSuccess ? "登录成功" : "登录失败");

        // 调用方法
        userService.logout();


        System.out.println("+++++++++++++++++++++");

        // 通过反射机制调用login方法
        // 获取Class
        Class clazz = Class.forName("oop1.UserService");

        // 获取login方法
        Method loginMethod = clazz.getDeclaredMethod("login", String.class, String.class);

        // 调用login方法
        Object retValue = loginMethod.invoke(userService, "admin", "123456");
        System.out.println(retValue);

        // 调用logout方法
        Method logoutMethod = clazz.getDeclaredMethod("logout");
        logoutMethod.invoke(userService);
    }
}

运行结果:

六、反射Constructor

* 通过反射机制获取一个类中所有的构造方法

1.反编译类的构造方法

order类:

public class Order {
    private String no;
    private double price;
    private String state;

    @Override
    public String toString() {
        return "Order{" +
                "no='" + no + '\'' +
                ", price=" + price +
                ", state='" + state + '\'' +
                '}';
    }

    public Order() {
    }

    public Order(String no) {
        this.no = no;
    }

    public Order(String no, double price) {
        this.no = no;
        this.price = price;
    }

    public Order(String no, double price, String state) {
        this.no = no;
        this.price = price;
        this.state = state;
    }

    public String getNo() {
        return no;
    }

    public void setNo(String no) {
        this.no = no;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

测试类:


/**
 * 通过反射机制获取一个类中所有的构造方法
 */
public class ReflectTest11 {
    public static void main(String[] args) throws Exception{
        StringBuilder sb = new StringBuilder();
        // 获取类
        Class clazz = Class.forName("oop1.Order");
        // 类的修饰符
        sb.append(Modifier.toString(clazz.getModifiers()));
        sb.append(" class ");
        // 类名
        sb.append(clazz.getName());
        sb.append(" extends ");
        // 父类名
        sb.append(clazz.getSuperclass().getName());
        // 实现的接口
        Class[] interfaces = clazz.getInterfaces();
        if(interfaces.length > 0) {
            sb.append(" implements ");
            for (int i = 0; i < interfaces.length; i++) {
                sb.append(interfaces[i].getName());
                if(i != interfaces.length - 1){
                    sb.append(",");
                }
            }
        }
        sb.append("{\n");

        //类体
        // 获取所有的构造方法
        Constructor[] cons = clazz.getDeclaredConstructors();
        // 遍历所有的构造方法
        for(Constructor con : cons){
            sb.append("\t");
            // 构造方法修饰符
            sb.append(Modifier.toString(con.getModifiers()));
            sb.append(" ");
            // 构造方法名
            sb.append(con.getName());
            sb.append("(");

            // 构造方法参数列表
            Parameter[] parameters = con.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                sb.append(parameter.getType().getName());
                sb.append(" ");
                sb.append(parameter.getName());
                if(i != parameters.length - 1){
                    sb.append(",");
                }
            }

            sb.append("){}\n");
        }

        sb.append("}");

        System.out.println(sb);
    }
}

运行结果:

2.通过反射机制调用构造方法

order类:

public class Order {
    private String no;
    private double price;
    private String state;

    @Override
    public String toString() {
        return "Order{" +
                "no='" + no + '\'' +
                ", price=" + price +
                ", state='" + state + '\'' +
                '}';
    }

    public Order() {
    }

    public Order(String no) {
        this.no = no;
    }

    public Order(String no, double price) {
        this.no = no;
        this.price = price;
    }

    public Order(String no, double price, String state) {
        this.no = no;
        this.price = price;
        this.state = state;
    }

    public String getNo() {
        return no;
    }

    public void setNo(String no) {
        this.no = no;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

测试类:

import java.lang.reflect.Constructor;
/**
 * 通过反射机制调用构造方法来创建对象。
 */
public class ReflectTest12 {
    public static void main(String[] args) throws Exception{

        // 不使用反射机制的时候,怎么创建的对象?
        Order order1 = new Order();
        System.out.println(order1);

        Order order2 = new Order("1111122222", 3650.5, "已完成");
        System.out.println(order2);
        System.out.println("=================================================");

        // 通过反射机制来实例化对象?
        Class clazz = Class.forName("oop1.Order");
        // 这种方式依赖的是必须有一个无参数构造方法。如果没有会出现异常!
        // 在Java9的时候,这个方法被标注了已过时。不建议使用了。
        /*Object obj = clazz.newInstance();
        System.out.println(obj);*/

        // 获取Order的无参数构造方法
        Constructor defaultCon = clazz.getDeclaredConstructor();
        // 调用无参数构造方法实例化对象
        Object obj = defaultCon.newInstance();
        System.out.println(obj);

        // 获取三个参数的构造方法
        Constructor threeArgsCon = clazz.getDeclaredConstructor(String.class, double.class, String.class);
        // 调用三个参数的构造方法
        Object obj1 = threeArgsCon.newInstance("5552454222", 6985.0, "未完成");
        System.out.println(obj1);
    }
}

运行结果:

七、模拟框架的部分实现

UserService类:

public class UserService {

    /*public void login(){

    }*/

    /**
     * 登录系统的方法
     * @param username 用户名
     * @param password 密码
     * @return true表示登录成功,false表示失败
     */
    public boolean login(String username, String password){
       /* if("admin".equals(username) && "123456".equals(password)){
            return true;
        }
        return false;*/
        return "admin".equals(username) && "123456".equals(password);
    }

    public String concat(String s1, String s2, String s3){
        return s1 + s2 + s3;
    }

    /**
     * 退出系统的方法
     */
    public void logout(){
        System.out.println("系统已安全退出!");
    }
}

config.properties

className=oop1.UserService
methodName=concat
parameterTypes=java.lang.String,java.lang.String,java.lang.String
parameterValues=abc,def,xyz

测试类:

/**
 * 模拟框架的部分代码。通过读取属性配置文件,获取类信息,方法信息,然后通过反射机制调用方法。
 */
public class ReflectTest13 {
    public static void main(String[] args) throws Exception {
        // 读取属性配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("oop1.config");
        String className = bundle.getString("className");
        String methodName = bundle.getString("methodName");
        String parameterTypes = bundle.getString("parameterTypes");
        String parameterValues = bundle.getString("parameterValues");

        // 通过反射机制调用方法
        // 创建对象(依赖无参数构造方法)
        Class<?> clazz = Class.forName(className);
        Constructor<?> defaultCon = clazz.getDeclaredConstructor();
        Object obj = defaultCon.newInstance();

        // 获取方法
        // java.lang.String,java.lang.String
        String[] strParameterTypes = parameterTypes.split(",");
        Class[] classParameterTypes = new Class[strParameterTypes.length];
        for (int i = 0; i < strParameterTypes.length; i++) {
            classParameterTypes[i] = Class.forName(strParameterTypes[i]);
        }
        Method method = clazz.getDeclaredMethod(methodName, classParameterTypes);

        // 调用方法
        // parameterValues=admin,123456
        Object retValue = method.invoke(obj, parameterValues.split(","));

        System.out.println(retValue);
    }
}

运行结果:

八、类加载及双亲委派机制

1.类加载的过程

Java 类加载过程详解

类加载是 JVM 将类的字节码文件(.class)加载到内存,并转换为可执行代码的过程。整个过程分为三个阶段:装载(Loading)连接(Linking)初始化(Initialization)。以下是详细解析:


⑴.装载(Loading)

核心任务:将类的 .class 文件读入内存,并生成对应的 java.lang.Class 对象。
关键步骤

  1. 定位类文件

    • 通过类加载器(ClassLoader)按全限定名(如 com.example.MyClass)查找 .class 文件。

    • 查找路径包括本地文件系统、JAR 包、网络资源等。

  2. 读取字节码

    • 将 .class 文件内容读取到内存中的字节数组。

  3. 生成 Class 对象

    • JVM 在方法区(元空间)创建类的 Class 对象,作为该类在内存中的唯一入口。

示例

Class<?> clazz = Class.forName("com.example.MyClass"); // 触发类加载

⑵.连接(Linking)

连接阶段分为三个子阶段,确保类的正确性和可用性:

1. 验证(Verification)
  • 目的:确保字节码文件符合 JVM 规范,防止恶意代码或损坏文件破坏 JVM。

  • 验证内容

    • 文件格式:魔数(CAFEBABE)、版本号等。

    • 元数据:类继承关系、字段/方法合法性。

    • 字节码:指令合法性、栈数据类型匹配。

    • 符号引用:引用的类/方法/字段是否存在。

2. 准备(Preparation)
  • 目的:为类的静态变量分配内存并设置初始值(默认值)。

  • 规则

    • 静态变量(非 final)分配内存并赋默认值(如 int → 0,对象 → null)。

    • final 静态常量直接赋代码中定义的值(编译时确定)。

示例

public static int k = 10;     // 准备阶段:k = 0 → 初始化阶段:k = 10
public static final int f = 10; // 准备阶段:f = 10(final 常量)
3. 解析(Resolution)
  • 目的:将常量池中的符号引用转换为直接引用(内存地址)。

  • 符号引用:用符号(如全限定名)描述的类、方法、字段。

  • 直接引用:指向目标在内存中的指针或偏移量。

示例

// 符号引用:java/lang/Object
// 解析后转换为方法区中 Object 类的直接地址
Object obj = new Object();

⑶.初始化(Initialization)

核心任务:执行类构造器 <clinit>() 方法,为静态变量赋值并执行静态代码块。
触发条件

  • 首次创建类的实例(new)。

  • 访问类的静态变量或静态方法(非 final 常量)。

  • 子类初始化时,若父类未初始化,先触发父类初始化。

  • 反射调用(如 Class.forName())。

执行顺序

  1. 父类静态变量和静态代码块(按代码顺序)。

  2. 子类静态变量和静态代码块(按代码顺序)。

示例

public class MyClass {
    public static int a = 1;          // 初始化阶段赋值
    static {
        System.out.println("静态代码块"); // 初始化阶段执行
    }
}

低版本的JDK中类加载器的名字:

启动类加载器:负责加载rt.jar

扩展类加载器:ext/*.jar

系统类加载器:classpath


⑷.类加载的线程安全
  • JVM 通过加锁(每个类对应一个锁)确保类只被初始化一次。

  • 多线程环境下,即使多个线程同时触发初始化,最终只有一个线程执行 <clinit>() 方法。


类加载器(ClassLoader)

类加载由类加载器完成,Java 采用 双亲委派模型

  1. 引导类加载器(Bootstrap):加载核心类库(如 java.lang.*)。

  2. 扩展类加载器(Extension):加载 jre/lib/ext 目录下的类。

  3. 应用类加载器(Application):加载用户类路径(ClassPath)的类。

  4. 自定义类加载器:用户可扩展的类加载器(如 Tomcat 的 WebAppClassLoader)。

双亲委派流程

  • 类加载请求先委派给父加载器处理。

  • 若父加载器无法完成,子加载器才尝试加载。


类生命周期总结

阶段操作
装载加载字节码 → 生成 Class 对象
连接验证 → 准备(静态变量默认值)→ 解析(符号引用转直接引用)
初始化执行 <clinit>()(静态变量赋值 + 静态代码块)
使用创建对象、调用方法等
卸载从方法区移除类信息(由 JVM 垃圾回收控制)

2.虚拟机的三个类加载器

类加载器

虚拟机内部提供了三种类加载器(Java9+):

启动类加载器(BootstrapClassLoader):加载Java最核心的类,例如String

平台类加载器(PlatformClassLoader):加载Java平台扩展的类库,例如解析XML的

应用类加载器(AppClassLoader):加载classpath中的

同时我们还可以自定义一个类加载器(UserClassLoader)

获取类加载器可以通过 getParent()方法一级一级获取

/**
 * 虚拟机内部有三个不同的类加载器:
 *      1. 启动类加载器:BootstrapClassLoader
 *          负责加载核心类库
 *      2. 平台类加载器:PlatformClassLoader
 *          负责加载扩展类库
 *      3. 应用类加载器:AppClassLoader
 *          负责加载classpath
 *
 * 类加载器也是可以自定义的,只要符合类加载器的规范即可。
 * 自定义的类加载器,我们一般称为:用户类加载器。
 */
public class ReflectTest15 {
    public static void main(String[] args) {

        // 通过自定义的类获取的类加载器是:应用类加载器。
        ClassLoader appClassLoader = ReflectTest15.class.getClassLoader();
        System.out.println("应用类加载器:" + appClassLoader);

        // 获取应用类加载器
        ClassLoader appClassLoader2 = ClassLoader.getSystemClassLoader();
        System.out.println("应用类加载器:" + appClassLoader2);

        // 获取应用类加载器
        ClassLoader appClassLoader3 = Thread.currentThread().getContextClassLoader();
        System.out.println("应用类加载器:" + appClassLoader3);

        // 通过 getParent() 方法可以获取当前类加载器的 “父 类加载器”。
        // 获取平台类加载器。
        System.out.println("平台类加载器:" + appClassLoader.getParent());

        // 获取启动类加载器。
        // 注意:启动类加载器负责加载的是JDK核心类库,这个类加载器的名字看不到,直接输出的时候,结果是null。
        System.out.println("启动类加载器:" + appClassLoader.getParent().getParent());
    }
}

运行结果:

3.双亲委派机制

九、获取Class的第四种方式

User类:

public class User {

    static {
        System.out.println("User类的静态代码块执行了!");
    }

    private String name;
    private int age;

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

    public User() {
        System.out.println("User类的无参数构造方法执行了!");
    }

    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;
    }
}

测试类:

/**
 * 获取Class的第四种方式
 */
public class ReflectTest14 {
    public static void main(String[] args) throws Exception {
        // 获取类加载器对象(获取的是 系统类加载器/应用类加载器 )
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

        // jdk.internal.loader.ClassLoaders$AppClassLoader@36baf30c
        // 这个类加载器是负责加载 classpath 中的字节码文件的。
        //System.out.println(systemClassLoader);

        // 加载类:但是这个加载过程只是将类加载过程中的前两步完成了,第三步的初始化没做。
        // 什么时候做初始化?在这个类真正的被第一次使用的时候。
        Class<?> aClass = systemClassLoader.loadClass("oop1.User");

        System.out.println(aClass.newInstance());

        // 这种方式会走完类加载的全部过程,三步齐全
        //Class clazz = Class.forName("oop1.User");

    }
}

运行结果;

十、反射泛型

1.反射父类的泛型

Animal类:

/**
 * 在类上定义泛型
 * @param <X>
 * @param <Y>
 * @param <Z>
 */
public class Animal<X, Y, Z> {
}

Cat类:

public class Cat extends Animal<String, Integer, Double>{
}

测试类:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 获取父类的泛型信息
 */
public class Test {
    public static void main(String[] args) {
        // 获取类
        Class<Cat> catClass = Cat.class;

        // 获取当前类的父类泛型
        Type genericSuperclass = catClass.getGenericSuperclass();
        //System.out.println(genericSuperclass instanceof Class);
        //System.out.println(genericSuperclass instanceof ParameterizedType);

        // 如果父类使用了泛型
        if(genericSuperclass instanceof ParameterizedType){
            // 转型为参数化类型
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            // 获取泛型数组
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            // 遍历泛型数组
            for(Type a : actualTypeArguments){
                // 获取泛型的具体类型名
                System.out.println(a.getTypeName());
            }
        }
    }
}

运行结果:

2.反射接口的泛型

接口:

public interface Flyable<X, Y> {
}

Mouse类:

public class Mouse implements Flyable<String, Integer>, Comparable<Mouse>{
    @Override
    public int compareTo(Mouse o) {
        return 0;
    }
}

测试类;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;


// 定义一个名为Test的公共类
public class Test {
    public static void main(String[] args) {
        // 通过类字面量的方式获取Mouse类对应的Class对象
        // Class对象是Java反射机制的核心,它包含了类的各种信息,如类名、方法、字段等
        Class<Mouse> mouseClass = Mouse.class;

        // 调用Class对象的getGenericInterfaces方法,获取Mouse类实现的所有接口(包括泛型信息)
        // 返回的是一个Type数组,Type是Java反射中表示类型的接口,它可以表示类、接口、数组、泛型等各种类型
        Type[] genericInterfaces = mouseClass.getGenericInterfaces();

        // 遍历Mouse类实现的所有接口
        for (Type g : genericInterfaces) {
            // 判断当前接口类型是否使用了泛型
            // ParameterizedType是Type的子接口,表示使用了泛型的类型
            if(g instanceof ParameterizedType){
                // 如果使用了泛型,将Type对象强制转换为ParameterizedType对象
                // 这样就可以获取该泛型类型的具体信息
                ParameterizedType parameterizedType = (ParameterizedType) g;

                // 调用ParameterizedType对象的getActualTypeArguments方法,获取泛型类型的实际类型参数
                // 例如,如果接口定义为Interface<T>,这里获取的就是T的实际类型
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

                // 遍历泛型类型的实际类型参数
                for(Type a : actualTypeArguments){
                    // 调用Type对象的getTypeName方法,获取实际类型参数的类型名称,并将其打印输出
                    System.out.println(a.getTypeName());
                }
            }
        }
    }
}

运行结果:

3.反射属性上的泛型

User类:

public class User {
    private Map<Integer, String> map;
}

测试类:

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Test {
    public static void main(String[] args) throws Exception{
        // 获取这个类
        Class<User> userClass = User.class;
        // 获取属性上的泛型,需要先获取到属性
        Field mapField = userClass.getDeclaredField("map"); // 获取公开的以及私有的
        // 获取这个属性上的泛型
        Type genericType = mapField.getGenericType();
        // 用泛型了
        if(genericType instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for(Type a : actualTypeArguments){
                System.out.println(a.getTypeName());
            }
        }
    }
}

运行结果:

4.反射方法参数上和反射方法返回值的泛型

MyClass类:

import java.util.List;
import java.util.Map;

public class MyClass {

    public Map<Integer, Integer> m(List<String> list, List<Integer> list2){
        return null;
    }

}

测试类:

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * 获取方法参数上的泛型信息
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 获取类
        Class<MyClass> myClassClass = MyClass.class;

        // 获取方法
        Method mMethod = myClassClass.getDeclaredMethod("m", List.class, List.class);

        // 获取方法参数上的泛型
        Type[] genericParameterTypes = mMethod.getGenericParameterTypes();
        for(Type g : genericParameterTypes){
            // 如果这个参数使用了泛型
            if(g instanceof ParameterizedType){
                ParameterizedType parameterizedType = (ParameterizedType) g;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                for(Type a : actualTypeArguments){
                    System.out.println(a.getTypeName());
                }
            }
        }

        // 获取方法返回值上的泛型
        Type genericReturnType = mMethod.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for(Type a : actualTypeArguments){
                System.out.println(a.getTypeName());
            }
        }
    }
}

运行结果:

5.反射构造参数的泛型

User类:

import java.util.Map;

public class User {

    public User(Map<String ,Integer> map){
    }

}

测试类:

import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * 获取构造方法参数的泛型
 */
public class Test {
    public static void main(String[] args) throws Exception{
        Class<User> userClass = User.class;
        Constructor<User> con = userClass.getDeclaredConstructor(Map.class);
        Type[] genericParameterTypes = con.getGenericParameterTypes();
        for(Type g :genericParameterTypes){
            if(g instanceof ParameterizedType){
                ParameterizedType parameterizedType = (ParameterizedType) g;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                for(Type a : actualTypeArguments){
                    System.out.println(a.getTypeName());
                }
            }
        }
    }
}

运行结果:

相关文章:

  • vue3表单验证的时候访问接口如果有值就通过否则不通过.主动去触发校验
  • MySQL分库分表之带来查询相关问题
  • 【洛谷贪心算法题】P2240部分背包问题
  • JavaScript遍历方式总结
  • 【AI+智造】用DeepSeek支持设备温度、振动、速度、加速度量化数据的应用方案——以常州新能源动力电池制造企业为例
  • 实践教程:使用DeepSeek实现PDF转Word的高效方案
  • DeepSeek 开源狂欢周(二)DeepEP深度技术解析 | 解锁 MoE 模型并行加速
  • IXI MEGA M1和M2 Plus DAW和跳线盘设置
  • 验证环境中为什么要用virtual interface
  • 【Go】十八、http 调用服务的编写
  • webstorm的Live Edit插件配合chrome扩展程序JetBrains IDE Support实现实时预览html效果
  • 深度剖析设备预测性维护系统有必要吗?
  • Sqlserver安全篇之_启用TLS即配置SQL Server 数据库引擎以加密连接
  • Idea 和 Pycharm 快捷键
  • Docker部署ZLMediaKit流媒体服务器并自定义配置指南
  • Java内存的堆(堆内、堆外)、栈含义理解笔记
  • 实时记录SQL注入靶场心得(labs1-10)
  • WPF-3天快速WPF入门并达到企业级水准
  • Readability.js 与 Newspaper提取网页内容和元数据
  • 四、表关系与复杂查询
  • 复旦一校友捐赠1亿元,却不留名
  • 当“诈骗诱饵”盯上短剧
  • 网警打谣:传播涉刘国梁不实信息,2人被处罚
  • 时隔3年,持续近2小时,俄乌在土耳其谈成了什么?
  • 李伟任山东省委常委、省纪委书记
  • 阿里上财年营收增6%,蒋凡:会积极投资,把更多淘宝用户转变成即时零售用户