java String 深入理解
- 说出下面程序的输出
class StringEqualTest { public static void main(String[] args) { String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program" + "ming";
String s4=new String(s2);
String s5 = "Programming";
System.out.println(s1 == s2);//false
System.out.println(s4 == s2);//false
System.out.println(s1 == s3);//true
System.out.println(s1 == s1.intern());//true } }
解释:
在此引入常量池这个概念:
常量池(constant
pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量
首先,我们要知道Java会确保一个字符串常量只有一个拷贝。
因为例子中的s1和s3中的”
Programming
”都
是字
符串常量,它们在编译期就被确定了,所以s1==s3为true;而”Program”和”ming”也都是字符
串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s3也同样在编译期就被解析为一个字符串常量,所以s3也是常量池中
”kvill”的一个引用。
所以我们得出s5==s1==s2;
用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String()
创建的字符串不放入常量池中,它们有自己的地址空间。
所以有s2!=s4
String对象的intern方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用。
再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个 方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用, 如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;
案例
String s0= “kvill”; String s1=new String(”kvill”); String s2=new String(“kvill”); System.out.println( s0==s1 ); System.out.println( “**********” ); s1.intern(); s2=s2.intern(); //把常量池中“kvill”的引用赋给s2 System.out.println( s0==s1); System.out.println( s0==s1.intern() ); System.out.println( s0==s2 ); 结果为: false ********** false //虽然执行了s1.intern(),但它的返回值没有赋给s1 true //说明s1.intern()返回的是常量池中”kvill”的引用 true
关于equals()和==:
这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true;String类已经重写过了equals方法。
。而==是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用
而在符合数据类型中,则equals和==都是比较两对象的地址是否相同,除非重写equals,详见hashcode与equals的区别。
- 使用equals方法比较两个相同的StringBuffer对象
StringBuffer sb0=new StringBuffer("hello"); StringBuffer sb1=new StringBuffer("hello"); System.out.println(sb0==sb1); System.out.println(sb0.equals(sb1)); System.out.println(sb0.toString().equals(sb1.toString())); false false true
StringBuffer 类没有重写Object类下的equals方法,所以实质还是==比较,所以如果需要比较两个StringBuffer对象是否相等则toString()方法转为String。
.关于String是不可变的
这一说又要说很多,大家只要知道String的实例一旦生成就不会再改变了,比如说:String str=”kv”+”ill”+” “+”ans”;
就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” “ 生成 ”kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的“不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原 因了,因为StringBuffer是可改变的。- 可不可以在分割字符串是使用多个分割符
String str="2012-05-25 13:24:16"; String[] s=str.split("-|:|\\s"); for(String temp:s){ System.out.print(temp+" "); } 2012 05 25 13 24 16
关键点就是在split()方法中使用多个分割符;其实split()方法里的分割符参数是一个正则表达式,所以可以使用或操作符(|)链接多个分割符,这样在分割的时候就会按照所有的分割符对字符串进行分割了。\s表示空格,不过在字符串使用过程中还需要在\s之前添加转义字符"\",否则会出现编译错误。