Java基础教程(13)-Java中的反射和动态代理

简介: 【4月更文挑战第13天】Java反射机制允许程序在运行时获取类的信息并调用其方法。Class类是基础,提供获取类属性和方法的能力。通过Class对象,可以操作实例字段和方法,如getField、getDeclaredField等。动态代理是Java提供的创建接口实例的机制,其中JDK动态代理需目标类实现接口,而Cglib则可代理未实现接口的类。动态代理涉及Proxy和InvocationHandler接口。

反射

什么是反射?

反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。

在 java 中,只要给定类的名字,那么就可以通过反射机制来获得类的所有属性和方法。

反射有什么作用:

  • 在运行时判断任意一个对象所属的类。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时任意调用一个对象的方法。
  • 在运行时构造任意一个类的对象

Class 类

Java 的 Class 类是 java 反射机制的基础,通过 Class 类我们可以获得关于一个类的相关信息。

Java.lang.Class 是一个比较特殊的类,它用于封装被装入到 JVM 中的类(包括类和接口)的信息。当一个类或接口被装入的 JVM 时便会产生一个与之关联的 java.lang. Class 对象,可以通过这个Class 对象对被装入类的详细信息进行访问。

虚拟机为每种类型管理一个独一无二的 Class 对象。也就是说,每个类(型)都有一个 Class 对象。运行程序时,Java 虚拟机(JVM)首先检查是否所要加载的类对应的Class 对象是否已经加载。如果没有加载,JVM 就会根据类名查找.class 文件,并将其Class 对象载入。

JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载

以 String 类为例,当JVM加载 String 类时,它首先读取String.class 文件到内存,然后,为 String 类创建一个 Class 实例并关联起来; 这个 Class 实例是JVM内部创建的, Class 类的构造方法
是 private ,只有JVM能创建 Class 实例,我们自己的Java程序是无法创建 Class 实例的;

这种通过 Class 实例获取 class 信息的方法称为反射

获取一个 class 的 Class 实例有三个方法:

  • 方法一:直接通过一个 class 的静态变量 class 获取
  • 通过实例变量提供的 getClass() 方法获取;
  • 如果知道一个 class 的完整类名,可以通过静态方法 Class.forName() 获取;

操作实例的字段和方法

Class 类提供了以下几个方法来获取字段:

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

Field 对象包含了一个字段的所有信息:

  • getName() :返回字段名称
  • getType() :返回字段类型,也是一个 Class 实例 ;
  • getModifiers() :返回字段的修饰符,它是一个 int ,不同的bit表示不同的含义。
package com.demo;

import java.lang.reflect.Field;

public class ReflectionDemo {
   

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException {
   
        Box box = new Box();
        Class<Box> boxClass = Box.class;
        Class box_class = box.getClass();
        box_class = Class.forName("com.demo.Box");

        Field[] fields = box_class.getDeclaredFields();
        for (Field f : fields) {
   
            System.out.println(f.getName());
            System.out.println(f.getType());
            System.out.println(f.getModifiers());
            f.setAccessible(true); // 调用 Field.setAccessible(true) 的意思是,别管这个字段是不是 public ,一律允许访问
            System.out.println(f.get(box)); //,用 Field.get(Object) 获取指定实例的指定字段的值
            f.set(box,111); // 设置字段值

        }
    }
}

Class 类提供了以下几个方法来获取 Method :

  • Method getMethod(name, Class…) :获取某个 public 的 Method (包括父类)
  • Method getDeclaredMethod(name, Class…) :获取当前类的某个 Method (不包括父类)
  • Method[] getMethods() :获取所有 public 的 Method (包括父类)
  • Method[] getDeclaredMethods() :获取当前类的所有 Method (不包括父类)

一个 Method 对象包含一个方法的所有信息:

  • getName() :返回方法名称,例如: "getScore" ;
  • getReturnType() :返回方法返回值类型,也是一个Class实例,例如: String.class ;
  • getParameterTypes() :返回方法的参数类型,是一个Class数组,例如: {String.class,int.class} ;
  • getModifiers() :返回方法的修饰符,它是一个 int ,不同的bit表示不同的含义。
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
   
        Box box = new Box();
        Class<Box> boxClass = Box.class;
        Class box_class = box.getClass();
        box_class = Class.forName("com.demo.Box");

        Method[] methods = boxClass.getDeclaredMethods();
        for (Method method: methods) {
   
            System.out.println(method.getName());
            System.out.println(method.getReturnType());
            System.out.println(method.getParameterTypes());
        }
        Method m = boxClass.getMethod("getWidth",Integer.class);
        m.setAccessible(true); // 调用非public方法,我们通过 Method.setAccessible(true) 允许其调用
        int re = (int)m.invoke(box_class,3); //调用非静态方法;
        Method m_static = boxClass.getMethod("getWidth",Integer.class,Integer.class);
        // 调用静态方法时,由于无需指定实例对象,所以 invoke 方法传入的第一个参数永远为 null 。
        int result = (int) m.invoke(null,1,2);


    }

其他方法

如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法;

调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。

通过Class实例获取Constructor的方法如下:

  • getConstructor(Class…) :获取某个 public 的 Constructor
  • getDeclaredConstructor(Class…) :获取某个 Constructor
  • getConstructors() :获取所有 public 的 Constructor
  • getDeclaredConstructors() :获取所有 Constructor

Constructor 总是当前类定义的构造方法,和父类无关

通过 Class 对象可以获取继承关系:

  • Class getSuperclass() :获取父类类型;
  • Class[] getInterfaces() :获取当前类实现的所有接口。
  • 通过 Class 对象的 isAssignableFrom() 方法可以判断一个向上转型是否可以实现

动态代理

什么是动态代理

JDK提供的动态创建接口对象的方式,就叫动态代理。
Java标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创
建某个 interface 的实例。

在运行期动态创建一个 interface 实例的方法如下:

  • 定义一个 InvocationHandler 实例,它负责实现接口的方法调用;
  • 通过 Proxy.newProxyInstance() 创建 interface 实例,它需要3个参数:

    使用的 ClassLoader ,通常就是接口类的 ClassLoader ;
    需要实现的接口数组,至少需要传入一个接口进去;
    用来处理接口方法调用的 InvocationHandler 实例。

  • 将返回的 Object 强制转型为接口。

反射是动态代理的一种实现方式

Java 中,实现动态代理有两种方式:

  • 1、JDK 动态代理:java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口提供了生成动态代理类的能力。
  • 2、Cglib 动态代理:Cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。

区别:

JDK 的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用 CGLIB实现。

Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 Java 类与实现 Java
接口。它广泛的被许多 AOP 的框架使用,例如 Spring AOP 和 dynaop,为他们提供方
法的 interception(拦截)

Java 实现动态代理主要涉及哪几个类

  • java.lang.reflect.Proxy: 这是生成代理类的主类,通过 Proxy 类生成的代理类都继承了 Proxy 类,即 DynamicProxyClass extends Proxy。
  • java.lang.reflect.InvocationHandler: 这里称他为"调用处理器",他是一个接口,我们动态生成的代理类需要完成的具体内容需要自己定义一个类,而这个类必须实现InvocationHandler 接口

Spring AOP 中的动态代理主要有两种方式,JDK 动态代理和 CGLIB 动态代理。JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK 动态代理的核心是 InvocationHandler 接口和 Proxy 类。如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。

相关文章
|
11天前
|
消息中间件 Java 数据库
自研Java框架 Sunrays-Framework使用教程「博客之星」
### Sunrays-Framework:助力高效开发的Java微服务框架 **Sunrays-Framework** 是一款基于 Spring Boot 构建的高效微服务开发框架,深度融合了 Spring Cloud 生态中的核心技术组件。它旨在简化数据访问、缓存管理、消息队列、文件存储等常见开发任务,帮助开发者快速构建高质量的企业级应用。 #### 核心功能 - **MyBatis-Plus**:简化数据访问层开发,提供强大的 CRUD 操作和分页功能。 - **Redis**:实现高性能缓存和分布式锁,提升系统响应速度。 - **RabbitMQ**:可靠的消息队列支持,适用于异步
自研Java框架 Sunrays-Framework使用教程「博客之星」
|
12天前
|
Java 数据库连接 数据处理
探究Java异常处理【保姆级教程】
Java 异常处理是确保程序稳健运行的关键机制。它通过捕获和处理运行时错误,避免程序崩溃。Java 的异常体系以 `Throwable` 为基础,分为 `Error` 和 `Exception`。前者表示严重错误,后者可细分为受检和非受检异常。常见的异常处理方式包括 `try-catch-finally`、`throws` 和 `throw` 关键字。此外,还可以自定义异常类以满足特定需求。最佳实践包括捕获具体异常、合理使用 `finally` 块和谨慎抛出异常。掌握这些技巧能显著提升程序的健壮性和可靠性。
33 4
|
12天前
|
存储 移动开发 算法
【潜意识Java】Java基础教程:从零开始的学习之旅
本文介绍了 Java 编程语言的基础知识,涵盖从简介、程序结构到面向对象编程的核心概念。首先,Java 是一种高级、跨平台的面向对象语言,支持“一次编写,到处运行”。接着,文章详细讲解了 Java 程序的基本结构,包括包声明、导入语句、类声明和 main 方法。随后,深入探讨了基础语法,如数据类型、变量、控制结构、方法和数组。此外,还介绍了面向对象编程的关键概念,例如类与对象、继承和多态。最后,针对常见的编程错误提供了调试技巧,并总结了学习 Java 的重要性和方法。适合初学者逐步掌握 Java 编程。
34 1
|
2月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
2月前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
158 26
|
12天前
|
前端开发 Java 开发工具
Git使用教程-将idea本地Java等文件配置到gitte上【保姆级教程】
本内容详细介绍了使用Git进行版本控制的全过程,涵盖从本地仓库创建到远程仓库配置,以及最终推送代码至远程仓库的步骤。
22 0
|
2月前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
4月前
|
Java Spring 数据库连接
[Java]代理模式
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
66 0
[Java]代理模式
|
8月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
39 0
|
8月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
46 0