Java反射(reflect)是开发过程中一种常用的手段,常常被用在处理一些感觉有共性但看起来又好像没法抽象的功能上.在OECP项目开发当中,我们在很多地方用到了反射,其中对实体类进行了分层抽象封装的过程中就有一些使用.本文将以重写equals方法为例,简单介绍一下java反射的应用.
先大体介绍一下我们的实体封装的结构.从UML图中我们可以看到顶级SuperEO类作为整个实体层次的顶端,它包含了所有实体类所共有的方法和属性,比如:克隆方法.我们知道克隆方法的默认实现是Object中的,并且为浅克隆.但浅克隆通常对于我们来说没什么意义,所以我们需要实现一个深克隆的方法(本文主要是说java反射的应用.深克隆和浅克隆的区别不了解的朋友可以去google一下),这个方法为所有实体类所共有.像这样需要所有实体共有的方法还有equals(比较),getAttributeValue(根据字段名得到字段值)等等.SuperEO下有两个分支,分别为BaseEO和BaseVO.BaseEO为可持久化实体的父类,包括跟持久化有关的一些属性和方法.BaseVO为非持久化实体父类,此类暂时没有自己的方法和属性,其存在的目的是预留有备扩充.由此,系统中所有的实体类都从这两个分支派生而来.下面几个类就不详细介绍了,毕竟此文的目的不是介绍实体抽象方案的.以后我们在其他地方再对这个实体结构做详细分析说明.
我们看到在实体继承关系之外,存在一个叫做EOUtility的类,此类是个EO的辅助工具类,SuperEO依赖这个工具类,来实现一些通用方法.我们所要说的java反射的应用,也就集中在这个工具类中.
这个类中,我们使用java反射实现了好多通用的功能.在这里我就不每个方法都说明了,只简单介绍其中一个来说一下java反射的用法吧.equals方法是我们java开发人员平时比较常用的一个方法,用来比较两个对象是否相等.此方法的默认实现为Object类中的实现,而默认实现的原理呢,则是按内存地址进行比较,也就是说,如果我们创建了两个内部值完全相同的对象,在我们使用equals对它们进行比较时返回的结果会是false,因为他们的内存地址是不同的.显然这不是我们想要的结果,我们需要的是,将两个对象的内部属性值进行逐一对比,全部相同则返回true,否则为false.为了实现这个目的,我们应该怎么去做呢?下面我们分析一下.
为了使所有的子类都拥有这个方法,我们必须将这个方法定义在顶级超类SuperEO中.在两个对象进行比较时,我们需要获取对象中所有的属性字段,以及这些字段的get方法,而后使用get方法得到属性值,对这些值进行比较.可是equals方法我们要在SuperEO中实现,而SuperEO的子类是由其他的开发人员定义的,其内部有多少属性字段,都叫什么名字,在我们的SuperEO中是不知道的.既然这些都不知道,我们又怎么能比较这些值呢? 看起来这是个问题.
但是这个问题能不能解决呢? 能!这就要用到我们所说的java反射机制了.反射机制简单的说是java提供给开发人员用来对类和方法属性进行操作的工具.利用反射机制,我们是可以获取我们想要的属性字段,以及他们的get方法的,并且我们可以调用这些方法.这样我们的问题就解决了,下面可以开工了.我实现的步骤是这样的.
首先,使用Class.getDeclaredFields方法得到当前类所有的属性字段,而后使用PropertyDescriptor.getReadMethod()得到他们的get方法,并将他们存入一个HashMap中,以字段名为Key,get方法作为Value.在进行字段比较时使用Method.invoke方法,循环调用这些字段的get方法取值而后比较.
代码片段:
PropertyDescriptor[] propertyDescriptors = null;
private void buildGetterANDSetters(Class beanclass){
// 得到当前类字段名称
Field[] fields = beanclass.getDeclaredFields();
String fieldname = null;
// 拼接字段对应的方法名
for( Field field : fields ){
// 一对多字段不要参加toString,hashcode和equals方法,不需要加载,一旦加载反而会引起数据级联更新时出错!!
OneToMany onetomany = field.getAnnotation(OneToMany.class);
Transient t = field.getAnnotation(Transient.class);
if(onetomany != null || t != null){
continue;
}
fieldname = field.getName();
for(PropertyDescriptor property : propertyDescriptors){
if(fieldname.equals(property.getName())){
Method reader = property.getReadMethod();
Method writer = property.getWriteMethod();
if(reader!=null
&& !(reader.isAnnotationPresent(OneToMany.class)||
reader.isAnnotationPresent(Transient.class)))
hm_Geters.put(fieldname, reader);
if(writer!=null
&&!(writer.isAnnotationPresent(OneToMany.class)||
writer.isAnnotationPresent(Transient.class)))
hm_Seters.put(fieldname, writer);
}
}
}
// 当前类不是 BaseEntityBean时,递归调用
if(!beanclass.equals(SuperEO.class)){
buildGetterANDSetters((Class<? extends SuperEO>)beanclass.getSuperclass());
}
}
而后在equals方法循环调用下面这个方法进行比较:
/**
* 比较两个对象,指定的字段值是否相同
* @author slx
* @date 2009-7-17 上午09:51:58
* @modifyNote
* @param fieldName
* 需要比较的字段
* @param obj1
* 对象1
* @param obj2
* 对象2
* @return
* 值相同则为true
*/
private boolean equalsField(String fieldName , Object obj1 ,Object obj2){
try {
Object obj_value = null;
Object current_value = null;
Method getter = hm_Geters.get(fieldName);
current_value = getter.invoke(obj1, null);
obj_value = getter.invoke(obj2, null);
if(current_value == null && obj_value ==null ){
return true ;
}else if(current_value!=null){
return current_value.equals(obj_value);
}else if(obj_value!=null){
return obj_value.equals(current_value);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return false ;
}
除equals之外通过类似这样的方式,在我们的EOUtility中我们还实现了根据字段名取值,根据字段名赋值,对象的toString方法打印出所有字段值,得到对象所有属性的名称等方法.这些方法为我们程序其他的一些功能封装时提供了可能性和方便性.比如我们需要封装一个界面表格的输出,我们只要知道当前的实体是SuperEO就可以了,而不需要知道具体是那个子类.通过getAttributeNames就可以得到所有的字段名称,通过getAttributeValue(字段名)就可以得到字段的值,而后使用循环就可以把我们想要的表格画出来了.而这一切的便捷是我们封装的父类中提供的,提供这些方法的前提则是java反射 — reflect.
当然,java反射绝不仅仅是这么一点点功能,连同动态代理,类加载器等也都应该是属于java反射的范畴.在我们需要封装针对类的通用功能时,是很有用的.
提供该文档的机构为 百洋软件研究实验室 ,更多的博客文章可以到 百洋软件研究实验室博客 查看。该文档附件欢迎各位转载,但是在没有获得文章作者许可之前,不得对文章内容或者版权信息进行更改,版权归百洋软件研究实验室所有,仅此声明。
原文:http://www.po-soft.com/blog/slx/443.html
分享到:
相关推荐
#### 三、Java反射机制在实际项目中的应用 ##### 1. 表单数据填充 在Web开发中,表单提交的数据通常需要填充到对应的实体类中。使用反射可以实现这一功能: - 使用`Class.forName()`加载表单对应的实体类。 - 使用...
Java反射在实际工作中的应用笔记 Java反射是Java语言中一个非常重要的特性,它可以在实际工作中发挥着非常重要的作用。通过Java反射,我们可以在运行时动态地调用类的成员变量和方法,从而实现了更加灵活和灵活的...
Java反射是Java编程语言中...在实际应用中,你可以通过分析这个项目的源代码,结合反射的相关知识,了解如何在实际项目中运用反射技术。通过实践,你将能够更好地掌握这一核心Java特性,为你的程序开发带来极大的便利。
在标题“Java反射实例”中,我们聚焦于如何在实际编程中应用这一特性。通过反射,我们可以动态地获取类的信息,如类名、方法名、字段名,甚至可以在运行时创建和调用对象。这对于实现元编程、插件系统、序列化、单元...
Java反射机制是Java编程语言中一个...通过阅读"1139760298222java.pdf"这份文档,你可以更深入地理解Java反射机制,并结合实际示例学习如何在项目中有效利用这一特性。记得在实际操作中结合代码实践,以巩固理论知识。
Java反射机制是Java编程语言中的一项核心特性,它允许程序在运行时动态地获取类的信息并进行操作。这使得Java能够在编译后仍然能够灵活地处理类和对象,增强了代码的动态性。在Android应用开发中,反射机制尤其重要...
Java反射和工厂模式是两种非常重要的编程概念,它们在软件设计和实现中起着关键作用。反射机制允许我们在运行时动态地获取类的信息并操作对象,而工厂模式是一种设计模式,它提供了一种创建对象的最佳方式,使得代码...
由于Java反射机制允许程序在运行时构造和访问任何类的属性和方法,它为Reflect-ORM框架提供了实现的理论基础。通过反射,框架可以动态地读取对象的属性信息,将这些信息转换为数据库的表结构,以及将数据库查询结果...
在"20180716HongputaoStudy"这个压缩包中的项目实践源码,很可能是作者对于Java反射机制的实际应用案例,可能涵盖了上述知识点的具体实现。通过阅读和学习这些源码,我们可以更深入地理解如何在实际项目中运用反射,...
通过这份资料,开发者不仅可以理解Java反射的基本概念,还能通过实例学习到反射在实际项目中的应用技巧,提升编程的灵活性和可扩展性。同时,对反射的深入理解和正确使用,也能帮助解决很多设计模式和框架中的核心...
Java反射机制是Java编程语言中的一个强大特性,它允许程序在运行时检查和操作类、接口、字段和...通过阅读和学习提供的"Java反射机制经典案例"文档,你可以深入理解这些概念,并学会如何在实际项目中有效地应用反射。
在Java编程中,反射(Reflection)是一个强大的工具,它允许我们在运行时检查和操作类、接口、字段和方法。然而,反射操作通常比直接的Java代码执行慢,因为它涉及到动态类型检查和方法调用。因此,为了提高性能,...
这个"java反射调用实例代码"应该包含了以上介绍的各种反射操作,可以帮助学习者深入理解Java反射机制,并在实际项目中灵活运用。通过研究和实践这些代码,你可以更好地掌握Java反射的精髓,提升你的编程技能。
Java反射是Java编程语言中的一个强大特性,它允许运行时的程序访问并操作类、接口、字段和方法等对象,即使这些对象在编译时并未被明确地引用。这一特性使得Java具有高度的动态性,使得代码能够在运行时检查类的信息...
Java是一种广泛使用的面向对象的编程语言,其强大的功能和灵活性使其在软件开发领域占据着...在实际项目中,根据需求灵活运用反射、流和内存管理,可以提高代码的可扩展性和性能,而扎实的基础知识则是这一切的前提。
### Java反射机制与NoSuchMethodException详解 在Java编程中,反射是一种强大的机制,允许程序在运行时检查和修改自身结构和行为。然而,当开发者尝试使用反射调用一个不存在的方法时,便会遇到`java.lang....
通过他的学习笔记,我们可以更深入地理解如何在实际项目中应用反射,解决一些特定问题,如插件化开发、序列化、动态代理等。 总的来说,Java反射机制提供了一种强大的工具,让程序员能够在运行时动态地探索和操作类...
Java反射机制是Java编程语言中的一个强大工具,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。这个特性使得Java具有了高度的灵活性和动态性,尤其是在构建框架和库时尤为有用。本篇文章将...
下面我们将深入探讨Java反射的核心概念及其在实际开发中的应用。 首先,我们需要了解什么是Java反射。在Java中,当我们编译一个程序,所有的类都被编译成.class文件。反射允许我们获取这些.class文件中的元数据,...