目录导读:

  Hibernate 系列 学习笔记 目录

本篇目录:

  为了区别不同的对象,有两种识别方法:

  1. 内存地址识别(“==”号识别)

  2. equals()和hashCode()识别

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 - 对象识别机制的更多相关文章

  1. Hibernate 系列 06 - 对象在JVM中的生命周期

    引导目录: Hibernate 系列教程 目录 Java对象通过new命令进行创建,Java虚拟机(Java Virtual Machine,JVM)会为新的Java对象在内存中开辟一个新空间以存放次 ...

  2. Hibernate 系列 学习笔记 目录 (持续更新...)

    前言: 最近也在学习Hibernate,遇到的问题差不多都解决了,顺便把学习过程遇到的问题和查找的资料文档都整理了一下分享出来,也算是能帮助更多的朋友们了. 最开始使用的是经典的MyEclipse,后 ...

  3. Hibernate 系列 07 - Hibernate中Java对象的三种状态

    引导目录: Hibernate 系列教程 目录 1. Java对象的三种状态 当应用通过调用Hibernate API与框架发生交互时,需要从持久化的角度关注应用对象的生命周期. 持久化声明周期是Hi ...

  4. Hibernate 系列 02 - Hibernate介绍及其环境搭建

    引导目录: Hibernate 系列教程 目录 昨晚喝多了,下午刚清醒,继续搞Hibernate.走起. 觉得还行的话,记得点赞哈,给我这个渣渣点学习的动力.有错误的话也请指出,省的我在错误上走了不归 ...

  5. 写给程序员的机器学习入门 (十一) - 对象识别 YOLO - 识别人脸位置与是否戴口罩

    这篇将会介绍目前最流行的对象识别模型 YOLO,YOLO 的特征是快,识别速度非常快

  6. Hibernate 系列 01 - 框架技术 (介绍Hibernate框架的发展由来)

    引导目录: Hibernate 系列教程 目录 本篇导航: 为什么学习框架技术 框架的概念 主流框架的介绍 1.为什么学习框架技术 如何制作一份看上去具有专业水准的PPT文档呢?一个简单的方法就是使用 ...

  7. Hibernate 系列 03 - 使用Hibernate完成持久化操作

    引导目录: Hibernate 系列教程 目录 康姆昂,北鼻,来此狗.动次打次,Hibernate继续走起. 目录: 使用Hibernate实现按主键查询 使用Hibernate实现数据库的增.删.改 ...

  8. Hibernate 系列 04 - Hibernate 配置相关的类

    引导目录: Hibernate 系列教程 目录 前言: 通过上一篇的增删改查小练习之后,咱们大概已经掌握了Hibernate的基本用法. 我们发现,在调用Hibernate API的过程中,虽然Hib ...

  9. Hibernate 系列 05 - Session 类

    引导目录: Hibernate 系列教程 目录 前言: Session是Hibernate运作的中心,对象的生命周期.事务的管理.数据库的存取都与Session息息相关. 就如同在编写JDBC时需要关 ...

随机推荐

  1. C++模板编程:如何使非通用的模板函数实现声明和定义分离

    我们在编写C++类库时,为了隐藏实现,往往只能忍痛舍弃模版的强大特性.但如果我们只需要有限的几个类型的模版实现,并且不允许用户传入其他类型时,我们就可以将实例化的代码放在cpp文件中实现了.然而,当我 ...

  2. webstorm license key

    JetBrains WebStorm注册码 UserName: William License Key : ===== LICENSE BEGIN ===== 45550-12042010 00001 ...

  3. ABP源码分析十四:Entity的设计

    IEntity<TPrimaryKey>: 封装了PrimaryKey:Id,这是一个泛型类型 IEntity: 封装了PrimaryKey:Id,这是一个int类型 Entity< ...

  4. .NET基础 一步步 一幕幕[循环、逻辑语句块]

    循环.逻辑语句块   好久不写博客了,断更了好几天了,从上周五到今天,从北京到上海,跨越了1213.0公里,从一个熟悉的城市到陌生的城市,还好本人适应力比较好,还有感谢小伙伴的接风咯,一切都不是事,好 ...

  5. C#设计模式-访问者模式

    一. 访问者(Vistor)模式 访问者模式是封装一些施加于某种数据结构之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保存不变.访问者模式适用于数据结构相对稳定的系统, 它把数据结 ...

  6. OpenCASCADE Job - Shoe Doctor

    鞋博士 鞋博士经过8年沉淀,在鞋类工业4.0全流程平台上积累了相当的技术实力,获投资商亲睐. 新的一年,在投资商协助下,将踏上新的征途,因此诚邀您加盟顶层技术合伙人. 如果您具备以下实力,我们期待您的 ...

  7. 用php实现一个简单的链式操作

    最近在读<php核心技术与最佳实践>这本书,书中第一章提到用__call()方法可以实现一个简单的字符串链式操作,比如,下面这个过滤字符串然后再求长度的操作,一般要这么写: strlen( ...

  8. 你真的会玩SQL吗?让人晕头转向的三值逻辑

    你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接.外连接 你真的会玩SQL吗?三范式.数据完整性 你真的会玩SQL吗?查询指定节点及其所有父节 ...

  9. 11 个很少人知道但很有用的 Linux 命令

    Linux命令行吸引了大多数Linux爱好者.一个正常的Linux用户一般掌握大约50-60个命令来处理每日的任务.Linux命令和它们的转换对于Linux用户.Shell脚本程序员和管理员来说是最有 ...

  10. php内核分析(一)-sapi_module_struct

    这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux 首先是寻找php的入口,php有很多种模式,apache,php-fpm, cli模式,我要入手的话,只能先从最简单的cl ...