JAVA中String类深度探究

来源:互联网 发布:淘宝怎么看历史价格 编辑:程序博客网 时间:2024/05/16 15:13

     学过java的人应该对String类再熟悉不过了,但是真正了解它的人又有多少?

     先看几个读程序题:
     1、
        String str1 = "ab";
        String str2 = "a" + "b";
        String str3 = "a";
        str3 += "b";
        System.out.println((str1 == str2) + "," + str1.equals(str2));//out:true true
        System.out.println((str1 == str3) + "," + str1.equals(str3));//out:false true

     2、
        String a = "ab";
        String bb = "b";
        String b = "a" + bb;
        System.out.println(a == b); //out:false

     3、
        String a = "ab"; 
        final String bb = "b";
        String b = "a" + bb;
        System.out.println(a == b);//out:true

     4、
        String a = "ab"; 
        String b = "a" + new String("b"); 
        System.out.println((a == b)); //out:false

     5、
        String a = "ab";
        final String bb = getBB();
        String b = "a" + bb;
        System.out.println((a == b)); //out:false
        private static String getBB() {
            return "b";
        }

     6、
        String str1="ab";
        String str2=new String("ab");//两者的区别在哪里?这条语句到底生成了几个String对象?

     ok,如果能够把上面的题都做对,那证明对String类了解得很深了。否则,请接着看。

     首先,了解下String类。
     1、String类是final类型的,不允许继承和修改该类。
     2、String s=new String("ab");//生成了两个对象。String类的源代码中:
    
image     可以看出,上条语句实质上是先String ori="ab",然后再新建一个String对象(该对象的空间是在堆上申请的,通过对ori.value的访问来填充该对象的值) 。注意:String ori="ab";这条语句来创建一个String对象,这种方式是String特有的,并且它与new的方式存在很大区别。在JVM中存在着一个字符串常量池,保存着很多String对象,并且可以被共享使用,这样做提高了效率!由于String类是final的,所以一经创建就不可改变,因此不存在因共享而带来的混乱。

     当代码String ori="ab"+"cd";执行时,发生了什么?3个String对象(http://z-jq1015.iteye.com/blog/248599)吗?其实还是1个!因为编译器已经做了优化(后面将提到),和String ori="abcd";是等价的了。JVM首先会在字符串常量池中查找是否已经存在值为"abcd"的对象(通过String类中的equal方法来判断),如果存在,则直接返回该对象的引用,不再新创建对象;若不存在,则先创建这个String对象(char value[]来保存),然后放入字符串常量池,而后返回该对象的引用!

     3、String不属于8种基本数据类型,String变量是一个对象。因此,它的默认值是null,不是空!

     4、new String()和new String("")都是声明了一个空字符串,不是null!

     5、对String的比较有两种方式。一种是使用==,对对象的地址进行比较;另一种是equals()方法,它覆盖了Object中的equals方法,实现了对String对象内容的比较。

     先举个例子来详细说明(如下图所示):

     image

     图片上面方框中的内容为源代码,下面方框的内容是.class文件中对于的java版的汇编代码。
     可以得到如下结论:
     1、在java中,维持着一个常量池(constant pool)。它是指在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。在String类中,主要依靠这个常量池来保存字符串常量,在前面已经提到了。
     2、可能注意到"+"连接的字符串,有的时候编译器优化了。如上所示,"a"+"b"被优化为"ab"。那编译器的优化原则是怎么样的呢?
     String+String(是常量字符串间连接,如"xx"+"xx")可以,那String+基本类型呢?

     String a = "a1";
     String b = "a" + 1;
     System.out.println((a == b)); //true

     String a = "atrue";
     String b = "a" + true;
     System.out.println((a == b)); //true

     String a = "a3.4";
     String b = "a" + 3.4;
     System.out.println((a == b)); //true
     由此可知String+基本类型也可以!

     但是,最开始的例2中那种,bb是变量,因此不能优化。(注:若不是常量,则这个值在编译时是不定的)。如果String+String是在运行时进行的,则会产生新的对象,而不是从JVM中取得。
     例3中,final变量,编译时就知道该值了,因此可以优化。像例5中,需要函数调用,当然无法优化。
     因此,如果不能在编译时优化,则通过"+"来连接就会在运行时产生新的对象!

     重点解释下例1,其实,在编译后,常量池中有"ab","a","b"这三个字符串常量。显然str1、str2指向池中的"ab",而str3则是新构建了String对象。运行时,首先从常量池中取得"a"和"b",然后"a"+"b"得到"ab",发现常量池中已有了,所以不再新建String对象,而是返回常量池中的"ab"对象的引用AB。根据这个对象在堆中生成了一个String变量通过AB来构建(new String(String ori)这个构造函数来在堆中新建对象),最后返回该引用,就str3所指向的真正的对象!

   
这也是为什么在进行大量字符串连接时选择用StringBuffer或StringBulider,而不用"+"来拼接!

   学习过程中参考了如下资料,在此表示感谢!  

   http://news.softhouse.com.cn/news/show/60046.html
  
http://news.softhouse.com.cn/news/show/26380.html
  
http://digest.softhouse.com.cn/digest/show/21250.html
  
http://huaxia524151.iteye.com/blog/800303
  
http://www.mianwww.com/html/2011/03/8195.html

   版权所有,未经允许,严禁拷贝,转载等。如需引用,请务必保留原地址!
  
附:
   让人很气愤的是,有些网站随便拷贝我的文章,如
http://www.crazycoder.cn/Bo-abstracts-selected/Article168604.html,对这种行为表示强烈谴责!!
  
image  
   以及http://edu.codepub.com/comment/?keyid=phpcms-content-title-30655&verify=553e373c0dfd1341857bfec6daa37d95
   image

    中国的盗版真是猖獗!!!国人的悲哀,好不容易写点东西就这么白白得被窃取了!!居然是百度和google搜索结果的第一名!!无语~~