目录
一.关于字符串的常量池
1.关于字符串产生的三种方式
2.关于字符串的常量池
3.直接赋值法和new的方式产生对象的区别
二.关于intern方法
1.情况一(已经包含)
2.情况二(已经包含)
3.情况三(未包含)
4.情况四
三.关于字符串的不可变性
1.了解字符串的不可变性
2.String对象不可变性的原因
3.相关的考题
一.关于字符串的常量池
1.关于字符串产生的三种方式
String s1="abc";
String s2=new String("abc");
char[] value={'a','b','c'};
String s3=new String(value);
2.关于字符串的常量池
当字符串采用直接赋值法的时候,JVM会维护一个字符串的常量池.
当字符串常量第一次产生的时候,就会产生字符常量,放入到常量池中
当使用直接复制法再次产生相同的对象时,若常量池中存在该值的对象直接复用常量池中的对象,并不会产生新的对象
3.直接赋值法和new的方式产生对象的区别
首先需要明确==比较的引用对象的地址是否相同,而不是值!!!
String s1 = "abc";
String s2 = new String("abc");
char[] value = {'a', 'b', 'c'};
String s3 = new String(value);
String s4 = "abc";
System.out.println(s1 == s2);//false
System.out.println(s1 == s4);//true
System.out.println(s2 == s3);//false
System.out.println(s3 == s4);//fasle
关于这样四对字符串==的比较,因为s1和s4都采用的是直接赋值法,所以他们对象的产生在常量池中,由于他们的值都是相同的,所以他们指向同一个地址的值.
而new对象的产生是在堆上,所以他们都是指向不同地址的对象,虽然他们的值都是一样的
二.关于intern方法
这是一个手动置入常量池的方法:调用此方法,会将当前字符串对象尝试置入常量池
主要分为两种情况:
1.若常量池中已经包含了当前对象的内容,不会将当前对象置入常量池,返回值是常量池中原有的对象地址
⒉若常量池中没有包含当前字符串对象的内容,就将当前对象置入常量池之中,返回值是当前对象的地址
1.情况一(已经包含)
String s1 = "abc";
String s2=new String("abc");
System.out.println(s1==s2);//false
s2=s2.intern();
System.out.println(s1==s2);//true
第一个s1==s2输出的是false,前边已经讲解过了,因为new对象是在堆上产生的,s1是在常量池中产生,==比较的地址,显然他们的地址不一样.
第二个s1==s2,因为值为"abc"已经存在与常量池中了,不会将s2对象置入到常量池中,但是结果的返回值为s1的地址,此时令s2=s2.intern(),s2的地址便与s1的地址相同
2.情况二(已经包含)
String s1 = "abc";
String s2=new String("abc");
System.out.println(s1==s2);//false
s2.intern();
System.out.println(s1==s2);//false
第一个s1==s2同上
第二个s1==s2,因为值为"abc"已经存在与常量池中了,不会将s2对象置入到常量池中,所以s2指向的还是s2原本的地址,s1与s2的地址不相同,所以返回的false
3.情况三(未包含)
char[] ch = {'a', 'b', 'c'};
String s2 = new String(ch);
s2.intern();
String s1 = "abc";
System.out.println(s1 == s2);//true
new出来s2的时候,常量池中并没有"abc"这个字符串,这个时候将s2压入到常量池中,然后s1直接赋值法直接就是和s2压入到常量池的地址一样了,具体看下图
4.情况四
String s2 = new String("abc");//"abc"是字符串常量,此时存在于常量池中了
s2.intern();//此时常量池中已经存在"abc",因为上边new对象的时候"abc"在常量池
String s1 = "abc";
System.out.println(s1 == s2);//false
因为刚开始new对象的时候是拿常量池中的"abc"new出来,所以此时常量池中已经有了"abc",这个时候s2的地址还是处在堆中,s1直接赋值"abc"在常量池中,所以s1和s2的地址不一样
三.关于字符串的不可变性
1.了解字符串的不可变性
String对象一旦产生,字符串对象中保存的值不可改变
String s1="hello";
s1+=",world";
s1+="!!";
System.out.println(s1);
很多人可能会疑问,这样s1的值不是改变了吗?
这样一段代码,改变的是s1的引用,它的指向一直在改变,不断指向新的字符串常量
不断在常量池中产生了新的字符串对象,str这个引用一直在变
字符串常量池中一旦字符串对象产生,内容不变的.这就是字符串对象的不可变性
2.String对象不可变性的原因
String对象的底层是用一个字符串数组进行保存的.
有些人可能以为String对象不可变是因为char数组是final修饰的,实则这是一个很大的误解,因为final修饰的引用对象,只是它的地址值不会发生改变,它的实际内容还是会改变的,下面这段代码便可以很好的证明
final char[] ch={'a','b','c'};
System.out.println(Arrays.toString(ch));//[a, b, c]
ch[0]='g';
System.out.println(Arrays.toString(ch));//[g, b, c]
实际上是因为字符串数组前边的private进行修饰,因为String类中没有具体的get和set方法,所以我们在外边的时候,无法直接对char数组进行改变
所以我们常用的subString,replace等操作字符串的方法,并没有对原来的字符串进行了改变,只是产生了新的字符串对象
String s="abcdefg";
String substring = s.substring(0, 5);
System.out.println(s);//abcdefg
3.相关的考题
public static void main(String args[]){
String str = new String("good");
char[ ] ch = { 'a' , 'b' , 'c' };
change(str,ch);
System.out.println(str);
System.out.println(ch);
}
public static void change(String str,char ch[ ]){
str = "abc";
ch[0] = 'g';
}
对于这样一段代码的输出是什么?
我们学习过了字符串的不可变性,进入到change方法之后,字符数组直接在堆进行改变,而对字符串的改变,直接产生了新的字符串,str指向了新的地址,而主方法里的str指向的还是原来的地址,所以最后输出
str="good"
ch="gbc"