Java网络编程——Java语言的反射机制

在Java运行环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自Java语言的反射(Reflection)机制。Java反射机制主要提供了以下功能:

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

1、Java Reflection API简介

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中。

  • Class类:代表一个类。
  • Field类:代表类的成员变量(成员变量也称为类的属性)。
  • Method类:代表类的方法。
  • Constructor类:代表类的构造方法。·Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

下面的DumpMethods类演示了Reflection API的基本作用,它读取命令行参数指定的类名,然后打印这个类所具有的方法信息:

import java.lang.reflect.Method;

/**
 * @title DumpMethods
 * @description 测试
 * @author: yangyongbing
 */
public class DumpMethods {
    public static void main(String[] args) throws ClassNotFoundException {
        // 加载并初始化命令行参数指定的类
        Class<?> classType = Class.forName(args[0]);
        // 获得类的所有方法
        Method[] methods = classType.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println(methods[i].toString());
        }
    }
}

运行命令“java DumpMethods java.util.Stack”,就会显示java.util.Stack类所具有的方法,程序的打印结果如下:
在这里插入图片描述
下面的ReflectTester类进一步演示了Reflection API的基本使用方法。ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回。这个例子只能复制简单的JavaBean,假定JavaBean的每个属性都有public类型的getXXX()和setXXX()方法。

package com.yang.springframework.net;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @title ReflectTester
 * @description 测试
 * @author: yangyongbing
 */
public class ReflectTester {
    public Object copy(Object object) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 获得对象的类型
        Class<?> classType = object.getClass();
        System.out.println("Class:" + classType.getName());

        // 通过默认构造方法创建一个新的对象
        Object objectCopy = classType
                .getConstructor(new Class[]{})
                .newInstance(new Object() {});
        // 获得对象的所有属性
        Field[] fields = classType.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            String fieldName = field.getName();
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            // 获得和属性对应的getXXX()方法的名字
            String getMethodName = "get" + firstLetter + fieldName.substring(1);
            // 获得和属性对应的setXXX()方法的名字
            String setMethodName = "set" + firstLetter + fieldName.substring(1);
            // 获得和属性对应的getXXX()方法
            Method getMethod = classType.getMethod(getMethodName, new Class[]{});
            // 获得和属性对应的setXXX()方法
            Method setMethod = classType.getMethod(setMethodName, new Class[]{});
            // 调用原对象的getXXX()方法
            Object value = getMethod.invoke(object, new Object());
            System.out.println(fieldName + ":" + value);
            // 调用拷贝对象的setXXX()方法
            setMethod.invoke(objectCopy, new Object[]{value});
        }
        return objectCopy;
    }

    public static void main(String[] args) throws Exception {
        Customer customer = new Customer("Tom", 21);
        customer.setId(Long.valueOf(1));
        Customer customerCopy = (Customer) new ReflectTester().copy(customer);
        System.out.println("Copy information:" + customerCopy.getName() + " " + customerCopy.getAge());
    }

}

// Customer类是一个JavaBean
class Customer {
    private Long id;
    private String name;
    private int age;

    public Customer() {
    }

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

    public Long getId() {
        return id;
    }

    public void setId(Long 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;
    }
}

ReflectTester类的copy(Object object)方法依次执行以下步骤:
(1)获得对象的类型:

        Class<?> classType = object.getClass();
        System.out.println("Class:" + classType.getName());

在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API中的核心类,它有以下方法:

  • getName():获得类的完整名字。
  • getFields():获得类的所有public类型的属性。
  • getDeclaredFields():获得类的所有属性。
  • getMethods():获得类的所有public类型的方法。
  • getDeclaredMethods():获得类的所有方法。
  • getMethod(String name,Class<?>…parameterTypes):获得类的public类型的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
  • getDeclaredMethod(String name,Class<?>…parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
  • getConstrutors():获得类的所有public类型的构造方法。
  • getDeclaredConstrutors():获得类的所有构造方法。
  • getConstrutor(Class<?>…parameterTypes):获得类的public类型的特定构造方法,parameterTypes参数指定构造方法的参数类型。
  • getDeclaredConstructor(Class<?>…parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。

(2)通过默认构造方法创建一个新的对象:

        Object objectCopy = classType
                .getConstructor(new Class[]{})
                .newInstance(new Object() {});

以上代码先调用Class类的getConstructor()方法获得一个Constructor对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。

(3)获得对象的所有属性:

Field[] fields = classType.getDeclaredFields();

Class类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性。

(4)获得每个属性相应的getXXX()和setXXX()方法,然后执行这些方法,把原来对象的属性拷贝到新的对象中:

        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            String fieldName = field.getName();
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            // 获得和属性对应的getXXX()方法的名字
            String getMethodName = "get" + firstLetter + fieldName.substring(1);
            // 获得和属性对应的setXXX()方法的名字
            String setMethodName = "set" + firstLetter + fieldName.substring(1);
            // 获得和属性对应的getXXX()方法
            Method getMethod = classType.getMethod(getMethodName, new Class[]{});
            // 获得和属性对应的setXXX()方法
            Method setMethod = classType.getMethod(setMethodName, new Class[]{});
            // 调用原对象的getXXX()方法
            Object value = getMethod.invoke(object, new Object());
            System.out.println(fieldName + ":" + value);
            // 调用拷贝对象的setXXX()方法
            setMethod.invoke(objectCopy, new Object[]{value});
        }

以上代码假定每个属性都有相应的getXXX()和setXXX()方法,并且在方法名中,“get”和“set”的后面一个字母为大写。例如Customer类的name属性对应getName()和setName()方法。Method类的invoke(Object obj,Object args[])方法用于动态执行一个对象的特定方法,它的第1个obj参数指定具有该方法的对象,第2个args参数指定向该方法传递的参数。

下面的InvokeTester类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法:

import java.lang.reflect.Method;

/**
 * @title InvokeTester
 * @description 测试
 * @author: yangyongbing
 */
public class InvokeTester {
    public int add(int param1,int param2){
        return param1+param2;
    }

    public String echo(String msg){
        return "echo:"+msg;
    }

    public static void main(String[] args) throws Exception {
        Class<InvokeTester> classType = InvokeTester.class;
        InvokeTester invokeTester = classType.getConstructor().newInstance();

        // 调用InvokeTester对象的add()方法
        Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});

        Object result = addMethod.invoke(invokeTester, new Object[]{Integer.valueOf(100), Integer.valueOf(200)});
        System.out.println(result);

        // 调用InvokeTester对象的echo()方法
        Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
        result = echoMethod.invoke(invokeTester, new Object[]{"Hello"});
        System.out.println(result);
    }
}

在这里插入图片描述

2、在远程方法调用中运用反射机制

假定在SimpleServer服务器端创建了一个HelloServiceImpl对象,它具有getTime()和echo()方法。HelloServiceImpl类实现了HelloService接口。下面分别是HelloService接口和HelloServiceImpl类的源程序。
在这里插入图片描述
在这里插入图片描述
SimpleClient客户端如何调用服务器端的HelloServiceImpl对象的getTime()和echo()方法呢?显然,SimpleClient客户端需要把调用的方法名、方法参数类型、方法参数值,以及方法所属的类名或接口名发送给SimpleServer,SimpleServer再调用相关对象的方法,然后把方法的返回值发送给SimpleClient。

为了便于按照面向对象的方式来处理客户端与服务器端的通信,可以把它们发送的信息用Call类来表示:
在这里插入图片描述

一个Call对象表示客户端发起的一个远程调用,它包括调用的类名或接口名、方法名、方法参数类型、方法参数值和方法执行结果。

SimpleClient调用SimpleServer端的HelloServiceImpl对象的echo()方法的流程如下:

  • (1)SimpleClient创建一个Call对象,它包含了调用HelloService接口的echo()方法的信息。
  • (2)SimpleClient通过对象输出流把Call对象发送给SimpleServer。
  • (3)SimpleServer通过对象输入流读取Call对象,运用反射机制调用HelloServiceImpl对象的echo()方法,把echo()方法的执行结果保存到Call对象中。
  • (4)SimpleServer通过对象输出流把包含了方法执行结果的Call对象发送给SimpleClient。
  • (5)SimpleClient通过对象输入流读取Call对象,从中获得方法执行结果。

下面分别是SimpleServer和SimpleClient的源程序:
在这里插入图片描述

在这里插入图片描述
先运行命令“java remotecall.SimpleServer”,再运行命令“java remotecall.SimpleClient”,SimpleClient端将打印“echo:Hello”。该打印结果是服务器端执行HelloServiceImpl对象的echo()方法的返回值。下图显示了SimpleClient与SimpleServer的通信过程。
在这里插入图片描述

3、代理模式

在日常生活中,常常会遇到各种各样的代理。例如小王是一个公司的老板,他请秘书小张代他去房产公司寻找合适的办公楼,小张会把需要租用的办公楼的具体需求告诉房产公司。秘书小张是老板小王的代理,而房产公司是受委托的公司。

代理模式是常用的Java设计模式,它的特征是:代理类与委托类有同样的接口,如下图所示:
在这里插入图片描述

代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

按照代理类的创建时期,代理类可分为两种:

  • 静态代理类:由开发人员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
  • 动态代理类:在程序运行时,运用反射机制动态创建而成。

4、总结

Java反射机制是Java语言的一个重要特性。考虑实现一个newInstance(String className)方法,它的作用是根据参数className指定的类名,通过该类的不带参数的构造方法创建这个类的对象,并将其返回。如果不运用Java反射机制,那么必须在newInstance()方法中罗列参数className所有可能的取值,然后创建相应的对象。
在这里插入图片描述
以上程序代码很冗长,而且可维护性较差。如果在以后软件的升级版本中去除了一个HelloService4类,或者增加了一个HelloService1001类,就需要修改以上newInstance()方法。

如果运用反射机制,就可以简化程序代码,并且能提高软件系统的可维护性和可扩展性:
在这里插入图片描述
Java反射机制在服务器程序和中间件程序中得到了广泛的运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,有一种对象—关系映射(Object-Relation Mapping,ORM)中间件能够把任意一个JavaBean持久化到关系数据库中。在ORM中间件的实现中,运用Java反射机制来读取任意一个JavaBean的所有属性,或者给这些属性赋值。

在JDK类库中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:

  • Class类:代表一个类。·Field类:代表类的属性。
  • Method类:代表类的方法。
  • Constructor类:代表类的构造方法。
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
  • Proxy类和InvocationHandler接口:提供了生成动态代理类及其实例的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/229112.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ESP32 IDF】ESP32移植u8g2库,实现oled界面显示

ESP32移植u8g2库&#xff0c;实现oled界面显示 1. 简单描述2. 环境准备1. 硬件准备2. 软件准备 3. IIC屏幕 【基于 ssd1362 256*64 】4. SPI屏幕 【基于 ssd1306 128*32】 1. 简单描述 开发方式为 IDF5.0参考连接为 iic基于esp-idf移植使用u8g2 spi基于esp-idf移植使用u8g2 …

机器学习第15天:GBDT模型

☁️主页 Nowl &#x1f525;专栏《机器学习实战》 《机器学习》 &#x1f4d1;君子坐而论道&#xff0c;少年起而行之 ​​ 文章目录 GBDT模型介绍 Boosting 残差 GBDT的缺点 python代码实现 代码 模型参数解释 结语 GBDT模型介绍 GBDT&#xff08;Gradient Boos…

一度超越Sketch用户的Figma真的好用吗?

Figma 是一个可以在所有平台上使用的软件&#xff0c;和 Sketch 功能差不多&#xff0c;但不像Sketch一样只能在Mac上使用。Figma 可以在 Windows PC&#xff0c;Linux 计算机甚至 Chromebook&#xff0c;目前 iPad 也可以使用。只要你能打开网页&#xff0c;理论上你基本上可以…

【FPGA】Verilog:BCD 加法器的实现

0x00 XOR 运算在 2 的补码加减法中的应用 2 的补码加减法的特点是&#xff0c;当从某个数中减去负数时&#xff0c;将其转换为正数的加法来计算&#xff0c;并将减去正数的情况转换为负数的加法来计算&#xff0c;从而将所有减法运算转换为加法运算。在这种情况下&#xff0c;…

打工人副业变现秘籍,某多/某手变现底层引擎-StableDiffusionWebUI界面基本布局和操作

一、界面设置 文生图:根据文本提示生成图像 图生图:图像生成图像;功能很强大,自己在后续使用中探索。 后期处理:图片处理;功能很强大,自己在后续使用中探索。 PNG信息:这是一个快速获取图片生成参数的便捷功能。如果图像是在SD里生成的,您可以使用“发送到”按钮将…

大创项目推荐 交通目标检测-行人车辆检测流量计数 - 大创项目推荐

文章目录 0 前言1\. 目标检测概况1.1 什么是目标检测&#xff1f;1.2 发展阶段 2\. 行人检测2.1 行人检测简介2.2 行人检测技术难点2.3 行人检测实现效果2.4 关键代码-训练过程 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 毕业设计…

RocketMQ安装和使用

RocketMQ快速入门 下载RocketMQ 下载地址 环境要求 Linux64位系统 JDK1.8(64位) 安装RocketMQ 解压 unzip rocketmq-all-4.4.0-bin-release.zip启动RocketMQ 启动NameServer # 1.启动NameServer nohup sh bin/mqnamesrv & # 2.查看启动日志 tail -f ~/logs/rocke…

混合预编码(Hybrid Precoding)的全连接结构与子连接结构

A Survey on Hybrid Beamforming Techniques in 5G: Architecture and System Model Perspectives 全连接结构的混合预编码 子连接结构的混合预编码 Alternating Minimization Algorithms for HybridPrecoding in Millimeter Wave MIMO Systems

WGCLOUD v3.5.0 新增支持监测交换机的接口状态UP DOWN

WGCLOUD v3.5.0开始 可以监测交换机或SNMP设备的接口状态了&#xff0c;直接上图

奕碳科技亮相COP28:展现中国智慧,引领全球碳减排新篇章

11月30日,联合国气候变化框架公约第28次缔约方大会 (COP28) 在阿联酋迪拜开幕。COP28是全球气候治理的重要盛会&#xff0c;汇聚了世界各国领导人、企业界和科学界代表&#xff0c;共同探讨和制定应对全球气候变化的策略与行动计划。在这样的背景下&#xff0c;企业群体的积极参…

初识人工智能,一文读懂过拟合欠拟合和模型压缩的知识文集(3)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

前端自动化测试Vue中TDD和单元测试示例详解

1、简单用例入门 Vue 提供了 vue/test-utils 来帮助我们进行单元测试&#xff0c;创建 Vue 项目的时候勾选测试选项会自动帮我们安装 先来介绍两个常用的挂载方法&#xff1a; mount&#xff1a;会将组件以及组件包含的子组件都进行挂载shallowMount&#xff1a;浅挂载&…

潮落云起:中国云桌面的产业变局

云桌面&#xff0c;又被称为桌面云、桌面虚拟化技术。这项技术的起源可以追溯到20世纪70年代&#xff0c;IBM通过一台计算机来实现多用户资源的桌面共享。在数十年的发展中&#xff0c;云桌面从技术实现方式到产品形态日趋丰富。 相比于传统PC数据单独存放、系统独立运维的分散…

2023年12月8日:UI登陆界面

作业 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMovie> #include <QPushButton> #include <QDebug>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpub…

DeepIn,UOS统信专业版安装运行Java,JavaFx程序

因为要适配国产统信UOS系统&#xff0c;要求JavaFx程序能简便双击运行&#xff0c;由于网上UOS开发相关文章少&#xff0c;多数文章没用&#xff0c;因此花了不少时间&#xff0c;踩了不少坑&#xff0c;下面记录一些遇到的问题&#xff0c;我的程序环境是jdk1.8&#xff0c;为…

vue项目新增弹窗打开时:其中邮箱和密码被默认设置为当前登录的账号和密码

解决方法&#xff1a;就是在input的前后分别再添加一个input&#xff08;注意截图顺序不能错{密码放前面、邮箱放后面}&#xff09;

2024最新金三银四软件测试面试题

一直以来大大小小参与过不少面试&#xff0c;遇到过不少坑&#xff0c;但是没来的及好好总结汇总下。现在把之前遇到的问题汇总下&#xff0c;希望以后自己能加深印象。 1、appium 怎么定位toast弹框 appium1.6以后回答需要升级u2进行定位。 2、什么是事务&#xff0c;知道事…

Filed II 绘制超声 3D/2D 点扩散函数

点扩散函数可以较好地描述超声对成像目标分辨能力,利用 filed II 仿真工具实现点扩算函数 PSF 的 3D 和 2D 绘制。 定义换能器基本参数 f0=5e6; % Transducer center frequency [Hz] fs=100e6; % Sampling frequency [Hz] c=1540; % Speed of sound [m/s] width=0.15/1000

MYSQL练题笔记-高级查询和连接-这系列最后一题以及下个系列(子查询)的第一题

今天做了下面两题&#xff0c;到第三题的时候想了下但是没有太多的思路&#xff0c;然后看题解的时候实在是觉得自己不会&#xff0c;打算明天看吧。 1.按分类统计薪水相关的表和题目如下 我是想着简化问题&#xff0c;先找出薪水低于30000的员工&#xff0c;然后找这些员工的上…

Anaconda建虚拟环境并在jupyter中打开

1.假设要用yaml格式创建虚拟环境 从开始里打开anaconda powersheel 输入以下 conda env create -f environment.yaml conda activate env_name activate以下虚拟环境 修改名称 如果不用yaml也可以用 conda create --name my_first_env python3.6 这个来指定 2.(base)变(…
最新文章