Hibernate 系列 08 - 对象识别机制
目录导读:
本篇目录:
为了区别不同的对象,有两种识别方法:
1. 以内存地址识别
如果两个对象的内存地址相同,毫无疑问,它们是相同的。
如果要比较的是对象携带的信息,使用内存地址识别就不可用,因为地址不同的对象,它们所代表的的信息可能是一样的。
例如有两个字符串,代码如下:
public class CNBlogsTest {
public static void main(String[] args) {
String str1 = new String("cnblogs");
String str2 = new String("cnblogs");
if (str1 == str2) // 判断内存地址是否相同
System.out.println("str1和str2的内存地址相同。");
else if(str1.equals(str2)) // 判断它们的值是否相同
System.out.println("str1和str2的值相同。");
}
}
由于str1和str2是用两个new命令开辟出来的字符串空间,它们的内存地址是不一样的,而它们所携带的信息都是cnblogs,所以运行上例程序打印出来的结果是:
str1和str2的值相同。
2. 以对象携带的信息识别
在Hibernate中Session的操作可能会有一些疑惑。
Session不是线程安全的,不同的Session维护者自己的缓存空间(在Hibernate中用Map实现),这个这个缓存空间存放着纳入了持久层的持久对象。
但是按道理讲,从数据库同一记录取得的字段所组装成的对象应该是同一个对象,然后由不同的Session从数据库同一条记录上分别取得对象,它们的内存地址是不一样的。
尽管它们所携带的信息一致。
如下面的程序所示:
Configuration cfg = new Configuration().configure(); SessionFactory sf = cfg.buildSessionFactory(); Session session = sf.getCurrentSession(); Transaction tx = session.beginTransaction(); Object obj1 = session.get(Student.class, 12); Object obj2 = session.get(Student.class, 12); tx.commit(); session.close(); System.out.println(obj1==obj2); // 结果为true
上面这个代码片段最终输出结果为true,表示obj1和obj2引用的是同一对象,它们的内存地址相同。
但如果是下面的代码的话:
Configuration cfg = new Configuration().configure(); SessionFactory sf = cfg.buildSessionFactory(); // Session1 Session session1 = sf.getCurrentSession(); Transaction tx1 = session1.beginTransaction(); Object obj1 = session1.get(Student.class, 12); tx1.commit(); session1.close(); // Session2 Session session2 = sf.getCurrentSession(); Transaction tx2 = session2.beginTransaction(); Object obj2 = session2.get(Student.class, 12); tx2.commit(); session2.close(); System.out.println(obj1==obj2); // 结果为false System.out.println(obj1.equals(obj2)); // 结果为false
以上代码最终的运行结果为false、false。
对于第一个false,是因为不同的Session用不同的Map缓存Session级别的持久对象。
因此,虽然它们是从数据库的同一条记录中取数据,但两个Session把组装的对象放在了不同的内存地址中。
如图所示:

对于第二个false,有的朋友可能会有点儿懵逼,equals()方法不就是比较对象信息的嘛?既然是同一条记录组装的对象,为什么还是false嘞?
这是因为Student类默认的equals()方法继承自java.lang.Object类,Object类的equals()方法的源码如下:
package java.lang;
public class Object {
public boolean equals(Object obj) {
return (this == obj);
}
}
可以看到,Object类的equals()方法使用的仍是内存地址判断,由于obj1和obj2的内存地址不一样,所以使用继承自Object的equals()方法得到的依然还是false。
平时所熟悉的String类的equals()方法是重写了Object的equals()方法。
当调用String类的a.equals(b)方法时,是将a字符串和b字符串一个字符一个字符进行比较,因此String类的equals()方法可以比较不同内存地址的字符串是否相同。
String类的equals()方法源码如下:
package java.lang;
import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public final class String
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) { // 只要两个String之中有一个字符不同,则认为二者不同
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
}
同理,要实现Student类的信息比较,可以自己实现equals()和hashCode()方法。
一个方法时,通过getStudentNo()方法取得对象的studentNo值并加以比较。
例如:若studentNo的类型是String,代码如下:
public class Student {
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // 如果内存地址相等,返回true
if (this.studentNo == null || !(obj instanceof Student)) return false;
Student stu = (Student) obj;
return this.studentNo.equals(stu.studentNo);
}
@Override
public int hashCode() {
return this.studentNo.hashCode();
}
}
通过studentNo来比较对象是否相等存在一些问题。
因为当一个对象被new出来而还没有save()的时候,它并不会被赋予studentNo值,这是这个方法就不太适合了。
通常使用的方法是根据对象中真正包含的属性值来做比较,例如:
public class Student {
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // 如果内存地址相等,返回true
if(!(obj instanceof Student)) return false;
Student stu = (Student)obj;
if(!getStudentName().equals(stu.getStudentName())) return false;
if(!getBornDate().equals(stu.getBornDate())) return false;
return true;
}
// 通过一个简单的算法得到哈希码
@Override
public int hashCode() {
int result;
result = getStudentName().hashCode();
result = 29*result+getBornDate().hashCode();
return result;
}
}
上述例子不再简单地比较studentNo值了,而是根据学生姓名和生日对Student对象实例进行比较。因为基本上名字和生日就能确定一个人的身份了。
当然,实现的方法还有很多,也可以使用其他的属性来比较Student的身份,这就要根据实际的需求来决定了。
Hibernate 系列 08 - 对象识别机制的更多相关文章
- Hibernate 系列 06 - 对象在JVM中的生命周期
引导目录: Hibernate 系列教程 目录 Java对象通过new命令进行创建,Java虚拟机(Java Virtual Machine,JVM)会为新的Java对象在内存中开辟一个新空间以存放次 ...
- Hibernate 系列 学习笔记 目录 (持续更新...)
前言: 最近也在学习Hibernate,遇到的问题差不多都解决了,顺便把学习过程遇到的问题和查找的资料文档都整理了一下分享出来,也算是能帮助更多的朋友们了. 最开始使用的是经典的MyEclipse,后 ...
- Hibernate 系列 07 - Hibernate中Java对象的三种状态
引导目录: Hibernate 系列教程 目录 1. Java对象的三种状态 当应用通过调用Hibernate API与框架发生交互时,需要从持久化的角度关注应用对象的生命周期. 持久化声明周期是Hi ...
- Hibernate 系列 02 - Hibernate介绍及其环境搭建
引导目录: Hibernate 系列教程 目录 昨晚喝多了,下午刚清醒,继续搞Hibernate.走起. 觉得还行的话,记得点赞哈,给我这个渣渣点学习的动力.有错误的话也请指出,省的我在错误上走了不归 ...
- 写给程序员的机器学习入门 (十一) - 对象识别 YOLO - 识别人脸位置与是否戴口罩
这篇将会介绍目前最流行的对象识别模型 YOLO,YOLO 的特征是快,识别速度非常快
- Hibernate 系列 01 - 框架技术 (介绍Hibernate框架的发展由来)
引导目录: Hibernate 系列教程 目录 本篇导航: 为什么学习框架技术 框架的概念 主流框架的介绍 1.为什么学习框架技术 如何制作一份看上去具有专业水准的PPT文档呢?一个简单的方法就是使用 ...
- Hibernate 系列 03 - 使用Hibernate完成持久化操作
引导目录: Hibernate 系列教程 目录 康姆昂,北鼻,来此狗.动次打次,Hibernate继续走起. 目录: 使用Hibernate实现按主键查询 使用Hibernate实现数据库的增.删.改 ...
- Hibernate 系列 04 - Hibernate 配置相关的类
引导目录: Hibernate 系列教程 目录 前言: 通过上一篇的增删改查小练习之后,咱们大概已经掌握了Hibernate的基本用法. 我们发现,在调用Hibernate API的过程中,虽然Hib ...
- Hibernate 系列 05 - Session 类
引导目录: Hibernate 系列教程 目录 前言: Session是Hibernate运作的中心,对象的生命周期.事务的管理.数据库的存取都与Session息息相关. 就如同在编写JDBC时需要关 ...
随机推荐
- 有意思的记录-Java
1.文件读取 项目外的绝对路径或相对路径文件读取 String path = "/xx/xx.txt"; BufferedReader reader = new BufferedR ...
- .NET 基础 一步步 一幕幕[运算符、占位符、转义符]
运算符.占位符.转义符 好吧,在五局全胜之后,终于升到了三个钻,距离一个星星还有一大段距离,忽然想起来今天的博客还没写,果断坑队友,来写博客了....感觉以后还是每天更新一篇比较好.要不晚上就该熬 ...
- 【PRINCE2是什么】PRINCE2认证之七大原则(6)
我们先来回顾一下,PRINCE2七大原则分别是持续的业务验证,经验学习,角色与责任,按阶段管理,例外管理,关注产品,剪裁 第六个原则:关注产品 PRINCE2指出,一个成功的项目必须以产品为导向,而不 ...
- java注意的一些细节问题
1. 大括弧作用域问题 public static void main(String[] args) { { int x; { int x;//编译错误:Duplicate local variabl ...
- Java异常总结
异常就是在程序中可能要发生的未知错误,java机制中异常分为2大类:Exception和Error. 对异常的处理方式有2种,一是将异常通过关键字throws抛出,二是将异常进行try catch处理 ...
- Linux平台 Oracle 11gR2 RAC安装Part3:DB安装
四.DB(Database)安装 4.1 解压DB的安装包 4.2 DB软件安装 4.3 ASMCA创建磁盘组 4.4 DBCA建库 4.5 验证crsctl的状态 Linux平台 Oracle 11 ...
- 分布式系统理论基础 - 一致性、2PC和3PC
引言 狭义的分布式系统指由网络连接的计算机系统,每个节点独立地承担计算或存储任务,节点间通过网络协同工作.广义的分布式系统是一个相对的概念,正如Leslie Lamport所说[1]: What is ...
- 移动端自适应之——rem与font-size
需求:在不同的移动终端设备中实现,UI设计稿的等比例适配. 方案:布局排版都用rem做单位,然后不同宽度的屏,js动态计算根节点的font-size. 假设设计稿是宽750px来做的,书写css方便计 ...
- Python 正则表达式入门(初级篇)
Python 正则表达式入门(初级篇) 本文主要为没有使用正则表达式经验的新手入门所写. 转载请写明出处 引子 首先说 正则表达式是什么? 正则表达式,又称正规表示式.正规表示法.正规表达式.规则表达 ...
- .NET 版本区别,以及与 Windows 的关系
老是记不住各 Windows 版本中的 .NET 版本号,下面汇总一下: .NET Framework各版本汇总以及之间的关系 Mailbag: What version of the .NET Fr ...