今天看书看到这样一段代码,感觉十分有趣,在这放一下
private static void test(){
String a = "a" + "b";
String b = "ab";
System.out.println(a == b);
}
呐尼,这边怎么用了”==”,不是应该用equals()么?
那么其实,这段代码运行的最终结果是true,如果你了解原理,这篇文章完全可以跳过,如果是猜的小伙伴,有兴趣可以继续往下看一看
想了解为什么会是true,需要了解以下几个知识点:
1.“==”是做什么的?
2.equals()呢?
3.a和b在内存中是什么样存在的?
4.编译时的优化方案
==
首先,”==”在Java中,原始类型如byte、short、int、long、float、double、boolean、char都是直接比较它们的值是否相等,这个不用多说。
但如果是引用类型的对象,那么比较的其实是两个对象的地址。
equals()
equals()方法在jdk源码的Object类中被定义,它可以被重写。
来看下equals()在Object类中的实现:
public boolean equals(Object obj) {
return (this == obj);
}
原来源码的原始实现用的就是==比较两个对象的地址是否一致,so easy。那么再来看下String类中的equals方法是怎么样的呢?
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) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
大致看一下代码,分为如下几个步骤:
1. 先判断两个对象地址是否相同,如果地址相同,则返回true
2. 再判断传入对象是否为String,若不是则返回false
3. 判断传入的String长度与当前String是否一致,若不一致则返回false
4. 循坏匹配两个String的每个字符是否相等,若不相等,返回false
5. 直到所有字符比较完成,返回true
好了,现在理解了String类中的equals如何工作了
hashCode()
讲到equals()不得不提一下hashCode(),这是一个Object类提供的native(本地)方法,作用是获取对象的hashCode,它的返回值与System.identityHashCode(Obj)一致。是一组由对象头部一部分二进制位组成的数字,用于标识该对象,但绝不等价于地址。这个hashCode主要在HashMap、HashSet等集合类中的对象写入和查找等算法中用于算法快速定位数据之用。
Ps:在JDK1.7中,如果Hash相关的集合的key是String类型,不再使用hashCode,而是用一种hash32属性。
编译时优化
JVM有时会在编译时对代码进行自动优化,如针对代码String a = "a" + "b"
,因为”a”和”b”都是常量,他们都储存在常量池中,在编译时其实已经转化成了String a = "ab"
,无须运行时再计算,同样的道理还有int i = 1 + 3
,编译时自动优化为int i = 4
。
当然编译器做的优化远远不止这些,这边只是稍微提及一下。
String的”+”操作
这边稍微瞄一眼String的”+”操作的工作原理,例如代码String b = a + "b"
,其中a是一个String类型的字符串,因为a是一个变量,因此这段代码编译好之后是这个样子滴:
StringBuilder temp = new StringBuilder();
temp.append(a).append("b");
String b = temp.toString();
关于StringBuilder类的append源码在这就不分析了。(源码什么的 还是很有趣的!)
这边提一个intern()
方法,这个方法的源码注释是这个样子的
* @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings.
大致意思就是,返回一个与当前字符串内容相同,且在Perm Gen(永久代,即内存的永久保存区域)中保证全局唯一的字符串。
wtf,这是什么意思呢?
首先,Perm Gen是常量池中的一块,它对于同一个值的字符串保证全局唯一。如果一个字符串调用intern(),JVM首先会去常量池中通过equals()选择等值的String,若存在,返回常量池中这个String的地址,如果未找到,会创建等值的字符串,再返回新创建空间的地址。
到这里,有点小累,来个例题放松放松吧
public class test {
public static String getA(){return "a";}
public static void main(String[] args) {
String a = "a";
final String _a = "a";
String b = a + "b";
String c = _a + "b";
String d = getA() + "b";
String e = new String(b);
String compare = "ab";
System.out.println(b == compare);
System.out.println(c == compare);
System.out.println(d == compare);
System.out.println(e == compare);
System.out.println(d.intern() == compare);
System.out.println(e.intern() == compare);
System.out.println(e.intern() == d.intern());
}
}
答案是:false true false false true true true