java 反射创建对象并赋值_java使用反射创建并操作对象的方法

Class 对象可以获得该类里的方法(由 Method 对象表示)、构造器(由 Constructor 对象表示)、成员变量(由 Field 对象表示),这三个类都位于 java.lang.reflect 包下,并实现了 java.lang.reflect.Member 接口。程序可以通过对象来执行对应的方法,通过 Constructor 对象来调用对应的构造器创建实例,能通过 Field 对象直接访问并修改对象的成员变量值。

创建对象

通过反射来生成对象需要先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建该 Class 对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。

在很多 Java EE 框架中都需要根据配置文件信息来创建 Java 对象,从配置文件读取的只是某个类的字符串类名,程序需要根据该字符串来创建对应的实例,就必须使用反射。

下面程序就实现了一个简单的对象池,该对象池会根据配置文件读取 key-value 对,然后创建这些对象,并将这些对象放入一个 HashMap 中。

public class ObjectPoolFactory {

// 定义一个对象池,前面是对象名,后面是实际对象

private Map objectPool = new HashMap<>();

// 定义一个创建对象的方法

// 该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象

private Object createObject(String clazzName) throws Exception, IllegalAccessException, ClassNotFoundException {

// 根据字符串来获取对应的Class对象

Class> clazz = Class.forName(clazzName);

// 使用clazz对应类的默认构造器创建实例

return clazz.getConstructor().newInstance();

}

// 该方法根据指定文件来初始化对象池

// 它会根据配置文件来创建对象

public void initPool(String fileName)

throws InstantiationException, IllegalAccessException, ClassNotFoundException {

try (FileInputStream fis = new FileInputStream(fileName)) {

Properties props = new Properties();

props.load(fis);

for (String name : props.stringPropertyNames()) {

// 每取出一对key-value对,就根据value创建一个对象

// 调用createObject()创建对象,并将对象添加到对象池中

objectPool.put(name, createObject(props.getProperty(name)));

}

} catch (Exception ex) {

System.out.println("读取" + fileName + "异常");

}

}

public Object getObject(String name) {

// 从objectPool中取出指定name对应的对象

return objectPool.get(name);

}

public static void main(String[] args) throws Exception {

ObjectPoolFactory pf = new ObjectPoolFactory();

pf.initPool("obj.txt");

System.out.println(pf.getObject("a")); // ①

System.out.println(pf.getObject("b")); // ②

}

}

上面程序中 createObject() 方法里的两行粗体字代码就是根据字符串来创建 Java 对象的关键代码,程序调用 Class 对象的 newInstance() 方法即可创建一个 Java 对象。程序中的 initPool() 方法会读取属性文件,对属性文件中每个 key-value 对创建一个 Java 对象,其中 value 是该 Java 对象的实现类,而 key 是该 Java 对象放入对象池中的名字。为该程序提供如下属性配置文件。

a=java.util.Date

b=javax.swing.JFrame

编译、运行上面的 ObjectPoolFactory 程序,执行到 main 方法中的①号代码处,将看到输出系统当前时间——这表明对象池中已经有了一个名为a的对象,该对象是一个 java.util.Date 对象。执行到②号代码处,将看到输出一个 JFrame 对象。

提示:这种使用配置文件来配置对象,然后由程序根据配置文件来创建对象的方式非常有用,大名鼎鼎的 Spring 框架就采用这种方式大大简化了 Java EE 应用的开发。当然,Spring 采用的是 XML 配置文件——毕竟属性文件能配置的信息太有限了,而 XML 配置文件能配置的信息就丰富多。

如果不想利用默认构造器来创建 Java 对象,而想利用指定的构造器来创建 Java 对象,则需要利用 Constructor 对象,每个 Constructor 对应一个构造器。为了利用指定的构造器来创建 Java 对象,需要如下三个步骤。

获取该类的 Class 对象。

利用 Class 对象的 getConstructor() 方法来获取指定的构造器。

调用 Constructor 的 newInstance() 方法来创建 Java 对象。

下面程序利用反射来创建一个 JFrame 对象,而且使用指定的构造器。

public class CreateJFrame {

public static void main(String[] args) throws Exception {

// 获取JFrame对应的Class对象

Class> jframeClazz = Class.forName("javax.swing.JFrame");

// 获取JFrame中带一个字符串参数的构造器

Constructor ctor = jframeClazz.getConstructor(String.class);

// 调用Constructor的newInstance方法创建对象

Object obj = ctor.newInstance("测试窗口");

// 输出JFrame对象

System.out.println(obj);

}

}

上面程序中第一行粗休字代码用于获取 JFrame 类的指定构造器,前面已经提到:如果要唯一地确定某类中的构造器,只要指定构造器的形参列表即可。第一行粗体字代码获取构造器时传入了一个 String 类型,即表明想获取只有一个字符串参数的构造器。

程序中第二行粗体字代码使用指定构造器的 newInstance() 方法来创建一个 Java 对象,当调用 Constructor 对象的 newInstance() 方法时通常需要传入参数,因为调用 Constructor 的 newInstance() 方法实际上等于调用它对应的构造器,传给 newInstance() 方法的参数将作为对应构造器的参数。

对于上面的 CreateFrame.java 中已知 java.swing.JFrame 类的情形,通常没有必要使用反射来创建该对象,毕竟通过反射创建对象时性能要稍低一些。实际上,只有当程序需要动态创建某个类的对象时才会考虑使用反射,通常在开发通用性比较广的框架、基础平台时可能会大量使用反射。

调用方法

当获得某个类对应的 Class 对象后,就可以通过该 Class 对象的 getMethods() 方法或者 getMethod()方法来获取全部方法或指定方法——这两个方法的返回值是 Method 数组,或者 Method 对象。

每个 Method 对象对应一个方法,获得 Method 对象后,程序就可通过该 Method 来调用它对应的方法。在 Method 里包含一个 Invoke() 方法,该方法的签名如下。

Object invoke(Object obj, Object...args):该方法中的 obj 是执行该方法的主调,后面的 args 是执行该方法时传入该方法的实参。

下面程序对前面的对象池工厂进行加强,允许在配置文件中增加配置对象的成员变量的值,对象池工厂会读取为该对象配置的成员变量值,并利用该对象对应的 setter 方法设置成员变量的值。

public class ExtendedObjectPoolFactory {

// 定义一个对象池,前面是对象名,后面是实际对象

private Map objectPool = new HashMap<>();

private Properties config = new Properties();

// 从指定属性文件中初始化Properties对象

public void init(String fileName) {

try (FileInputStream fis = new FileInputStream(fileName)) {

config.load(fis);

} catch (IOException ex) {

System.out.println("读取" + fileName + "异常");

}

}

// 定义一个创建对象的方法

// 该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象

private Object createObject(String clazzName) throws Exception {

// 根据字符串来获取对应的Class对象

Class> clazz = Class.forName(clazzName);

// 使用clazz对应类的默认构造器创建实例

return clazz.getConstructor().newInstance();

}

// 该方法根据指定文件来初始化对象池

// 它会根据配置文件来创建对象

public void initPool() throws Exception {

for (String name : config.stringPropertyNames()) {

// 每取出一个key-value对,如果key中不包含百分号(%)

// 这就表明是根据value来创建一个对象

// 调用createObject创建对象,并将对象添加到对象池中

if (!name.contains("%")) {

objectPool.put(name, createObject(config.getProperty(name)));

}

}

}

// 该方法将会根据属性文件来调用指定对象的setter方法

public void initProperty() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

for (String name : config.stringPropertyNames()) {

// 每取出一对key-value对,如果key中包含百分号(%)

// 即可认为该key用于控制调用对象的setter方法设置值

// %前半为对象名字,后半控制setter方法名

if (name.contains("%")) {

// 将配置文件中的key按%分割

String[] objAndProp = name.split("%");

// 取出调用setter方法的参数值

Object target = getObject(objAndProp[0]);

// 获取setter方法名:set + "首字母大写" + 剩下部分

String mtdName = "set" + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1);

// 通过target的getClass()获取它的实现类所对应的Class对象

Class> targetClass = target.getClass();

// 获取希望调用的setter方法

Method mtd = targetClass.getMethod(mtdName, String.class);

// 通过Method的invoke方法执行setter方法

// 将config.getProperty(name)的值作为调用setter方法的参数

mtd.invoke(target, config.getProperty(name));

}

}

}

public Object getObject(String name) {

// 从objectPool中取出指定name对应的对象

return objectPool.get(name);

}

public static void main(String[] args) throws Exception {

ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();

epf.init("extObj.txt");

epf.initPool();

epf.initProperty();

System.out.println(epf.getObject("a"));

}

}

上面程序中 initProperty() 方法里的第一行粗体字代码获取目标类中包含一个 String 参数的 setter 方法,第二行粗体字代码通过调用 Method 的 invoke() 方法来执行该 setter 方法,该方法执行完成后,就相当于执行了目标对象的 setter 方法。为上面程序提供如下配置文件。

a=javax.swing.JFrame

b=javax.swing.JLabel

#set the title of a

a%title=Test Title

上面配置文件中的 a%title 行表明希望调用a对象的 setTitle() 方法,调用该方法的参数值为 Test Title。编译、运行上面的 ExtendedObjectPoolFactory.java 程序,可以看到输出一个 JFrame 窗口,该窗口的标题为 Test Title。

提示:Spring 框架就是通过这种方式将成员变量值以及依赖对象等都放在配置文件中进行管理的,从而实现了较好的解耦。这也是 Spring 框架的 IOC 的秘密。

当通过 Method 的 invoke() 方法来调用对应的方法时,Java 会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的 private 方法,则可以先调用 Method 对象的如下方法。

setAccessible(boolean flag):将 Method 对象的 accessible 设置为指定的布尔值。值为true,指示该 Method 在使用时应该取消 Java 语言的访问权限检查:值为false,则指示该 Method 在使用时要实施 Java 语言的访问权限检查。

注意:实际上,setAccessible() 方法并不属于 Method,而是属于它的父类 AccessibleObject。因此 Method、Constructor、Field 都可调用该方法,从而实现通过反射来调用 private 方法、private 构造器和成员变量,下一节将会让读者看到这种示例。也就是说,它们可以通过调用该方法来取消访问权限检查,通过反射即可访问 private 成员。

访问成员变量值

通过 Class 对象的 getFields() 或 getField() 方法可以获取该类所包括的全部成员变量或指定成员变量。Field 提供了如下两组方法来读取或设置成员变量值。

getXxx(Object obj):获取 obj 对象的该成员变量的值。此处的 Xxx 对应8种基本类型,如果该成员变量的类型是引用类型,则取消 get 后面的Xxx。

setXxx(Object obj, Xxx val):将 obj 对象的该成员变量设置成值。此处的 Xxx 对应8种基本类型,如果该成员变量的类型是引用类型,则取消 set 后面的Xxx。

使用这两个方法可以随意地访问指定对象的所有成员变量,包括 private 修饰的成员变量。

class Person {

private String name;

private int age;

public String toString() {

return "Person[name:" + name + " , age:" + age + " ]";

}

}

public class FieldTest {

public static void main(String[] args) throws Exception {

// 创建一个Person对象

Person p = new Person();

// 获取Person类对应的Class对象

Class personClazz = Person.class;

// 获取Person的名为name的成员变量

// 使用getDeclaredField()方法表明可获取各种访问控制符的成员变量

Field nameField = personClazz.getDeclaredField("name");

// 设置通过反射访问该成员变量时取消访问权限检查

nameField.setAccessible(true);

// 调用set()方法为p对象的name成员变量设置值

nameField.set(p, "Yeeku.H.Lee");

// 获取Person类名为age的成员变量

Field ageField = personClazz.getDeclaredField("age");

// 设置通过反射访问该成员变量时取消访问权限检查

ageField.setAccessible(true);

// 调用setInt()方法为p对象的age成员变量设置值

ageField.setInt(p, 30);

System.out.println(p);

}

}

上面程序中先定义了一个 Person 类,该类里包含两个 private 成员变量:name 和 age,在通常情况下,这两个成员变量只能在 Person 类里访问。但本程序 FieldTest 的 main() 方法中6行粗体字代码通过反射修改了 Person 对象的 name、age 两个成员变量的值。

第一行粗体字代码使用 getDeclaredField() 方法获取了名为 name 的成员变量,注意此处不是使用 getField()方法,因为 getField() 方法只能获取 public 访问控制的成员变量,而 getDeclaredField() 方法则可以获取所有的成员变量;第二行粗体字代码则通过反射访问该成员变量时不受访问权限的控制;第三行粗体字代码修改了 Person 对象的 name 成员变量的值。修改 Person 对象的 age 成员变量的值的方式与此完全相同。

编译、运行上面程序,会看到如下输出:

Person[name:Yeeku.H.Lee , age:30 ]

操作数组

在 java.lang.reflect 包下还提供了一个 Array 类,Array 对象可以代表所有的数组。程序可以通过使用 Array 来动态地创建数组,操作数组元素等。

Array 提供了如下几类方法。

static Object newInstance(Class> componentType,int...length):创建一个具有指定的元素类型、指定维度的新数组。

static xxx getXxx(Object array, int index):返回 array 数组中第 index 个元素。其中是各种基本数据类型,如果数组元素是引用类型,则该方法变为 get(Object array, int index)。

static void setXxx(Object array, int index, xxx val):将 array 数组中第 index 个元素的值设为 val。其中 xxx 是各种基本数据类型,如果数组元素是引用类型,则该方法变成 set(Object array, int index, Object val)。

下面程序示范了如何使用 Array 来生成数组,为指定数组元素赋值,并获取指定数组元素的方式。

public class ArrayTest1 {

public static void main(String args[]) {

try {

// 创建一个元素类型为String ,长度为10的数组

Object arr = Array.newInstance(String.class, 10);

// 依次为arr数组中index为5、6的元素赋值

Array.set(arr, 5, "疯狂Java讲义");

Array.set(arr, 6, "轻量级Java EE企业应用实战");

// 依次取出arr数组中index为5、6的元素的值

Object book1 = Array.get(arr, 5);

Object book2 = Array.get(arr, 6);

// 输出arr数组中index为5、6的元素

System.out.println(book1);

System.out.println(book2);

} catch (Throwable e) {

System.err.println(e);

}

}

}

上面程序中三行粗体字代码分别是通过 Array 创建数组,为数组元素设置值,访问数组元素的值的示例代码,程序通过使用 Array 就可以动态地创建并操作数组。

下面程序比上面程序稍微复杂一点,下面程序使用 Array 类创建了一个三维数组。

public class ArrayTest2 {

public static void main(String args[]) {

/*

* 创建一个三维数组。 根据前面介绍数组时讲的:三维数组也是一维数组, 是数组元素是二维数组的一维数组,

* 因此可以认为arr是长度为3的一维数组

*/

Object arr = Array.newInstance(String.class, 3, 4, 10);

// 获取arr数组中index为2的元素,该元素应该是二维数组

Object arrObj = Array.get(arr, 2);

// 使用Array为二维数组的数组元素赋值。二维数组的数组元素是一维数组,

// 所以传入Array的set()方法的第三个参数是一维数组。

Array.set(arrObj, 2, new String[] { "疯狂Java讲义", "轻量级Java EE企业应用实战" });

// 获取arrObj数组中index为3的元素,该元素应该是一维数组。

Object anArr = Array.get(arrObj, 3);

Array.set(anArr, 8, "疯狂Android讲义");

// 将arr强制类型转换为三维数组

String[][][] cast = (String[][][]) arr;

// 获取cast三维数组中指定元素的值

System.out.println(cast[2][3][8]);

System.out.println(cast[2][2][0]);

System.out.println(cast[2][2][1]);

}

}

上面程序的第一行粗体字代码使用 Array 创建了一个三维数组,程序中较难理解的地方是第二段粗体字代码部分,使用 Array 为 arrObj 的指定元素赋值,相当于为二维数组的元素赋值。由于二维数组的元素是一维数组,所以程序传入的参数是一个一维数组对象。

运行上面程序,将看到 cast[2][3][8]、cast[2][2][0]、cast[2][2][1] 元素都有值,这些值就是刚才程序通过反射传入的数组元素值。

以上就是java使用反射创建并操作对象的方法的详细内容,更多关于JAVA 反射创建并操作对象的资料请关注脚本之家其它相关文章!

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

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

相关文章

cad注释比例和打印比例不一样_cad注释比例(cad注释比例与打印比例)

这两个比例有什么区别 分别代表的是什么意思 麻烦说详细点 谢谢是物体被缩小了50倍后的图形这两个比例。但是一张图纸不允许出现两个或以上的比例(局部放大除外)&#xff0c;但是标注的数字是实际尺寸&#xff0c;两个没有区别画的是800*500mm的线&#xff0c;注释比例用的1:1&…

java bundle管理_java.util.ResourceBundle使用详解

一、认识国际化资源文件这个类提供软件国际化的捷径。通过此类&#xff0c;可以使您所编写的程序可以&#xff1a;轻松地本地化或翻译成不同的语言一次处理多个语言环境以后可以轻松地进行修改&#xff0c;支持更多的语言环境说的简单点&#xff0c;这个类的作用就是读取资源属…

java mac postgresql_PostgreSQL 的安装与使用 for mac

##安装 在 mac 下&#xff0c;可以利用 homebrew 直接安装 PostgreSQL&#xff1a;brew install postgresql安装好之后有一个数据库(postgres)&#xff0c;如需要重新初始化数据库&#xff1a;initdb /usr/local/var/postgres_mao启动&#xff1a;pg_ctl -D /usr/local/var/pos…

疯狂java讲义价格_疯狂java讲义

封装&#xff1a;封装&#xff1a;将对象状态信息隐藏在对象内部&#xff0c;不允许外部程序直接访问对象内部信息&#xff0c;而是用类所提供的方法访问和操作。访问控制符&#xff1a;private(当前类访问权) ,protected(子类访问),public(公共访问)&#xff0c;default(包访问…

java源代码实例倒计时_Java倒计时三种实现方式代码实例

写完js倒计时&#xff0c;突然想用java实现倒计时&#xff0c;写了三种实现方式一&#xff1a;设置时长的倒计时&#xff1b;二&#xff1a;设置时间戳的倒计时&#xff1b;三&#xff1a;使用java.util.Timer类实现的时间戳倒计时代码如下&#xff1a;package timer;import ja…

java 加密 encrypt_JAVA Encrypter 加密算法

Java代码/*** 加密者** time: 2007-8-29 下午05:45:36*/public final class Encrypter {private static Cipher ecipher;private static Cipher dcipher;// 必须24个字符private static final String key "*:1$7!a*:1$7!a*:1$7!^";private static final String alg …

java base64 加解密_java Base64加解密

import sun.misc.BASE64Decoder;import sun.misc.BASE64Encoder;public class Base64Util {public static void main( String[] args ) {String data2 " var re1/[a-zA-Z]/g; return (FIELD_VALUE.match(re1)).length;";try {// BASE64加密BASE64Encoder encoder n…

python svn库_python实现svn新老库迁移

1 #!/usr/bin/env python2 #codingutf-834 #启动环境&#xff1a;linux&#xff0c;安装了python&#xff0c;svn客户端&#xff0c;并且具备python部分依赖包&#xff0c;xlrd&#xff0c;shutil&#xff0c;如果没有网上搜索安装5 #启动方式&#xff1a;将文件MvSvnLibToNew.…

python二维表转一维表_二维表格转换成一维表格

# 加载数据import pandas as pddf_old1 pd.read_excel(r"D:\Jupyter\data\Python.xlsx",sheet_name "变一维")df_old1# 数据清洗&#xff0c;把第一列设为索引列df_old2 pd.read_excel(r"D:\Jupyter\data\Python.xlsx",sheet_name "变一…

java web后台_java web 后台那些事

java web 后台运行原理当Web服务器接收到一个HTTP请求时&#xff0c;它会先判断请求内容——如果是静态网页数据&#xff0c;Web服务器将会自行处理&#xff0c;然后产生响应信息&#xff1b;如果牵涉到动态数据&#xff0c;Web服务器会将请求转交给Servlet容器。此时Servlet容…

MyBatis的缓存!!!!

1.一级缓存&#xff08;默认开启&#xff09; (1) 首先在UserMapper接口定义两个方法&#xff1a; package com.by.mapper;import com.by.pojo.User; import org.apache.ibatis.annotations.Param; import org.junit.Test;import java.util.List;/*** <p>Project: myb…

java动态语言_探秘Java 7:JVM动态语言支持详解

JDK 7 增加了对 JSR 292 的支持&#xff0c;在 JVM 中动态类型语言的运行速度将变得更快。这一支持的关键在于增加了新的 Java 字节码&#xff0c;invokedynamic&#xff0c;它用于方法调用&#xff0c;还有新的连接机制&#xff0c;其中包含了一个新的构造&#xff1a;方法句柄…

java 反射机制 视频_JAVA反射机制及其原理实现

9.1 概念JAVA反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&#xff1b;public、protected、private。OO(面向对象)&#xff0c;private私有的&#x…

python模型的属性是什么_python – Django:为什么Django模型字段的类属性?

不,原因与此相同&#xff1a;>>> class Foo(object):... bar Foo attribute...>>> f Foo()>>> f.barFoo attribute>>> Foo.barFoo attribute>>> f.bar instance attribute>>> f.barinstance attribute>>> F…

java 安卓下载文件_GitHub - Charay/downloadfile: 使用Retrofit2+Rxjava+Rxandroid+okhttp的方式下载文件并存储到sd卡指定目录...

downloadfile使用Retrofit2RxjavaRxandroidokhttp的方式下载文件并存储到sd卡指定目录使用&#xff1a;gradleStep 1.在工程build.gradle文件中加入maven地址repositories:allprojects {repositories {...maven { url https://jitpack.io }}}Step 2. 在module的build.gradle中添…

java map 队列_Java:queue队列,map集合

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼Queue&#xff1a; 基本上&#xff0c;一个队列就是一个先入先出(FIFO)的数据结构Queue接口与List、Set同一级别&#xff0c;都是继承了Collection接口。LinkedList实现了Deque接 口示例代码&#xff1a;1. import java.util.Linke…

java溢出怎么处理_java数据溢出怎么处理?

publicstaticvoidmain(String[]args){//TODO自动生成的方法存根ScannerscnewScanner(System.in);Stringaasc.nextLine();sc.close();floatdn0,xn0,kn0,sn0,qn0;for(inti0;i<a...public static void main(String[] args) {// TODO 自动生成的方法存根Scanner scnew Scanner(S…

java基础 最重要的部分_Java基础(1)最基础的部分

本文章均为自己在自学期间整理的笔记&#xff0c;2020年四月份开始学习Java&#xff0c;如有不足&#xff0c;请补充。希望对各位小伙伴都能有帮助。1.Java中public class和class区别&#xff1a;1)在一个Java文件中可以定义多个class 2)public的class不是必须的 3)public修…

logstash mysql增量_Logstash jdbc 按时间增量更新的一些总结

不同数据库的支持mysql数据类型显示样例是否支持timestampstatementtracking_columntracking_column_typeSQL示例date2020-10-20Nselect *, datediff(date, 1970-01-01) as days from tbl_time where datediff(date, 1970-01-01) > :sql_last_valuedaysnumericselect *, dat…

用java编写奖金税率_企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%_java企业利润提成计算代码...

今天上java课程的时候老师给布置了一个作业&#xff0c;让让java计算一下企业该发放的奖金总额&#xff0c;接下来吾爱编程就为大家介绍一下java实现企业发放的奖金根据利润提成的代码&#xff0c;有需要的小伙伴可以参考一下&#xff1a;1、题目如下&#xff1a;企业发放的奖金…