前言
自己毕业后做JAVA开发已经2年了,从最初的JAVA WEB开发,到投入到Hadoop大数据开发潮流中,越来越发现自己的JAVA基础,数据结构,多线程等等,是那么的脆弱!还有什么比基础更加重要呢,应该静下心来,去学习它们,就从这一篇博客开始吧~
话题一:==与equals
让我们首先看看Object类中定义的equals方法的源码: 在Object类中,equals就是利用==实现对象之间的比较的。 我们知道对于基本数据类型,==比较的就是他们的值是否相等,那么对于对象类型的==比较的是什么呢? 对于对象类型,a==b比较的就是a和b是否就是同一个对象! 很显然,我们常常需要做的就是比较2个对象是不是从业务的角度相等,而并不是想比较他们是不是同一个对象。因此我们需要override equals()方法。 比如String就是有这样的需求,因此我们来看看她是怎么复写equals()的: String的obj1.equals(obj2)实现的步骤: 第一,利用==看一下,obj1和obj2是不是同一个对象,如果是那么则肯定equals的! 第二,他们的类型相同吗?长度相同吗? 第三,开始逐个字符进行==比较,如果出现不同,则可以得到结果,否则继续比较。 感悟: 以后,我们去实现自定义类的equals方法时,是不是要考虑下: 他们是同一个对象吗? 他们是同一个数据类型吗? 他们自身有哪些关键属性,是否通过比较这些关键属性来快速得到结论呢? |
话题二:equals与hashCode
hashCode(),和equals()一样,都是在Object中定义的,我们来看看吧: 很不幸,在Object中他是一个native调用,我们还是看看String中的hashCode是如何是实现的: 分析: 第一,可以看出,一旦一个String在调用hashCode方法后,那么hash属性就会得到一次赋值,那么以后再次调用这个hashCode方法,就会直接返回hash属性,而不再计算了。 第二,从hashCode方法内部的for循环可以看出,这个hash值,是一个综合值,是综合了每一个字符计算得到的。 想法: 如果2个String他们的字符内容完全一样,那么显然他们的hashCode理应一样! 如果2个String他们的字符内容不同,那么hashCode就不同吗?根据上面的分析,hashCode是一个综合值,字符内容不同,完全可以得到一样的hashCode! 那如果hashCode一样,那也不到任何结论! 那如果hashCode不一样,那么对于String而言,他们的字符内容肯定不同! 关系: String的字符内容的比较正是equals方法,那么equals与hashCode是什么关系呢? 以前,我以为自定义一个类,就应该重写这2个方法,他们应该是“天生一对”。其实不是! 上面的话题,其实已经说明,我们重写equals方法是为了从业务角度来实现2个对象的比较,那hashCode是干嘛的呢? 我们知道算法的基础是数字,比如hash算法。在map中,很显然如果KEY是一个对象的话,那么这个对象应该实现HASH的查找和写入,可是他是一个对象啊,他应该提供一个数字,以便于实现HASH算法! 也就是说,hashCode是为了给对象一个数字,这个数字用于HASH这种数据结构中,去实现快速定位对象用的!hashCode定位链表后,其实还是得循环链表利用equals进行比较。 那equals和hashCode其实没有什么关系,不是非要在一起的,一个是为了比较对象是不是业务上相等,一个是为了HASH快速定位而存在的。 |
话题三:String的==迷惑
简单看一段代码吧: 结果是: true false 对于String而言,equals我们犯不了什么错误,可是==是经常让我们迷惑的地方! 第一,String在编译期存在优化行为 例如,String s = "a" + "b"; 在编译生成的CLASS文件中已经是这样的了: String s = "ab"; 那么上面的代码中,s4会在编译期就变成"abc"吗?不会,因为s2是一个变量,只能在运行期间确定,而且并没有强制约束他不能变。而s5在编译期间就已经确定为"abc",因为s3是final的,变不了的! 是这样吗? 我们来利用反汇编命令javap命令看下: 很多指令,我也不懂,我们只看能看得懂的地方,或者猜一猜~ 从0到8,根据注释String ab,应该是对应于代码的 从而也验证了,String存在编译期合并的事实。 从9到28,发生了new 操作,还有append,这应该对应于: 这说明,对于String而言,变量与常量的这种“+”操作,实际上在底层是搞成了一个StringBuilder进行append,最后在来一个toString得到的! 从30到32,根据注释String abc,应该对应于代码: 这也说明了,final变量与常量的这种“+”操作,直接编译合并! 第二,String存在常量池的概念 在编译期间就确定的String常量,是存在String pool中的,而且是保证全局唯一的。也就是说上面代码中,s1/s2指向String Pool中的同一个string。 相信到这里以后String的==以后不会在迷糊了,呵呵~ |
话题四:String + VS StringBuffer.append 谁更快?
我们都说,StringBuffer.append好,她更快? 根据话题三的分析,String的“+”可能在编译期间就合并了,而StringBuffer.append是必须在运行期间进行的操作,很显然,从这种角度而言,应该String的“+”更快些! 那么以前是怎么回事呢? 对于第一个for循环而言,注意到 s += i; 根据上面的分析,这是要发生new StringBuilder,append,toString操作的。 也就是,我们在一个大大的for循环中进行了new操作!真是不可思议!这并不是我们预料的! 我们暂且不考虑StringBuilder扩容机制,要知道随着循环的进行,字符串内容越来越大,new产生的垃圾对象也越来越大,可能内存不足了,GC要开始忙碌了!这应该就是程序性能下降的原因。 而对于第二个for循环而言, 没有new操作,虽然涉及到扩容,但是没有那么多的垃圾。 |
这篇博客就到这里了,收获很多,希望对大家也有帮助~
本文出自 “努力奋斗” 博客,请务必保留此出处http://zhangfengzhe.blog.51cto.com/8855103/1683059
Java功底篇系列-01-从==/equals/hashCode开始
原文地址:http://zhangfengzhe.blog.51cto.com/8855103/1683059