因为入门时学的是C语言,在自学java时,发现java的String和c中着极大的差别,当时半知半解,现在重新整理下这方面知识点.
1. String和int/double等不同点之一是:String是不可变类。
一旦创建了 String 对象,那它的值就无法改变了
如果我们队String对象进行如下操作:
String a="asd";
System.out.println(a);
a="qwe";
System.out.println(a);
两次打印结果不同,但是事实上a只是指向堆内存中的引用,存储的是对象在堆中的地址,而非对象本身,a本身存储在栈内存中。
一种比较简单的识别方法是打印查看两个a的hashcode值:
String a="asd";
System.out.println(a);
System.out.println("a.hashcode="+a.hashCode());
a="qwe";
System.out.println(a);
System.out.println("a.hashcode="+a.hashCode());
String c="asd";
System.out.println("c.hashcode="+c.hashCode());
输出结果:
asd
a.hashcode=96882
qwe
a.hashcode=112383
c.hashcode=96882
这里显示两次打印的a的hashcode值不同,这说明两个a指向的并不是同一个对象,但是c和第一个打印的a的hashcode值相同。
需要注意的是String对Object中的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类中有个私有实例字段hash表示该串的哈希值,在第一次调用hashCode方法时,字符串的哈希值被计算并且赋值给hash字段,之后再调用hashCode方法便可以直接取hash字段返回。
在这里:
两个对象相等则它们的hashCode值必然相等;
两个hashCode值相等的对象不一定相等;
2. String常用的两种创建方法如下:
String asd=new String(“haha”);
String zxc=“haha”;
这两种方法之间有着一定的区别
使用new方法的时候,会在堆内存申请一块内存存储字符串“haha”,asd指向其内存块对象。同时还会检查字符串常量池中是否含有“haha”字符串,若没有则添加“haha”到字符串常量池中。所以 new String()可能会创建两个对象。
但是在第二种情况中, 先检查字符串常量池中是否含有“haha”字符串,如果有则直接指向, 没有则在字符串常量池添加“haha”字符串并指向它.所以这种方法最多创建一个对象,有可能不创建对象。可以结合1中的例子。
3. String中使用加号拼接字符串
String中使用 + 字符串连接符进行字符串连接时,连接操作最开始时如果都是字符串常量,编译后将尽可能多的直接将字符串常量连接起来,形成新的字符串常量参与后续连接(通过反编译工具jd-gui也可以方便的直接看出);
接下来的字符串连接是从左向右依次进行,对于不同的字符串,首先以最左边的字符串为参数创建StringBuilder对象,然后依次对右边进行append操作,最后将StringBuilder对象通过toString()方法转换成String对象(注意:中间的多个字符串常量不会自动拼接)。
也就是说:
String k = "n1" + "n2 " + i + "n3" + "n4" + j; 实质上的实现过程是:
String k = new StringBuilder("n1n2").append(i).append("n3").append("n4").append(j).toString();
当使用+进行多个字符串连接时,实际上是产生了一个StringBuilder对象和一个String对象。
在比较简单的额字符串连接中“+”和直接使用StringBuilder的差别不大,但是在比较复杂的情况下,“+”可能会产生更多的StringBuilder对象,例如:
public class TestComplexPlus
{
public static void main(String[] args)
{
String s = "";
Random rand = new Random();
for (int i = 0; i < 10; i++)
{
s = s + rand.nextInt(1000) + " ";
}
System.out.println(s);
}
}
for循环处相当于:
for(int i = 0; i < 10; i++){
s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();
}
在这种情况中,每次for循环执行字符串拼接都会产生一个StringBuilder对象,不断产生垃圾,占用资源过多。