2024年最新版零基础详细Java知识笔记【反射】⑩

第十章 反射机制


前言

2024年最新版零基础Java笔记,最全最详细笔记,适用于零基础小白、软件设计师、就业、提升等,笔记会持续更新,共同进步,希望可以帮助更多的小伙伴!

感谢杜老师的超级无敌详细讲解,感谢动力节点,真的受益匪浅!第三次看老杜Java了,每次都有不一样的收获!由于老杜最新版Java没有md笔记,因此鄙人把所学所写的更新于此,共同进步,希望对大家有帮助!

笔记根据老杜《动力节点》视频进行编写,视频地址:动力节点Java零基础视频(下)
笔记有写的不好的地方,恳请在评论区指正批评,谢谢!


10.1 反射机制概述

  1. 反射机制是JDK中的一套类库,这套类库可以帮助我们操作/读写class字节码文件。
  2. Java反射机制是指在运行时动态获取类的信息动态调用对象的方法、修改属性等操作。
  3. 主要核心就是Class类、Constructor类、Field类、Method类等API。
  4. 反射机制主要应用于框架开发、动态代理、ORM框架、JDBC驱动等方面。
  5. 通过反射机制,程序员能够获得在编译期间不被知晓的类、属性、方法等信息。但是反射机制的性能较低,常常被认为是一种牺牲性能换取灵活性的实现方式。
  6. Java反射机制核心包:java.lang.reflect.*
  7. Java反射机制核心类:
    • java.lang.Class:Class类型的实例代表硬盘上的某个class文件。或者说代表某一种类型。
    • java.lang.reflect.Field:Filed类型的实例代表类中的属性/字段
    • java.lang.reflect.Method:类型的实例代表类中的方法
    • java.lang.reflect.Constructor:Constructor类型的实例代表类中的构造方法
    • java.lang.reflect.Modifier

10.2 获取Class

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

  1. 调用Object类的getClass()方法
    • 可以通过对象的getClass()方法来获取Class对象,例如:
    Object obj = new Object();
    Class clazz = obj.getClass();
    
    • 注意:这个方法是通过引用去调用的。
  2. 使用“类.class”语法
    可以使用“类.class”语法来获取Class对象,例如:
    Class clazz = Object.class;
  3. 使用Class类的forName()方法
    • 可以使用Class类的forName()方法来获取Class对象,例如:
    • Class clazz = Class.forName("java.lang.Object(完整的全限定类名)");
    • 注意:
      1. 全限定类名是带有包名的
      2. 是lang包下的,java.lang也不能省略
      3. 这个是字符串参数
      4. 如果这个类根本不存在,运行时会报异常:java.lang.ClassNotFoundException
      5. 这个方法的执行会导致类的加载动作的发生

10.3 反射作用的体现

  1. 通过IO流读取属性配置文件,获取类名,再通过反射机制实例化对象。(获取到Class类型的实例之后,可以实例化对象,通过反射机制实例化对象。)

    • 使用newInstance方法实例化对象,底层实现原理:调用User类的无参数构造方法完成了对象的实例化
      • 执行顺序:静态代码块→无参构造→main方法
      • 要使用此方法,必须保证这个类中是存在无参构造方法,如果没有无参数构造方法,则会出现NoSuchMethodException
    public class ReflectTest{
        public static void main(String[]args) throws Exception{
            Class userClass = Class.forName("com.xfanny.javase.reflect");
            User user = (User)userClass.newInstance();
            System.out.println(user);
        }
    }
    
  2. 在属性配置文件中配置类名:classInfo.properties
    className=java.util.Date

    /**
     * 读取属性配置文件,获取类名,通过反射机制实例化对象。
     * 通过这个案例的演示就知道反射机制是灵活的。这个程序可以做到对象的动态创建。
     * 只要修改属性配置文件就可以完成不同对象的实例化。
     */
    public class ReflectTest03 {
        public static void main(String[] args) throws Exception {
            // 资源绑定器
            ResourceBundle bundle = ResourceBundle.getBundle("com.xfanny.javase.reflect.classInfo");
            // 通过key获取value
            String className = bundle.getString("className");
            // 通过反射机制实例化对象
            Class classObj = Class.forName(className);
            // 实例化
            Object obj = classObj.newInstance();
            System.out.println(obj);
        }
    }
    

    config.properties

    className=com.xfanny.javase.reflect.UserService
    methodName=concat
    parameterTypes=java.lang.String,java.lang.String,java.lang.String
    parameterValues=aaa,bbb,ccc
    

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

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

10.4 反射Field

public class ReflectTest07 {
    public static void main(String[] args) throws Exception{
        // 如果不使用反射机制,怎么访问对象的属性?
        Customer customer = new Customer();
        // 修改属性的值(set动作)
        // customer要素一
        // name要素二
        // "张三"要素三
        customer.name = "张三";

        // 读取属性的值(get动作)
        // 读取哪个对象的哪个属性
        System.out.println(customer.name);

        // 如果使用反射机制,怎么访问对象的属性?
        // 获取类
        Class clazz = Class.forName("com.xfanny.javase.reflect.Customer");

        // 获取对应的Field
        Field ageField = clazz.getDeclaredField("age");

        // 调用方法打破封装
        ageField.setAccessible(true);

        // 修改属性的值
        // 给对象属性赋值三要素:给哪个对象 的 哪个属性 赋什么值
        ageField.set(customer, 30);
        // 读取属性的值
        System.out.println("年龄:" + ageField.get(customer));
        // 通过反射机制给name属性赋值,和读取name属性的值
        Field nameField = clazz.getDeclaredField("name");
        // 修改属性name的值
        nameField.set(customer, "李四");
        // 读取属性name的值
        System.out.println(nameField.get(customer));
    }
}

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

className=java.util.Date

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

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

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

10.5 反射Method

反射Method包括两方面:

  1. 一方面:通过反射机制获取Method

    Method method = clazz.getDeclaredMethod("methodName", paramTypes);
    
  2. 另一方面:通过Method调用方法

    Class clazz = MyClass.class;
    

Method method = clazz.getDeclaredMethod(“methodName”, paramTypes);
Object[] args = {arg1, arg2, arg3};
Object result = method.invoke(myObject, args);
```

代码:

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();

        // 通过反射机制调用login方法
        // 获取Class
        Class clazz = Class.forName("com.xfanny.javase.reflect.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);
    }
}

10.6 反射Constructor

反射Constructor包括两方面:

  1. 一方面:通过反射机制获取Constructor
    Constructor constructor2 = clazz.getDeclaredConstructor(paramTypes); 
    
  2. 另一方面:通过Constructor创建对象
    Class clazz = MyClass.class;
    Constructor constructor = clazz.getDeclaredConstructor(paramTypes);
    Object[] args = {arg1, arg2, arg3};
    Object myObject = constructor.newInstance(args);
    

10.7 模拟框架的部分实现

  1. 配置文件中配置如下信息:classInfo.properties
    className=com.xfanny.javase.reflect.UserService
    methodName=login
    parameterTypes=java.lang.String,java.lang.String
    parameterValues=admin,123456
    
  2. 通过反射机制创建对象,调用配置的方法
  3. 本案例的实现类似于Spring框架中部分实现,主要是通过修改配置文件来达到创建不同的对象,调用不同的方法。
public class ReflectTest13 {
    public static void main(String[] args) throws Exception {
        // 读取属性配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("com.xfanny.javase.reflect.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);
    }
}

10.8 类加载及双亲委派机制

10.8.1 类加载过程

  1. 装载(loading)
    • 类加载器负责将类的class文件读入内存,并创建一个java.lang.Class对象
  2. 连接(linking)
    • 验证(Verify)
      • 确保加载类的信息符合JVM规范。
    • 准备(Prepare)
      • 正式为静态变量在方法区中开辟存储空间并设置默认值
      • public static int k = 10; 此时:k会赋值0
      • public static final int f = 10; 此时: f会赋值10
    • 解析(Resolve)
      • 将虚拟机常量池内的符号引用替换为直接引用(地址)的过程。
  3. 初始化(initialization)
    • 静态变量赋值,静态代码块执行

低版本的JDK中类加载器的名字:
启动类加载器:负责加载rt.jar
扩展类加载器:ext/*.jar
系统类加载器:classpath

在这里插入图片描述

10.8.2 获取Class的四种方式

  1. 静态方法
    Class clazz = Class.forName(“全限定类名”)
  2. 实例方法
    Class clazz = 引用.getClass();
  3. class属性
    Class clazz = 类型名.class;
  4. 通过类加载器获取
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    

Class clazz = classLoader.loadClass(“全限定类名”);
```

Class.forName和classLoader.loadClass()的区别?

  • Class.forName():类加载时会进行初始化。
  • classLoader.loadClass():类加载时不会进行初始化,直到第一次使用该类。

10.8.3 类加载器

  1. 虚拟机内部提供了三种类加载器(Java9+):
    • 启动类加载器(BootstrapClassLoader):加载Java最核心的类,例如String
    • 平台类加载器(PlatformClassLoader):加载Java平台扩展的类库,例如解析XML的
    • 应用类加载器(AppClassLoader):加载classpath中的
    • 同时我们还可以自定义一个类加载器(UserClassLoader)
  2. 获取类加载器可以通过 getParent()方法一级一级获取
    在这里插入图片描述

10.8.4 双亲委派机制

  1. 某个类加载器接收到加载类的任务时,通常委托给“父 类加载”完成加载。
  2. 最“父 类加载器”无法加载时,一级一级向下委托加载任务。
  3. 作用:
    1. 保护程序的安全。
    2. 防止类加载重复。
      在这里插入图片描述

10.9 反射泛型(了解)

  1. 反射父类的泛型
  2. 反射接口的泛型
  3. 反射属性上的泛型
  4. 反射方法参数上的泛型
  5. 反射方法返回值的泛型
  6. 反射构造方法参数上的泛型
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值