Java中String类的一些发现和思考
遇到的问题
之前在网上浏览别人家的博客时,看到这样的小程序:
public static void main(String[] args){
String s1 = new String("cjt");
String s2 = new String("cjt");
System.out.println(s1==s2);
}
当时看到作者的答案是false,大脑瞬间没反应过来,觉得应该是作者手误,写错了。
回头越想越不对劲,就自己在eclipse上测试了一下,发现打印出来的答案真的是false。
public static void main(String[] args){
String s1 = new String("cjt");
String s2 = new String("cjt");
String s3 = "cjt";
String s4 = "cjt";
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
System.out.println(s4.hashCode());
System.out.println(s1==s2);
System.out.println(s2==s3);
System.out.println(s3==s4);
}
//控制台输出的结果是:
98541
98541
98541
98541
false
false
true
顿时让我困惑不解(不是说String类是不可修改的吗?既然已经创建了一个”cjt”的字符串对象,并且把引用复制给了s1,那么常量池中已经存在了”cjt”这个字符串了,再执行String s2 = new String(“cjt”)语句时,应该直接把常量池中的”cjt”字符串的引用直接赋值给s2,这样,s1和s2的引用就是指向同一个”cjt”字符串对象,打印的结果就是true,再说,既然打印出来的结果是false,为什么hashCode的值是一样的呢?)。后来百度了一下,发现我之前的理解是错误的= =。
自我的理解
String类创建字符串有两种方式。
第一种是直接把字符串赋值给String变量,如下:
String s = "cjt";
第一种方式,JVM直接在字符串常量池中查找有没有”cjt”的字符串,如果存在,则直接返回引用给变量s,如果不存在,就直接新建一个”cjt”字符串,然后把引用赋值给变量s。
第二种是通过创建String对象的方式,如下:
String s = new String("cjt");
而第二种方式,JVM也会在字符串常量池中查找有没有”cjt”的字符串,如果存在,则直接引用这个字符串在堆上新建一个String对象,如果不存在,就在常量池中新建一个”cjt”字符串,然后引用这个字符串在堆上新建一个String对象。
所以,通过第一种方式创建的字符串,引用的都是字符串常量池中的字符串,如果字符串相同,则引用相同,而通过第二种方式创建的,每创建一个新的String对象,在堆上就有一个新的地址存放String对象,所以,每次new String(),得到的引用是不同的。
那么,引用不同,为什么hashCode的值是一样的呢?
根据String类中重写的hashCode源码:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
从源码中可以看到,String类中的hashCode()的值跟字符串的内容相关,由于本例中字符串都是”cjt”,所以hashCode的值也一样。
总结
①String类是final类型的,不可改变的。
②String类创建字符串的方式有两种,它们有一定的区别。