通常来说,我们认为hashCode不相同就为不同的对象。就这样由一段代码引发了一场讨论,代码如下:
1 @Test 2 public void stringCompare() { 3 String s1 = "test"; 4 String s2 = "test"; 5 String s3 = new String("test"); 6 String s4 = new String("test"); 7 8 System.out.println("value compare:"); 9 System.out.println("s1.equals(s2):" + s1.equals(s2)); 10 System.out.println("s2.equals(s3):" + s2.equals(s3)); 11 System.out.println("s3.equals(s4):" + s3.equals(s4)); 12 System.out.println("s1==s2:" + (s1 == s2)); 13 System.out.println("s2==s3:" + (s2 == s3)); 14 System.out.println("s3==s4:" + (s3 == s4)); 15 16 System.out.println("hashCode compare:"); 17 System.out.println("s1~s2:"+ (s1.hashCode() == s2.hashCode())); 18 System.out.println("s2~s3:"+ (s2.hashCode() == s3.hashCode())); 19 System.out.println("s3~s4:"+ (s3.hashCode() == s4.hashCode())); 20 }
有兴趣的话先猜一猜上面的打印结果。这段代码可以很好的说明String在创建对象的特殊性。
运行上面的代码结果为:
value compare: s1.equals(s2):true s2.equals(s3):true s3.equals(s4):true s1==s2:true s2==s3:false s3==s4:false hashCode compare: s1~s2:true s2~s3:true s3~s4:true
猜对了吗?会不会有点奇怪,接下来我将逐一说明,并解释String创建对象的机理。
首先我们知道equals的比较是两个对象的值是否相同,这个毋庸置疑,我们都赋值“test”,所以结果必然是三个true,这一点没有什么问题。
然后看“==”的比较,“==”是比较物理地址时候相同,也是比较是否为同一对象的手段。结果是s1和s2是true,后面的两个比较是false,这就说明s1和s2指向的是同一块地址。s3和s4是通过new出来的对象,我们知道new通常是开辟了新空间,而直接赋值是引用赋值。所以这一点也基本上没有什么问题,这就解释了s2==s3:false,s3==s4:false。
最后同通常我们认为hashCode不同就是不同的对象,但是这四个对象的hashCode是全部相同的。这似乎和使用"=="比较有点违背。但其实并不矛盾,必须明确一点是hashCode和物理地址没有必然的关系。之所以是这样,是因为Sting在创建对象时的机制有所不同。
重点:
JVM为String类型提供了一个字符池(jdk7之前在permgen,jdk7之后也是堆里)。每次在创建对象时,都会现在字符池中查找该字符串是否存在。这是大的前提逻辑,现在我们就一下几种情况分别说明:
1.创建一个对象 String s1 = new String("我是字符串"),遵循大逻辑,先在pool中检索,但其实无论pool中有没有,都会创建一个新的对象。不同的是:
a.pool中没有:
在pool中添加一个新的对象“我是字符串”,也就是说这里创建了两个对象,除了s1这个对象外,在pool中还创建了一个对象;这个时候创建s2 = "我是字符串"; 同样检查pool,发现已有,那s2指向pool中的“我是字符串”的地址。
b.pool中有:
则只创建一个对象及就是s1。这个时候创建s2= “我是字符串”;同样会检查pool发现存在,那s2直接指向pool中的地址。
2.创建一个String s1 = "我是字符串",遵循大逻辑,先在pool中检索:
a.pool中没有:
在pool中没有检索到“我是字符串”,那么在pool中添加一个字符串对象“我是字符串”,然后s1指向pool中的地址。这个时候如果创建String s2 = new String("我是字符串");检测pool中已有,同1.b中创建s1过程。
b.pool中有:
在pool中检索到,s1直接指向pool中的地址,如果创建String s2 = new String("我是字符串");同1.b中创建s1过程。
注意:
1.hashCode实际与物理地址没有必然关系,只是习惯上我们可以用hashCode判断是否为同一对象。
2.pool也是有大小限制的,对于不用的字符串对象,垃圾机器人会回收,释放空间。