Java语法总结 - 字符串

Posted on 2007-10-21 12:47 Raylong 阅读(5630) 评论(17)  编辑  收藏 所属分类: Java语法总结
Java语法总结 - 字符串

Java的String太特别了,也太常用了,所以重要。我初学Java就被它搞蒙了,太多混淆的概念了,比如它的不变性。所以必须深入机制地去理解它。


1、String中的每个字符都是一个16位的Unicode字符,用Unicode很容易表达丰富的国际化字符集,比如很好的中文支持。甚至Java的标识符都可以用汉字,但是没人会用吧(只在一本清华的《Java2实用教程》看过)。

2、判断空字符串。根据需要自己选择某个或者它们的组合
    if ( s == null )    //从引用的角度
    if ( s.length() == 0 )     //从长度判别
    if ( s.trim().length () == 0 )     //是否有多个空白字符
trim()方法的作用是是移除前导和尾部的Unicode值小于'"u0020'的字符,并返回“修剪”好的字符串。这种方法很常用,比如需要用户输入用户名,用户不小心加了前导或者尾部空格,一个好的程序应该知道用户不是故意的,即使是故意的也应该智能点地处理。
判断空串是很常用的操作,但是Java类库直到1.6才提供了isEmpty()方法。当且仅当 length() 为 0 时返回 true。

3、未初始化、空串""与null。它们是不同的概念。对未初始化的对象操作会被编译器挡在门外;null是一个特殊的初始化值,是一个不指向任何对象的引用,对引用为null的对象操作会在运行时抛出异常NullPointerException;而空串是长度为0的字符串,和别的字符串的唯一区别就是长度为0。
例子:
    public class StringTest{
        static String s1;
        public static void main(String[] args) {
            String s2;
            String s3 = "";
            System.out.print(s1.isEmpty());     //运行时异常
            System.out.print(s2.isEmpty());     //编译出错
            System.out.print(s3.isEmpty());     //ok!输出true
        }
    }

4、String类的方法很多,在编写相关代码的时候看看JDK文档时有好处的,要不然花了大量时间实现一个已经存在的方法是很不值得的,因为编写、测试、维护自己的代码使项目的成本增加,利润减少,严重的话会导致开不出工资……

5、字符串的比较。
Java不允许自定义操作符重载,因此字符串的比较要用compareTo() 或者 compareToIgnoreCase()。s1.compareTo(s2),返回值大于0则,则前者大;等于0,一般大;小于0,后者大。比较的依据是字符串中各个字符的Unicode值。

6、toString()方法。
Java的任何对象都有toString()方法,是从Object对象继承而来的。它的作用就是让对象在输出时看起来更有意义,而不是奇怪的对象的内存地址。对测试也是很有帮助的。

7、String对象是不变的!可以变化的是String对象的引用。
String name = "ray";
name.concat("long");  //字符串连接
System.out.println(name); //输出name,ok,还是"ray"
name = name.concat("long");  //把字符串对象连接的结果赋给了name引用
System.out.println(name);  //输出name,oh!,变成了"raylong"
上述三条语句其实产生了3个String对象,"ray","long","raylong"。第2条语句确实产生了"raylong"字符串,但是没有指定把该字符串的引用赋给谁,因此没有改变name引用。第3条语句根据不变性,并没有改变"ray",JVM创建了一个新的对象,把"ray","long"的连接赋给了name引用,因此引用变了,但是原对象没变。

8、String的不变性的机制显然会在String常量内有大量的冗余。如:"1" + "2" + "3" +......+ "n" 产生了n+(n+1)个String对象!因此Java为了更有效地使用内存,JVM留出一块特殊的内存区域,被称为“String常量池”。对String多么照顾啊!当编译器遇见String常量的时候,它检查该池内是否已经存在相同的String常量。如果找到,就把新常量的引用指向现有的String,不创建任何新的String常量对象。

那么就可能出现多个引用指向同一个String常量,会不会有别名的危险呢?No problem!String对象的不变性可以保证不会出现别名问题!这是String对象与普通对象的一点区别。

乍看起来这是底层的机制,对我们编程没什么影响。而且这种机制会大幅度提高String的效率,实际上却不是这样。为连接n个字符串使用字符串连接操作时,要消耗的时间是n的平方级!因为每两个字符串连接,它们的内容都要被复制。因此在处理大量的字符串连接时,而且要求性能时,我们不要用String,StringBuffer是更好的选择。

8、StringBuffer类。StringBuffer类是可变的,不会在字符串常量池中,而是在堆中,不会留下一大堆无用的对象。而且它可将字符串缓冲区安全地用于多个线程。每个StringBuffer对象都有一定的容量。只要StringBuffer对象所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。这个固定的容量是16个字符。我给这种算法起个名字叫“添饭算法”。先给你一满碗饭,不够了再给你一满碗饭。
例子:
    StringBuffer sb = new StringBuffer();    //初始容量为 16 个字符
    sb.append("1234");    //这是4个字符,那么16个字符的容量就足够了,没有溢出
    System.out.println(sb.length());    //输出字符串长度是4
    System.out.println(sb.capacity());    //输出该字符串缓冲区的容量是16

    sb.append("12345678901234567");        //这是17个字符,16个字符的容量不够了,扩容为17+16个字符的容量
    System.out.println(sb.length());    //输出字符串长度是17
    System.out.println(sb.capacity());    //输出该字符串缓冲区的容量是34

    sb.append("890").reverse().insert(10,"-");    
    System.out.println(sb);        //输出0987654321-09876543214321

字符串的长度和字符缓冲区的容量是两个概念,注意区别。
还有串联的方式看起来是不是很酷!用返回值连接起来可以实现这种简洁和优雅。

10、StringBuilder类。 从J2SE 5.0 提供了StringBuilder类,它和StringBuffer类是孪生兄弟,很像。它存在的价值在于:对字符串操作的效率更高。不足的是线程安全无法保证,不保证同步。那么两者性能到底差多少呢?很多!
请参阅:http://book.csdn.net/bookfiles/135/1001354628.shtml
实践:
单个线程的时候使用StringBuilder类,以提高效率,而且它的API和StringBuffer兼容,不需要额外的学习成本,物美价廉。多线程时使用StringBuffer,以保证安全。

11、字符串的比较。
下面这条可能会让你晕,所以你可以选择看或者不看。它不会对你的职业生涯造成任何影响。而且谨记一条,比较字符串要用equals()就ok了!一旦用了“==”就会出现很怪异的现象。之所以把这部分放在最后,是想节省大家的时间,因为这条又臭又长。推荐三种人:一、没事闲着型。二、想深入地理解Java的字符串,即使明明知道学了也没用。三、和我一样爱好研究“茴”字有几种写法。

还是那句老话,String太特殊了,以至于某些规则对String不起作用。个人感觉这种特殊性并不好。看例子:
例子A:
    String str1 = "java";
    String str2 = "java";
    System.out.print(str1==str2);
地球上有点Java基础的人都知道会输出false,因为==比较的是引用,equals比较的是内容。不是我忽悠大家,你们可以在自己的机子上运行一下,结果是true!原因很简单,String对象被放进常量池里了,再次出现“java”字符串的时候,JVM很兴奋地把str2的引用也指向了“java”对象,它认为自己节省了内存开销。不难理解吧 呵呵
例子B:
    String str1 = new String("java");
    String str2 = new String("java");
    System.out.print(str1==str2);
看过上例的都学聪明了,这次肯定会输出true!很不幸,JVM并没有这么做,结果是false。原因很简单,例子A中那种声明的方式确实是在String常量池创建“java”对象,但是一旦看到new关键字,JVM会在堆中为String分配空间。两者声明方式貌合神离,这也是我把“如何创建字符串对象”放到后面来讲的原因。大家要沉住气,还有一个例子。
例子C:
    String str1 = "java";
    String str2 = "blog";
    String s = str1+str2;
    System.out.print(s=="javablog");
再看这个例子,很多同志不敢妄言是true还是false了吧。爱玩脑筋急转弯的人会说是false吧……恭喜你,你会抢答了!把那个“吧”字去掉你就完全正确。原因很简单,JVM确实会对型如String str1 = "java"; 的String对象放在字符串常量池里,但是它是在编译时刻那么做的,而String s = str1+str2; 是在运行时刻才能知道(我们当然一眼就看穿了,可是Java必须在运行时才知道的,人脑和电脑的结构不同),也就是说str1+str2是在堆里创建的,s引用当然不可能指向字符串常量池里的对象。没崩溃的人继续看例子D。
例子D:
    String s1 = "java";
    String s2 = new String("java");
    System.out.print(s1.intern()==s2.intern());
intern()是什么东东?反正结果是true。如果没用过这个方法,而且训练有素的程序员会去看JDK文档了。简单点说就是用intern()方法就可以用“==”比较字符串的内容了。在我看到intern()方法到底有什么用之前,我认为它太多余了。其实我写的这一条也很多余,intern()方法还存在诸多的问题,如效率、实现上的不统一……
例子E:
    String str1 = "java";
    String str2 = new String("java");
    System.out.print(str1.equals(str2));
无论在常量池还是堆中的对象,用equals()方法比较的就是内容,就这么简单!看完此条的人一定很后悔,但是在开始我劝你别看了……

后记:用彪哥的话说“有意思吗?”,确实没劲。在写这段的时候我也是思量再三,感觉自己像孔乙己炫耀“茴”字有几种写法。我查了一下茴 ,回,囘,囬,还有一种是“口”字里面有个“目”字,后面这四个都加上草字头……

返回索引页


Feedback

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 13:46 by 翔南
恩 又对String有了更深的理解
第一次听说还有个常量池的东西,学习了.
还有那个isEmpty()方法很nice!

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 13:51 by Raylong
@翔南
isEmpty()早该有了,对不?

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 13:59 by 翔南
呵呵 的确
要不每次都得自己判断..

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 14:13 by 海边沫沫
写得蛮好。看了收获不小。

博主文笔很不错。

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 15:40 by Raylong
@海边沫沫
谢谢夸奖,这个还没总结完呢,还要加更多的内容。

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 17:07 by 推荐java开源项目介绍大全《敏捷之源 - agilesource.org》
文笔不错~

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 19:20 by Raylong
@推荐java开源项目介绍大全《敏捷之源 - agilesource.org》
谢谢!我会继续努力补完的,到时候语法基础就夯实了

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 21:27 by 极品飞车
文章很不错,就是黑底白字的颜色搭配阅读相当不舒服,最标准的颜色搭配是白底黑字

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-21 23:04 by Raylong
@极品飞车
多数网站都是白底黑字吧,但是黑色能给我宁静的感觉。我也在想一种搭配,把白字换成更柔和的颜色,不知道你有什么建议?

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-22 09:13 by zhrb
还不错,帮忙顶顶。呵呵

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-22 11:33 by Raylong
@zhrb
谢谢。看了你写的接口和抽象类,以前对此我理解的是不够全面。呵呵

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-22 12:15 by zhrb
@Raylong
我写得那些只是部分的思考,后来陆续看了一些文章才发现那些理解还远远不够
可能更深入的思考要在读过、写过大量的源代码之后才有可能实现
你的blog很好,我会一直关注的。呵呵

# re: Java语法总结 - 字符串  回复  更多评论   

2007-10-22 12:29 by Raylong
@zhrb
是啊,我现在想明白了,上来就看TIJ是行不通的,根本理解不到那个层次。必须从基础开始多做练习,写代码,看代码。

# re: Java语法总结 - 字符串  回复  更多评论   

2007-11-04 21:10 by 糖果屋-Tocas

看了博主几篇文章,感觉很好,很严谨,讲问题也讲的很透彻!fighting

# re: Java语法总结 - 字符串  回复  更多评论   

2007-12-26 14:59 by pm6422
null是一个特殊的初始化值,是一个不指向任何对象的引用,因此不能够使用该类的所有方法,如果使用则会在运行时抛出NullPointerException,但是可以对null的对象进行一部分操作;如:String str=null; str=str+"aa";

# re: Java语法总结 - 字符串  回复  更多评论   

2009-01-28 22:58 by li002qwe
public class StringTest{
static String s1;
public static void main(String[] args) {
String s2;
String s3 = "";
System.out.print(s1.isEmpty()); //运行时异常
System.out.print(s2.isEmpty()); //编译出错
System.out.print(s3.isEmpty()); //ok!输出true
}
}

为什么s1.isEmpty()会出现运行时异常呢? 实例变量s1应该会被默认赋值吧。。。不理解。。希望给予回答

# re: Java语法总结 - 字符串  回复  更多评论   

2012-05-09 11:40 by xc
“sb.append("12345678901234567"); //这是17个字符,16个字符的容量不够了,扩容为17+16个字符的容量
System.out.println(sb.length()); //输出字符串长度是17
System.out.println(sb.capacity()); //输出该字符串缓冲区的容量是34”

应该是33吧

只有注册用户登录后才能发表评论。


网站导航: