【注解和反射】通过反射动态创建对象、调用普通方法、操作属性

继上一篇博客【注解和反射】获取类运行时结构-CSDN博客

目录

八、通过反射动态创建对象

测试:通过反射动态创建对象

思考:难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器并将参数传递进去之后,才可以实例化操作。

测试

测试:通过反射调用普通方法、通过反射操作属性


八、通过反射动态创建对象

在Java中,通过反射可以动态地创建对象,而不需要在编译时知道要创建的对象的具体类。以下是使用Java反射API创建对象的基本步骤:

  1. 获取Class对象: 首先,需要获取代表想要创建对象的类的Class对象。这可以通过几种方式之一来完成:

    • 使用.class语法,如果类名已知。
    • 使用Class.forName()方法,通过类的全限定名(包括包名)获取Class对象。这在类名在运行时才知道的情况下很有用。
  2. 检查访问权限: 如果该类或其构造函数不是可访问的(即不是public),则需要通过调用setAccessible(true)方法来临时允许访问。请注意,这种做法可能会违反Java的安全性原则,应谨慎使用。

  3. 获取Constructor对象: 接下来,需要获取Constructor对象,该对象表示类的构造函数。这可以通过调用Class对象的getDeclaredConstructor()方法来完成,该方法可以接收构造函数的参数类型作为参数。如果想要获取的是公共构造函数,则可以使用getConstructor()方法。

  4. 创建实例: 一旦获得了Constructor对象,就可以调用它的newInstance()方法来创建类的新实例。如果构造函数需要参数,那么这些参数需要传递给newInstance()方法。

测试:通过反射动态创建对象

创建一个User类,

class User{private String name;private int id;private int age;public User(String name, int id, int age) {this.name = name;this.id = id;this.age = age;}public User() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", id=" + id +", age=" + age +'}';}
}
public class Test06 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {Class c1 = Class.forName("com.itheima.sjms.User");User user = (User) c1.newInstance();System.out.println(user);}
}

从结果中我们可以看到,

(1)com.itheima.sjms.User类存在,并且该类有一个可访问的无参构造函数,那么程序将成功运行。c1.newInstance()方法会调用com.itheima.sjms.User类的无参构造函数来创建一个新的User对象实例。

 System.out.println(user);语句将打印新创建的User对象。默认情况下,这将输出User对象的类名和哈希码的无意义表示(例如com.itheima.sjms.User@15db9742),除非User类重写了toString()方法以提供更有意义的输出。

(2)如果com.itheima.sjms.User类不存在,或者不可访问(例如因为它是私有的或者在不同的包中并且没有公共的构造函数,那么程序将抛出异常。这里添加了NoSuchMethodExceptionInvocationTargetExceptionthrows子句中,因为getDeclaredConstructor()newInstance()方法可能会抛出这些异常。具体来说,

  • ClassNotFoundException将在类不存在时抛出
  • InstantiationException将在类是一个接口或抽象类时抛出
  • IllegalAccessException将在没有适当的访问权限时抛出

由于c1被声明为Class类型而不是Class<User>类型,编译器会发出未经检查的转换警告,因为将c1.newInstance()的结果转换为User类型时没有进行类型检查。为了避免这个警告,可以将c1的声明更改为Class<?> c1Class<User> c1(如果确实知道要加载的类类型)。

另外,从Java 9开始,Class.newInstance()方法已被弃用,因为它无法正确地与Java的新模块化系统一起工作。建议使用Class.getDeclaredConstructor().newInstance()来代替,这样可以更明确地指定要调用的构造函数,并且与Java的模块化系统兼容。

思考:难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器并将参数传递进去之后,才可以实例化操作。

如果目标类(例如com.itheima.sjms.User)没有无参的构造器,而你又想通过反射来创建其实例,

  1. 可以使用Class对象的getDeclaredConstructor方法来指定参数类型并获取相应的构造器。
  2. 向构造器的形参中传递一个对象数组进去里面包含了构造器中所需的各个参数
  3. 通过Constructor实例化对象:可以调用newInstance方法(在Java 8及以前)或者从Java 9开始推荐的Constructor.newInstance的替代方法,invoke,来创建对象实例。

测试

public class Test06 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {Class c1 = Class.forName("com.itheima.sjms.User");Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);User user2 = (User)declaredConstructor.newInstance("user", 1, 10);System.out.println(user2);}
}

在Java 9及以上版本,Constructor.newInstance已被弃用,因为它不支持varargs(可变参数)且可能与新的模块化系统不完全兼容。取而代之的是使用Constructor.newInstance的重载版本,该版本接受一个Class<?>[]参数类型数组和一个Object[]参数值数组,或者直接使用invoke方法。

测试:通过反射调用普通方法、通过反射操作属性

试图通过反射来创建 com.itheima.sjms.User 类的实例,并调用其 setName 方法。

public class Test06 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {Class c1 = Class.forName("com.itheima.sjms.User");User user3 = (User)c1.newInstance();Method setName = c1.getDeclaredMethod("setName", String.class);setName.invoke(user3, "user3");System.out.println(user3);System.out.println("------------");User user4 = (User)c1.newInstance();Field name = c1.getDeclaredField("name");name.setAccessible(true);name.set(user4, "user4");System.out.println(user4.getName());}
}

(1)通过反射调用普通方法步骤是:

  1. 首先使用Class.forName加载User类,并获取其Class对象,用Class对象的newInstance方法创建User类的一个新实例。
  2.  获取User类中声明的setName方法,该方法接受一个String参数
  3. 使用Method对象的invoke方法来调用user3对象的setName方法,并传入"user3"作为参数,
  4. 打印user3对象(这将调用User类的toString方法,如果User类没有重写toString,将打印对象的类名和哈希码)

(2)通过反射操作属性步骤是:

  1. 首先使用Class.forName加载User类,并获取其Class对象,用Class对象的newInstance方法创建User类的一个新实例。
  2. 获取User类中声明的name字段(无论它是公有还是私有)
  3. 设置name字段为可访问,以便能够修改私有字段的值
  4. 使用Field对象的set方法来设置user4对象的name字段的值为"user4"

  5. 调用user4对象的getName方法来获取name字段的值,并打印它

在Java反射API中,Method类的invoke方法允许你动态地调用一个方法。这个方法的签名如下:

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

参数解释:

  • obj:这是方法调用的目标对象。如果这个方法是静态的,那么这个参数可以为null;如果是实例方法,那么这个参数就是包含此方法的实例对象。
  • args:这是一个可变参数,代表调用目标方法时传入的参数列表。参数列表的数据类型和顺序必须与方法签名中定义的完全匹配。如果方法没有参数,那么这个参数可以省略。

invoke方法的返回值是被调用方法的返回值。如果被调用方法没有返回值(即void方法),则invoke返回null

异常:

  • IllegalAccessException:如果这个方法是不可访问的,比如一个私有方法且没有被设置为可访问(通过setAccessible(true))。
  • IllegalArgumentException:如果传入的参数和方法签名不匹配。
  • InvocationTargetException:如果目标方法抛出了异常,那么这个异常会被包装在InvocationTargetException中。

在Java反射API中,setAccessible()方法是AccessibleObject类的一个方法,它被FieldMethodConstructor类继承。这个方法用于设置反射对象(字段、方法或构造函数)的可访问性,允许你访问和修改私有成员,或者调用私有方法。

setAccessible()方法接受一个布尔值参数:

  • 如果参数为true,则指示反射的对象在使用时应该取消Java语言访问检查。这允许你访问任何字段、调用任何方法或实例化任何类,无论它们是否是私有的。
  • 如果参数为false,则反射的对象将进行正常的Java语言访问检查。

使用setAccessible(true)可以带来性能提升,因为它避免了Java安全管理器的安全检查,但这也会带来安全风险,因为它破坏了封装性并可能违反安全策略。因此,在使用setAccessible(true)时应该谨慎,并确保不会引入安全漏洞。

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

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

相关文章

bundle的下载和使用

1.bundle 的概要 BundleBundle 是一个嵌入式压缩库&#xff08;嵌入是指直接嵌入到代码中&#xff0c;可以直接使用无需编译&#xff09;&#xff0c;支持 23 种压缩算法和 2 种存档格式。 2.bundle 的使用 使用的时候只需要手动拷贝添加两个 bundle.h 和 bundle.cpp 文件即…

三维图形程序员必学-CGAL几何算法

GCAL几何算法库,涵盖了很多数学几何算法,矩阵运算、平面拟合、曲线拟合、曲面重建、网格优化、网格剖分、面线相交、布尔运算等等各种图形学几何相关的算法。 文章最后放了一个CGAL求点集拟合平面,投影求线的例子代码。 CGAL是一个开源代码库,官网连接GitHub - CGAL/cgal…

网络安全之弱口令与命令爆破(上篇)(技术进阶)

目录 一&#xff0c;什么是弱口令&#xff1f; 二&#xff0c;为什么会产生弱口令呢&#xff1f; 三&#xff0c;字典的生成 四&#xff0c;使用Burpsuite工具弱口令爆破 总结 一&#xff0c;什么是弱口令&#xff1f; 弱口令就是容易被人们所能猜到的密码呗&#xff0c;…

Linux动态追踪——eBPF

目录 摘要 1 什么是 eBPF 2 eBPF 支持的功能 3 BCC 4 编写脚本 5 总结 6 附 摘要 ftrace 和 perf 与 ebpf 同为 linux 内核提供的动态追踪工具&#xff0c;其中 ftrace 侧重于事件跟踪和内核行为的实时分析&#xff0c;perf 更侧重于性能分析和事件统计&#xff0c;与…

脚本:监控Oracle中正在运行的SQL

这是我自己平时用的一个监控Oracle中正在运行的SQL的脚本&#xff0c;有需要的请收藏&#xff0c;运行时直接复制和粘贴即可。 col inst_sid heading "INST_ID|:SID" format a7 col username format a10 col machine format a12 col sql_exec_start heading "…

vim+xxd 编辑16进制

1. vim -b mib 2. 在vim 中执行 %!xxd, 这样就可以输入16进制&#xff1a; 3. 输入完成后&#xff0c;在vim中 执行 %!xxd -r 切换至原模式&#xff1b; 4. 保存退出即可 5. 重新打开mib文件&#xff1a;vim -b mib 6. 在vim 中执行 %!xxd, 查看是否符合预期&#xff1a;…

c++实战篇(二)——基于自旋锁实现的日志服务模块

前言 日志模块一直是服务端开发比较重要的部分,而在实际应用中向日志中进行写入的操作往往不是单线/进程的&#xff0c;而在多进/线程中如何实现对共享资源的保护&#xff0c;就成了一个比较重要的问题,而在实际开发中我们常常会利用锁机制来实现对共享内存的保护&#xff0c;…

学习java第五十一天

解释不同方式的自动装配 。 有五种自动装配的方式&#xff0c;可以用来指导 Spring 容器用自动装配方式来进行依赖注入。 no&#xff1a;默认的方式是不进行自动装配&#xff0c;通过显式设置 ref 属性来进行装配。 byName&#xff1a;通过参数名 自动装配&#xff0c;Spring 容…

如何理解GDP、国民总收入(GNI)的区别和联系

国内生产总值和国民总收入是衡量一个国家&#xff08;地区&#xff09;经济状况和发展水平的两个重要总量指标。两者既有密切的联系&#xff0c;又有一定区别&#xff0c;用途都非常广泛。 一、GDP与GNI的基本概念 国内生产总值(Gross Domestic Product&#xff0c;GDP)&…

遇到PHP方法 二维数组随机排序shuffle 二维数组筛选搜索array_filter

在做二维数组随机排序的时候&#xff0c;发现shuffle 函数挺好用&#xff0c;就不用在做负载的数组随机排序了&#xff0c;对于二维数组同样可以随机排列 php中的shuffle()函数是一个非常有用的函数&#xff0c;常用于随机重排数组中的元素。这个函数可以在很多开发场景中&…

一个联合均值与方差模型的R包——dglm

目录 一、引言二、包的安装与载入三、模拟例子3.1 数据生成3.2 数据查看3.3 模型估计参数 一、引言 在 R 语言中&#xff0c;dglm 包是用于拟合双参数广义线性模型&#xff08;Double Generalized Linear Models&#xff0c;简称 DGLMs&#xff09;的一个工具。这类模型允许同…

模块三:二分——162.寻找峰值

文章目录 题目描述算法原理解法一&#xff1a;暴力查找解法二&#xff1a;二分查找 代码实现解法一&#xff1a;暴力查找解法二&#xff1a;CJava 题目描述 题目链接&#xff1a;162.寻找峰值 根据题意&#xff0c;需要使用O(log N)的时间复杂度来解决&#xff0c;得出本道题…

.rmallox勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复

导言&#xff1a; 在当今数字化的时代&#xff0c;计算机病毒已成为网络安全的一大威胁。其中&#xff0c;.rmallox勒索病毒以其高度的隐蔽性、快速的传播速度、巨大的危害性和不断变种的特性&#xff0c;引起了广大用户的关注。本文将详细解析.rmallox勒索病毒的特点&#xf…

在美国站群服务器部署时如何保障从253个IP到1000个IP的无缝扩展?

在美国站群服务器部署时如何保障从253个IP到1000个IP的无缝扩展? 在当今企业的数字化转型中&#xff0c;服务器的部署和管理成为了保证业务连续性和拓展性的关键。尤其对于站群服务器来说&#xff0c;随着企业业务的增长和市场的扩展&#xff0c;需要从较小规模的253个IP地址…

AWTK 异形进度条控件发布

异形进度条控件。通过多边形来定义进度条的形状。 代码地址&#xff1a;https://gitee.com/zlgopen/awtk-widget-progress-polygon 特性 通过多边形定义进度条的形状支持通过图片来定义进度条的背景支持通过图片来定义进度条的前景 使用图片填充比使用颜色填充消耗更多的内…

Debezium分享系列之:Debezium2.6稳定版本设置SQL Server数据库

Debezium分享系列之:Debezium2.6稳定版本设置SQL Server数据库 一、在 SQL Server 数据库上启用 CDC二、在 SQL Server 表上启用 CDC三、验证用户是否有权访问 CDC 表四、SQL Server Always On五、SQL Server 捕获作业代理配置对服务器负载和延迟的影响六、SQL Server 捕获作业…

速盾:常见的DDOS攻击类型有哪些?

DDoS&#xff08;分布式拒绝服务&#xff09;攻击是一种通过使目标系统超负荷而使其无法正常工作的网络攻击手段。在这种攻击中&#xff0c;恶意攻击者使用多个主机或设备&#xff08;也被称为僵尸网络或机器人网络&#xff09;来同时向目标系统发送大量的网络流量&#xff0c;…

数据结构练习-线性表定义与基本操作

----------------------------------------------------------------------------------------------------------------------------- 1. 线性表是( )。 A.一个有限序列&#xff0c;可以为空 B. 一个有限序列&#xff0c;不可以为空 C. 一个无限序列&#xff0c;可以为空…

第三次国土调查数据库字母缩写代表含义

CCWJQ拆除未尽区&#xff0c;CJDCQ村界调查区&#xff0c;CJDCQJX村界调查区界线&#xff0c;CLKZD测量控制点&#xff0c;CSKFBJ城市开发边界&#xff0c;CZCDYD城镇村等用地&#xff0c;DGX等高线&#xff0c;DLTB地类图斑&#xff0c;DZGY地质公园&#xff0c;FJMSQ风景名胜…

【一般排查思路】针对银河麒麟高级服务器操作系统磁盘空间已满

1. 本身磁盘空间已满 有时候我们会看到服务器上有提示“设备上没有空间”&#xff0c;如图1。 图 1 如果是磁盘本身空间已满&#xff0c;我们可以借助du工具来排查&#xff0c;比如首先cd / 切换到根目录&#xff0c;然后 du -sh * | sort -rh | head -n 3查看空间占用最大的…