[Java基础]关于String的一些基础

参考:
1.几张图轻松理解String.intern()
2.Java技术——你真的了解String类的intern()方法吗

关于String,会有这么几个经常提起的概念:==关系符,+号、new、intern,final,字面量、static,方法参数。
String是一种特殊的Java对象。注意,它归根结底还是一种对象。
首先要知道:
new的含义是:在堆上创建一个新对象。
使用引号声明的字符串都是会直接在字符串常量池中生成的;

new一个String的方式虽然不提倡也不常见,但是要知道,new语句之后,是真切地在堆上产生了一个新的对象,该对象的内容即值是一个字符串。

case1:通过字面量赋值创建字符串

String s1 = "abc";
String s2 = "abc";
问:s1 == s2?

答案:true;
原因:对于对象而言,==关系符比的是引用。所谓引用就是对象的内存地址。对于字符串来说,通过字面量赋值创建字符串时,会优先在常量池中查找是否已经存在相同的字符串,倘若已经存在,栈中的引用直接指向该字符串;倘若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串。所以,在字面量s1赋完值之后,常量区已经生成了字符串”abc”;待到字面量s2创建字符串时,不需要再生成字符串,s2和s1均指向常量池中这个对象。
这是基础,后面的变种,都是在此基础上的种种特殊情况。请记住!

case2:通过new的方式创建字符串

String s3 = new("abc");
String s4 = "abc";
问:s3 == s4?

答案:false;
原因:new的含义使然。String再特殊,也得遵守。这也是一个基本结论,请记住。

case3:+号,及编译器的优化

String s5 = "abc";
String s6 = "a"+"bc";
String s7 = "a";
String s8 = "bc";
String s9 = s7 + s8;
问:s5 == s6? s5 == s9?

答案:true,false。
原因:在JAVA 1.6之后,常量字符串的“+”操作,编译阶段直接会合成为一个字符串。于是语句{s6 = “a”+”bc”;}在编译期间就被等效成了{s6 = “abc”;}但是对于两个字符串变量相加+的{s9 = s7 + s8},在编译期间是被等效成了{s9 = new StringBuilder().append(s7).append(s8);}所以s9是在堆上创建的一个新对象,故两者引用不等。

case4:+号,final,及编译器的优化

String s10 = "abc";
final String s11 = "a";
final String s12 = "bc";
String s13 = s11 + s12;
问:s10 == s13?

答:true。
原因:与case3中语句{s9 = s7 + s8;}不同的是,s7和s8是final类型的;对于final字段,编译期直接进行了常量替换,而对于非final字段则是在运行期进行赋值处理的。这导致在编译期,语句{s13 = s11 + s12;}被等效成了{s13 = “a” + “bc”};进而会等效为{s13 = “abc”;}

case5:intern,以及Java版本1.7

首先,看一下String类的intern函数:

public native String intern();

它的作用是:
对于堆中的字符串对象,可以通过 intern() 方法来将字符串添加到常量池中,并返回指向该常量的引用。
JDK1.6以及以前版本中,常量池是放在 Perm 区(属于方法区)中的。
从JDK 1.7及之后,HotSpot 将常量池从永久代移到了元空间。正因为如此,JDK 1.7 后的intern方法在实现上发生了比较大的改变,JDK 1.7及之后,intern方法还是会先去查询常量池中是否已经存在该对象内容的字符串,如果存在,则返回常量池中的这个字符串的引用,这一点与之前没有区别;区别于JDK1.6,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用,该引用指向堆上的这个String对象。
现将结论放出来:
对于JDK1.6及之前版本来说,对于堆中的字符串对象s,s.intern()指向的一直是常量区的引用,而s指向的是堆上的引用,两者不等;
对于JDK1.7及之后版本来说,对于堆中的字符串对象s,在调用s.intern()时,如果常量区不存在s内容的字符串,则s.intern() == s为true;如果已存在s内容的字符串,则s.intern() == s为false。

case5.1:调用intern时在常量池中已经存在目标字符串
String s18 = "abc";
String s14 = new("abc");
String s16 = new("abc");
问:s14.intern() == s16.intern()? s14.intern() == s18?

答案:都是true,true
原因:执行语句{s14.intern()}时,发现在常量区已经存在字符串对象”abc”了;所以返回的引用与s18指向这同一个字符串;到执行语句{s16.intern()}以及再次执行语句{s14.intern()}时,都是一样。故均相等。

case5.2:调用intern时在常量池中没有找到目标字符串
String s14 = new("abc")+new("def");
问:s14.intern() == s14?

答案:这就要根据JDK版本来看了。若是JDK1.6,执行语句{s14.intern()}时,因为没发现,所以会在常量区生成一个字符串”abcdef”,并返回该常量区字符串的引用。所以JDK1.6环境下答案应该是:false。
在JDK1.7及之后的版本环境下,答案是验证过的,是:true。

case6:static

    static String s12 = "a";
    static String s13 = "bc";
    static String s14 = s12 + s13;
    public void stringTest(){
    String s15 = "abc";
    Log.e("chwn","s14 == s15?"+(s14 == s15));
    }

答:false。
原因:关键字static用在String对象前,相比起其他对象,没有任何特殊性。

case7:String作为方法参数

public void stringTest(){
    String s16 = new("abc");//"abc";
    changStringTest(s16);
    Log.e("chwn","s16:"+s16);
    }
public void changStringTest(String s){
s = "def";
}
问 printf的结果是啥

答案:abc
原因:**String对象是不可变的。因为String对象具有只读特性,所以指向它的任何引用都不可能改变它的值,因此也就不会对其他的引用有什么影响。**String类型的对象作为方法参数时,尽管传递的是对象s16,但实际上传递的是引用。方法changStringTest的参数s是字符串”abc”的一个新的引用。s的改变,不会影响到同样是引用的s16。

你可能感兴趣的:(Java)