一、String类
我们都知道String是一个不可变对象,用final修饰,这说明它不能被继承和修改这是Stirng类的部分源码,可以看到,它用final声明了一个char类型的数组,实际上,它就是用来保存字符串的,这也就是为什么字符串的值是不能被改变的原因,再看看下面String类中两个相关方法的代码
可以看到,对字符串的操作最后都是生成一个新的字符串对象,原先的字符串对象并没有被改变,所有对字符串的更改操作都会产生一个新的字符串对象
二、字符串常量池
在进一步了解String之前,我们先来说说字符串常量池字符串是我们经常使用的,如果每次修改一个字符串就产生一个新对象的话,这无疑是很耗资源的。JVM为了对这种情况进行优化,使用了字符串常量池。
1、直接创建
String str1 = “helloworld”String str2 = “helloworld”
当我们以字面量的形式创建字符串str1时,JVM会先从字符串常量池中查找是否存在"helloworld"这个对象,这里没有,所以会在池中创建一个helloworld对象,然后将该对象的引用返回给str1,在创建str2时,同样会去常量池中查找是否有hellloworld对象,因为前面str1已经创建了一个helloworld对象,所以此时会直接返回该对象的引用,此时的str1和str2是相等的
2、以new的形式创建
String str3 = new String("helloworld")当我们以new的形式创建str3时,JVM会从池中查找有没有helloworld,这里因为在创建str1时已经创建了helloworld对象,所以不会在池中创建对象了,如果池中没有helloworld对象的话,在池中创建一个helloworld对象,然后拷贝一份到堆中,并将堆中的引用返回。此时的str3与str1是不同的对象。要注意使用new产生的字符串的引用是堆中的引用,而不是池中的引用。
由此可知,使用new创建字符串对象的时候,实际上产生了两个对象
---------在继续了解更多关于与字符串与常量池之间关系的前,先来说说“+”连接符
我们都知道“+”在字符串中是用来拼接多个字符串的
String s1 = "java"
String s2 = "php"
String s3 = "java" + "php"
String s4 = s1 + s2
s3和s4都是用“+”拼接而成的,s3中的“java”和“php”都是字符常量,在编译时就已经确定了,实际上s3就相当于直接写s3 = "javaphp",因为它是两个字符串常量拼接而成的,JVM会在编译期进行优化;s4是由两个字符引用拼接而成的,因为s1和s2是字符引用,在编译时无法确定具体的值,必须在运行时才能确定;当“+”拼接多个字符引用的时候,实际上底层是以第一个“+”左边的第一个引用来创建一个StringBuilder对象,然后再通过append()方法来拼接其他字符引用,最后再通过toString()方法返回一个新的String对象,所以这里的s4可以简单的看作是s4 = new StringBuilder(s1).append(s2).toString()
3、用 “+”连接常量
String str4 = "hello" + "world"str4是由两个字符常量"hello"、"world"连接而成的,它们在编译期就已经是确定的了,因为str4由字符串常量组成,所以它本身也是一个字符串常量,就相当于str4 = "helloworld",也会在池中查找是否存在对应的对象,此时的str4与str1是一样的
4、用“+”连接字符引用
String str5 = "hello"String str6 = "world"
String str7 = str5 + str6
str5和str6都是编译时就已经确定的了,而str7是有str5和str6两个字符引用连接而成的,上面已经说了对于“+”拼接多个字符引用的情况,这里也是一样的道理,最后产生的str7是一个新的String对象,所以str7与str1是不同的对象
5、final修饰的String
final String str8 = "fin"final String str9 = Person.name
String str10 = “a” + str8
String str11 = “a” + str9
对于final修饰的String,如果修饰的是一个普通值,则在编译时就已经被解析为常量了,str8就是可以看成是一个常量,str10就可以直接看成是afin,而str9是通过对象(方法也不可以)来赋值的,这在编译时是无法确定的,只能在运行时才能确定,不能看成常量
三、小结
1、String是不可变的,有关String的修改操作全部都会产生一个新的String对象,原本的String对象不变,如果没被使用就等待被GC回收2、用字面量的方式创建String对象时,会先从字符串常量池中查找是否存在相同字符串的对象引用,如果有就返回引用,没有 就创建对象后再返回对象的引用
3、用new的方式创建String对象时,会先从字符串常量池中查找是否存在相同字符串的对象引用,如果没有就创建,如果有就拷贝一份到堆中,返回堆中对象的引用
4、使用String类的intern()方法可以往字符串常量池中添加对象:调用intern的字符串,会去字符串常量池中查找是否有与自己相等的字符串对象,如果有就返回该对象,没有就把自己添加到池中,并返回对应的引用
5、使用“+”来拼接字符串引用,需要在运行时才能够确定引用的字符串的值,底层是使用StringBuilder来实现的,最后获得的是一个新的对象