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

继上一篇博客【注解和反射】获取类运行时结构-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;
  }

  @Override
  public 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)时应该谨慎,并确保不会引入安全漏洞。

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

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

相关文章

四川赢涟电子商务有限公司可靠吗?正规吗?

随着电商行业的飞速发展&#xff0c;越来越多的企业开始进军电商领域&#xff0c;其中四川赢涟电子商务有限公司在抖音电商服务上的表现尤为引人注目。那么&#xff0c;四川赢涟电子商务有限公司的抖音电商服务究竟怎么样呢&#xff1f;本文将从多个角度进行深入分析。 一、赢…

edge浏览器新建标签页闪退怎么解决?(打不开标签页)

文章目录 问题描述方法一方法二 问题描述 昨天开始出现这个问题&#xff0c;每次点击 打开一个新的标签页&#xff0c;马上就闪退了。 既然是新建标签页的问题&#xff0c;那么就在设置里看一下新建标签页发生了什么问题。 方法一 进入设置&#xff0c;会发现&#xff0c;有…

R可视化:分组频率分布直方图和密度图

介绍 ggplot2绘制分组频率分布直方图和密度图 加载R包 knitr::opts_chunk$set(message FALSE, warning FALSE) library(tidyverse) library(patchwork) library(ggpubr) library(rstatix)# rm(list ls()) options(stringsAsFactors F) options(future.globals.maxSize …

对增加LLaMA 3 上下文长度技术的猜测

AI苏妲己&#xff1a; 在许多应用场景中&#xff0c;如长对话、长文档摘要或长期计划执行等&#xff0c;大语言模型能够支持较长的上下文窗口是非常理想的。以一次处理约50页书籍内容为例&#xff0c;通常需要模型支持32K个token的上下文长度。目前&#xff0c;主流的大语言模…

[InternLM训练营第二期笔记]6.Lagent AgentLego 智能体应用搭建

该系列是上海AI Lab举行的书生 浦语大模型训练营的相关笔记部分。 该笔记是第六节课&#xff0c;学习大语言模型智能体的基本概念&#xff0c;以及Lagent的使用。 0. 智能体 在大型语言模型&#xff08;LLM&#xff09;的上下文中&#xff0c;智能体&#xff08;agent&#xf…

如何解决高光谱数据读取、数据预处理、高光谱数据机器学习等技术难题......

深入探讨了高光谱遥感数据处理技术&#xff0c;涵盖了基本概念、成像原理、数据处理和分析方法&#xff0c;以及运用机器学习和深度学习模型提取和应用高光谱信息的技术。此外&#xff0c;通过Python实践练习&#xff0c;课程帮助学员巩固所学知识&#xff0c;使其得以深入理解…

Meta-Llama-3-8B-Instruct本地推理

Meta-Llama-3-8B-Instruct 本地推理 基础环境信息&#xff08;wsl2安装Ubuntu22.04 miniconda&#xff09; 使用miniconda搭建环境 (base) :~$ conda create --name pytorch212 python3.10 Retrieving notices: ...working... done Channels:- defaults Platform: linux-64 C…

EasyRecovery数据恢复软件2025破解版安装包下载

EasyRecovery数据恢复软件的主要功能及使用教程。coco玛奇朵可以提供一个概要和简化的教程&#xff0c;以便你了解其基本内容和操作步骤。 EasyRecovery绿色破解下载网盘链接: https://pan.baidu.com/s/1_6NmcOh_Jmc-DGc4TJD-Mg?pwddq4w 提取码: dq4w 复制这段内容后打开百度…

ABAP 第三代增强(BADI)--BADI旧方法

文章目录 第三代增强&#xff08;BADI&#xff09;--BADI旧方法需求分析确定BADI使用SE18查看BADIBADI的创建实施逻辑代码编写测试注意事项 第三代增强&#xff08;BADI&#xff09;–BADI旧方法 第三代增强BADI&#xff1a;全称是&#xff08;Business Add-Ins&#xff09; …

[卷积神经网络]YoloV9

一、概述 代码路径为&#xff1a; YoloV9https://github.com/WongKinYiu/yolov9 YoloV9的作者在论文中指出&#xff1a;现在的深度学习方法大多都在寻找一个合适的目标函数&#xff0c;但实际上输入数据在进行特征提取和空间变换的时候会丢失大量信息。针对这个问题&#xff…

MySQL数据类型:字符串类型详解

MySQL数据类型&#xff1a;字符串类型详解 在MySQL数据库中&#xff0c;字符串数据类型用于存储各种文本信息。这些数据类型主要包括CHAR、VARCHAR、TEXT和BLOB等。 CHAR与VARCHAR CHAR CHAR类型用于存储固定长度的字符串。它的长度在创建表时就已确定&#xff0c;长度范围…

书生·浦语大模型实战营之Llama 3 高效部署实践(LMDeploy 版)

书生浦语大模型实战营之Llama 3 高效部署实践&#xff08;LMDeploy 版&#xff09; 环境&#xff0c;模型准备LMDeploy chatTurmind和Transformer的速度对比LMDeploy模型量化(lite)LMDeploy服务(serve) 环境&#xff0c;模型准备 InternStudio 可以直接使用 studio-conda -t …

查找总价格为目标值的两个商品 ---- 双指针

题目链接 题目: 分析: 解法一: 暴力解法, 将每两个的和都算出来, 判断是否为目标值解法二: 数组中的数是按升序排序的, 我们可以定义左右指针 如果和小于目标值, 则应该让和变大, 所以左指针右移如果和大于目标值, 则应该让和变小, 所以右指针左移 思路: 定义left 0, righ…

使用Krukal算法解决图的最小生成树问题

Kruskal 算法 Kruskal算法是一种用于寻找连通图中最小生成树的算法。最小生成树是一个包含图中所有顶点的树&#xff0c;且边权重之和最小。Kruskal算法是一种贪心算法&#xff0c;它的基本思想是&#xff1a;每次选择边权重最小的边来扩展树&#xff0c;直到树包含所有的顶点…

一周学会Django5 Python Web开发-Django5 ORM执行SQL语句

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计49条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

Spring 注解开发详解

1. 注解驱动入门案例介绍 1.1 需求描述 1.需求&#xff1a;实现保存一条数据到数据库。 2.表结构&#xff1a;create table account(id int primary key auto_increment,name varchar(50),money double(7,2)); 3.要求&#xff1a;使用spring框架中的JdbcTemplate和DriverMana…

Python 使用相对路径读取文件失败

python open一个问及那时使用绝对路径可以&#xff0c;但是使用相对路径时报错&#xff0c;找不到指定文件 解决步骤如下&#xff1a; 添加Python配置 在新增的配置Json文件添加下图红框这一行

阿里云OSS

进入阿里云官网&#xff0c;手机号短信登录

Ansible 中的copy 复制模块应用详解

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月25日13点40分 Ansible 中的 copy 模块用于将文件或目录从本地计算机或远程主机复制到远程主机上的特定位置。它是一个功能强大的模块&#xff0c;可用于各种文…

prometheus helm install 如何配置告警模版

对接企业微信 获取企业id 注册完成之后&#xff0c;通过企业微信官网登录后台管理&#xff0c;在【我的企业】的企业信息里面&#xff0c;获取到Alertmanager服务配置需用到的第一个配置&#xff1a;企业ID 获取部门id 部门ID 在【通讯录】中&#xff0c;添加一个子部门&a…