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.rhkb.cn/news/213081.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vuepress-----18、图片缩放

图片引入两种方式 地址 # 图片缩放插件 # 实战 md文件引入图片 <img class"zoom-custom-imgs" :src"$withBase(/favicon.ico)" alt"favicon">安装配置插件 vuepress/medium-zoom: {selector: img.zoom-custom-imgs,},效果展示

Spring框架学习:Bean生命周期

目录 SpringBean的生命周期 Bean实例属性填充 三级缓存 常用的Aware接口 Spring IoC容器实例化Bean总结 SpringBean的生命周期 Spring Bean的生命周期是从 Bean 实例化之后&#xff0c;即通过反射创建出对象之后&#xff0c;到Bean成为一个完整对象&#xff0c;最终存储到…

Termux+Hexo结合内网穿透轻松实现安卓手机搭建博客网站发布公网访问

文章目录 前言 1.安装 Hexo2.安装cpolar3.远程访问4.固定公网地址 前言 Hexo 是一个用 Nodejs 编写的快速、简洁且高效的博客框架。Hexo 使用 Markdown 解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。 下面介绍在Termux中安装个人hexo博客并结合…

视频剪辑:视频转码实用技巧,批量将MP4转为MP3音频

随着数字媒体设备的普及&#xff0c;视频和音频文件已成为日常生活中的重要组成部分。有时&#xff0c;可能要将MP4视频文件转换为MP3音频文件&#xff0c;以提取其中的音频内容或者进行其他处理。这是耗费时间的任务&#xff0c;那要如何操作呢&#xff1f;本文详解云炫AI智剪…

一文理解什么是交叉熵损失函数以及它的作用

今天看一个在深度学习中很枯燥但很重要的概念——交叉熵损失函数。 作为一种损失函数&#xff0c;它的重要作用便是可以将“预测值”和“真实值(标签)”进行对比&#xff0c;从而输出 loss 值&#xff0c;直到 loss 值收敛&#xff0c;可以认为神经网络模型训练完成。 那么这…

进制 + 原码,反码,补码

进制转换 整数部分 小数部分 原码 反码 补码 原码转补码&#xff1a; 左边和右边第一个1不变&#xff0c;中间取反。-0 除外。 计算机系统中数值一律用补码来存储的原因 其他 术语 进制表 进制数的表示 详细教程可转 爱编程的大丙

SpringDataRedis 操作 Redis,并指定数据序列化器

文章目录 1. SpringDataRedis 概述2. 快速入门2.1 导入pom坐标2.2 配置文件2.3 测试代码2.4 数据序列化器2.5 StringRedisTemplate2.6 总结 1. SpringDataRedis 概述 SpringData 是Spring 中数据操作的模块&#xff0c;包含对各种数据库的集成&#xff0c;其中对Redis的集成模…

在pytorch中自定义dataset读取数据

这篇是我对哔哩哔哩up主 霹雳吧啦Wz 的视频的文字版学习笔记 感谢他对知识的分享 有关我们数据读取预训练 以及如何将它打包成一个一个batch输入我们的网络的 首先我们来看一下之前我们在讲resnet网络时所使用的源码 我们去使用了官方实现的image folder去读取我们的图像数据 然…

html实现各种好看的鼠标滑过图片特效模板

文章目录 1.鼠标悬浮效果1.1 渐动效果1.2 渐变效果1.3 边框效果1.4 线行效果1.5 图标效果1.6 块状效果1.7 边线效果1.8 放大效果1.9 渐出效果1.10 痕迹效果1.11 交叉效果1.12 着重效果1.13 详展效果1.14 能动效果1.15 明细效果 2.主要源码2.1 源代码 源码下载 作者&#xff1a;…

linux后端基础---笔记整理(tmux、vim、shell、ssh/scp、git、thrift、docker)

目录 1.Linux常用文件管理命令 2.tmux终端复用器/vim命令式文本编辑器 3.Shell语法 3.1 Shell—版本3.2 新建一个test.sh文件3.3 Shell文件—运行方式3.4 Shell—注释3.5 Shell—变量3.6 Shell—默认变量&#xff0c;文件参数, “$”的用法3.7 Shell—数组3.8 shell—expr命令…

今日问题:解决最新Chrome和chromedriver版本对不上的问题

from selenium import webdriver #from .chrome.webdriver import WebDriver as Chrome from selenium.webdriver.common.by import By from time import sleep driver webdriver.Chrome()driver.get("https://www.baidu.com/") driver.maximize_window()#窗口最大化…

SpringBoot + Spring Cloud Alibaba + Nacos实现服务管理

1、参考文档 Spring Cloud Alibaba参考文档 https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/zh-cn/index.html Spring Cloud Alibaba官方文档 https://github.com/alibaba/spring-cloud-alibaba/wiki/ 2、引入 Alibaba 依赖 每个 SpringBoot 都有对应的…

[Firefly-Linux] RK3568 Ubuntu固件分区详解

RK为了方便开发与产品定制&#xff0c;自己定义了一套固件的分区&#xff0c;这些分区信息存放在parameter.txt文件中&#xff0c;Firefly参考这个文件定义了自己的Ubuntu分区&#xff0c;文件为parameter-ubuntu.txt&#xff0c;存放于Linux_SDK的device/rockchip/rk356x目录下…

模电笔记。。。。

模电 2.8 蜂鸣器 按照蜂鸣器驱动方式分为有源蜂鸣器和无源蜂鸣器 有源的有自己的震荡电路&#xff0c;无源的要写代码控制。 里面有个线圈&#xff0c;相当于电感&#xff0c;储能&#xff0c;通直隔交。 蜂鸣器的参数&#xff1a;额定电压&#xff0c;工作电压&#xff0…

外贸辅助工具定制的价格范围,别被坑了哟!

随着全球化的不断发展&#xff0c;外贸已成为企业不可或缺的一部分。然而&#xff0c;在外贸过程中&#xff0c;企业往往会遇到各种问题&#xff0c;如语言障碍、文化差异、法规繁琐等&#xff0c;为了解决这些问题&#xff0c;许多企业选择定制外贸辅助工具。 但是&#xff0…

服务器数据恢复—ocfs2文件系统被格式化为其他文件系统如何恢复数据?

服务器故障&#xff1a; 由于工作人员的误操作&#xff0c;将Ext4文件系统误装入到存储中Ocfs2文件系统数据卷上&#xff0c;导致原Ocfs2文件系统被格式化为Ext4文件系统。 由于Ext4文件系统每隔几百兆就会写入文件系统的原始信息&#xff0c;原Ocfs2文件系统数据会遭受一定程度…

一个简单的postman设置断言,为何会难住一个工作5年的测试?

postman设置断言 作为一款接口测试工 具&#xff0c;postman需要对发送请求后返回的结果是否正确做验证&#xff0c;在postman中通过 tests页签做请求的验证&#xff0c;也称为断言。 postman设置断言的流程 1、在tests页签截取要对比的实际响应信息&#xff08;响应头、响应…

ArkTS快速入门

一、概述 ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript&#xff08;简称TS&#xff09;基本语法风格的基础上&#xff0c;对TS的动态类型特性施加更严格的约束&#xff0c;引入静态类型。同时&#xff0c;提供了声明式UI、状态管理等相应的能力&#xff0c;让开发者可以…

DOS 批处理 (一)

DOS 批处理 1. 批处理是什么&#xff1f;2. DOS和MS-DOS3. 各种操作系统shell的区别Shell 介绍图形用户界面&#xff08;GUI&#xff09;shell命令行界面&#xff08;CLI&#xff09;的 shell命令区别 1. 批处理是什么&#xff1f; 批处理(Batch)&#xff0c;也称为批处理脚本…

VisualStudio反汇编功能使用

VisualStudio反汇编功能使用 使用方法 到达断点时&#xff1a;CtrlK&#xff08;松开&#xff09;G&#xff08;后按&#xff09;唤出反汇编界面&#xff0c;就可以看到当前代码对应的汇编语言了 注意&#xff1a;进入反汇编窗口后可以F10单次调试一条汇编指令 其他&#…