Java学习笔记(1)String常见面试知识总结

简介(摘自Java源码)

Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared.

从这句话当中,我们可以知道String是一个不可变对象,对象的值不可以被改变。因为这一点,所以String是线程安全的。然后Stringbuffer是可变对象。

【问题1】什么是不可变对象(immutable object),不可变对象有什么好处,在什么情况下应该用,或者更具体一些,Java的String类为什么要设成immutable类型?

答:从String类的源码中,我们可以知道不可变对象是被final关键字修饰的类产生的对象,其成员变量也是被final修饰的,因此该类不能被修改也不能被继承。

不可变对象的好处主要体现在以下两方面:

1. 不可变对象是线程安全的,可用于多线程。在多线程通信中,某一个变量很可能被多个线程进行修改,因此是不安全的。而不可变对象不能被修改,安全;

2.不可变对象可以提高拷贝时的效率,不可变意味着拷贝时只需要拷贝地址,效率高。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public final class String  
  2.     implements java.io.Serializable, Comparable<String>, CharSequence {  
  3.     /** The value is used for character storage. */  
  4.     private final char value[];  
  5.   
  6.     /** Cache the hash code for the string */  
  7.     private int hash; // Default to 0  
  8.   
  9.     /** use serialVersionUID from JDK 1.0.2 for interoperability */  
  10.     private static final long serialVersionUID = -6849794470754667710L;  
  11.   
  12. ...........................................  
  13. }  
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public final class StringBuffer  
  2.     extends AbstractStringBuilder  
  3.     implements java.io.Serializable, CharSequence  
  4. {  
  5.   
  6.     /** 
  7.      * A cache of the last value returned by toString. Cleared 
  8.      * whenever the StringBuffer is modified. 
  9.      */  
  10.     private transient char[] toStringCache;  
  11.   
  12.     /** use serialVersionUID from JDK 1.0.2 for interoperability */  
  13.     static final long serialVersionUID = 3388685877147921107L;  
  14. }  

而StringBuffer是继承了AbstractStringBuilder

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. abstract class AbstractStringBuilder implements Appendable, CharSequence {  
  2.     /** 
  3.      * The value is used for character storage. 
  4.      */  
  5.     char[] value;  
  6.   
  7.     /** 
  8.      * The count is the number of characters used. 
  9.      */  
  10.     int count;  
  11. }  

从上面一段代码中再次可以看出,String和StringBuffer的区别,String是不可变对象,StringBuffer是不可变对象

【问题2】String和StringBuffer以及StringBuilder的区别是什么?

 

从问题1中我们已经看到了其中一个主要的区别:String是不可变对象,StringBuffer是可变对象。这一点也导致了其他的很多的区别。

看下面两个例子字符串拼接:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. String str = new String ("Stanford  ");  
  2.      str += "Lost!!";  
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. StringBuffer str = new StringBuffer ("Stanford ");  
  2.      str.append("Lost!!");  

问:哪个效率高?

 

乍一眼看过去还真以为第一种方式高于第二种方式,其实不然。我们看两张程序的字节码。

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. new #7 <Class java.lang.String>  
  2. 3 dup   
  3. 4 ldc #2 <String "Stanford ">  
  4. 6 invokespecial #12 <Method java.lang.String(java.lang.String)>  
  5. 9 astore_1  
  6. 10 new #8 <Class java.lang.StringBuffer>  
  7. 13 dup  
  8. 14 aload_1  
  9. 15 invokestatic #23 <Method java.lang.String valueOf(java.lang.Object)>  
  10. 18 invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>  
  11. 21 ldc #1 <String "Lost!!">  
  12. 23 invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>  
  13. 26 invokevirtual #22 <Method java.lang.String toString()>  
  14. 29 astore_1  
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. new #8 <Class java.lang.StringBuffer>  
  2. 3 dup  
  3. 4 ldc #2 <String "Stanford ">  
  4. 6 invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>  
  5. 9 astore_1  
  6. 10 aload_1   
  7. 11 ldc #1 <String "Lost!!">  
  8. 13 invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>  
  9. 16 pop  

在第一种方式中,由于String是不可变对象,所以在第10行的时候,它将string对象转换成了StringBuffer对象,再把字符拼接上去,在29行的时候重新转换成String对象,返回给用户。而在第二种方式中,StringBuffer是可变对象,直接拼接字符串并返回。从这一点很显然看出,在常见的字符串拼接问题上,String的效率比StringBuffer低。

 

 

再说StringBuilder,StringBuilder和StringBuffer一样都是可变对象,那有什么区别呢?我们稍微比较一下源码就可以看到StringBuffer中的大多数函数都带有关键字:synchronized和StringBuilder中没有。因此,StringBuilder在单线程中效率高于StringBuffer,而StringBuffer可以用于多线程。

【问题3】String两种初始化方式的区别

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. String str = new String("abc");  
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. String str = "abc";  

 


这是我们常见的两种String对象的初始化方式,区别何在?首先我们来看一段代码。

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. String str1 = new String("abc"); //jvm 在堆上创建一个String对象     
  2.     
  3.  //jvm 在strings pool中找不到值为“abc”的字符串,因此     
  4.  //在堆上创建一个String对象,并将该对象的引用加入至strings pool中     
  5.  //此时堆上有两个String对象     
  6. Stirng str2 = "abc";     
  7.     
  8.  if(str1 == str2){     
  9.          System.out.println("str1 == str2");     
  10.  }else{     
  11.          System.out.println("str1 != str2");     
  12.  }     
  13.   //打印结果是 str1 != str2,因为它们是堆上两个不同的对象     
  14.     
  15.   String str3 = "abc";     
  16.  //此时,jvm发现String Pool中已有“abc”对象了,因为“abc”equals “abc”     
  17.  //因此直接返回str2指向的对象给str3,也就是说str2和str3是指向同一个对象的引用     
  18.   if(str2 == str3){     
  19.          System.out.println("str2 == str3");     
  20.   }else{     
  21.          System.out.println("str2 != str3");     
  22.   }     

输出结果是什么?

 

再来看一段代码

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. String str1 = new String("abc"); //jvm 在堆上创建一个String对象     
  2.     
  3. str1 = str1.intern();     
  4. //程序显式将str1放到String Pool中,intern运行过程是这样的:首先查看String Pool     
  5. //有没“abc”对象的引用,没有,则在堆中新建一个对象,然后将新对象的引用加入至     
  6. //String Pool中。执行完该语句后,str1原来指向的String对象已经成为垃圾对象了,随时会     
  7. //被GC收集。     
  8.     
  9. //此时,jvm发现String Pool中已有“abc”对象了,因为“abc”equals “abc”     
  10. //因此直接返回str1指向的对象给str2,也就是说str2和str1引用着同一个对象,     
  11. //此时,堆上的有效对象只有一个。     
  12. Stirng str2 = "abc";     
  13.     
  14.  if(str1 == str2){     
  15.          System.out.println("str1 == str2");     
  16.  }else{     
  17.          System.out.println("str1 != str2");     
  18.  }     


打印结果又是什么?

 

这个问题就涉及到Java的内存模型了,简单的说来区别在于:

第一种初始化方式会立即在对上创建一个String对象,然后将该对象的引用返回给用户。对于第二种,jvm首先会在String Pool判断是否存在该String对象。如果有,则返回已有的String对象,如果没有,则在heap中重新创建对象,将其引用返回给用户同时将该引用添加至String Pool中。而第一种方式是不会主动把对象添加至String Pool中的。因此第一段程序的输出结果是

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. str1 != str2  
  2. str1 == str2  

那么第二段程序的输出结果呢?前面我们说到第一种方式是不会主动把对象添加至String Pool中的,但有个例外,手动调用intern()方法,会强制将该对象的引用加入到String Pool里面。因此第二段程序的结果是

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. str1 == str2  

【问题4】String Pool和Const Pool的区别和联系

 

了解Java内存模型的都知道,在Java内存中有一块区域叫做方法区。方法区主要用于存储一些常量和静态变量。而String Pool和Const Pool的区别简单的说来,String Pool是Const Pool的一部分,Const Pool包括很多种常量,整型啊之类的,而String Pool只是用来专门存储String常量。

没有写完,持续更新。。。。

posted @ 2016-12-17 19:16  天涯海角路  阅读(156)  评论(0)    收藏  举报