java面試題String,StringBuilder,StringBuffer
面試的經歷中,相信大家都經常被問到這三者的區別,說到String我相信絕大多數的人都會說:"String是不可變的,final修飾的",你會看到面試官微微猥瑣一笑,接著問到:“final修飾的類就是不可變的嗎,那StringBuilder和StringBuffer不是final修飾的?”
1. 先來說說String
看下JDK1.7 String成員變數的原始碼
/** * @author Lee Boynton * @author Arthur van Hoff * @author Martin Buchholz * @author Ulf Zibis * @see java.lang.Object#toString() * @see java.lang.StringBuffer * @see java.lang.StringBuilder * @see java.nio.charset.Charset * @since JDK1.0 */ public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L;
可以看到String定義的成員變數value和hash,其中value是個位元組陣列,而且是final修飾,這個才是String不可變的關鍵點;
JDK1.7 String的部分註解
上面解釋了java為String提供了特殊的支援,例如:String a="a"; String b="b" ;當執行String c=a+b操作時,實際上是建立一個StringBuilder物件,再通過apend()進行拼接,最後呼叫toStirng()生成一個新的物件給c。The Java language provides special support for the string * concatenation operator ( + ), and for conversion of * other objects to strings. String concatenation is implemented * through the <code>StringBuilder</code>(or <code>StringBuffer</code>) * class and its <code>append</code> method. * String conversions are implemented through the method * <code>toString</code>, defined by <code>Object</code> and * inherited by all classes in Java. For additional information on * string concatenation and conversion, see Gosling, Joy, and Steele, * <i>The Java Language Specification</i>.
String提供修改內容的方法最終都是呼叫new String()。看下 String的部分註解
/** * The <code>String</code> class represents character strings. All * string literals in Java programs, such as <code>"abc"</code>, are * implemented as instances of this class. * <p> * 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. For example: * <p><blockquote><pre> * String str = "abc"; * </pre></blockquote><p> * is equivalent to: * <p><blockquote><pre> * char data[] = {'a', 'b', 'c'}; * String str = new String(data); * </pre></blockquote><p> * Here are some more examples of how strings can be used:
這裡定義了一個String str = "abc";相當於char data[] = {'a', 'b', 'c'};String str = new String(data);
再來看一個substring()方法的原始碼
/**
* Returns a new string that is a substring of this string. The
* substring begins at the specified <code>beginIndex</code> and
* extends to the character at index <code>endIndex - 1</code>.
* Thus the length of the substring is <code>endIndex-beginIndex</code>.
* <p>
* Examples:
* <blockquote><pre>
* "hamburger".substring(4, 8) returns "urge"
* "smiles".substring(1, 5) returns "mile"
* </pre></blockquote>
*
* @param beginIndex the beginning index, inclusive.
* @param endIndex the ending index, exclusive.
* @return the specified substring.
* @exception IndexOutOfBoundsException if the
* <code>beginIndex</code> is negative, or
* <code>endIndex</code> is larger than the length of
* this <code>String</code> object, or
* <code>beginIndex</code> is larger than
* <code>endIndex</code>.
*/
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
看完這些,你可以清楚的知道定義一個變數str="hello world",則是在記憶體中分配一個物件new String("hello world"),當你修改str="hello nimei",變數重新指向記憶體中新分配的new String("hello nimei");原來記憶體中的new String("hello world")還在那裡,沒有改變,等待垃圾回收。難道真的沒有辦法修改new String("hello world")物件中的值而不重新在記憶體中重新new一次嗎?讓我們來看看一個例子。
原來通過反射可以修改String物件中的內容,反射太強大了。
2.StringBuffer和StringBuilder
StringBuffer的部分原始碼
* @author Arthur van Hoff
* @see java.lang.StringBuilder
* @see java.lang.String
* @since JDK1.0
*/
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L;
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
/**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @exception NegativeArraySizeException if the <code>capacity</code>
* argument is less than <code>0</code>.
*/
public StringBuffer(int capacity) {
super(capacity);
}
/**
* Constructs a string buffer initialized to the contents of the
* specified string. The initial capacity of the string buffer is
* <code>16</code> plus the length of the string argument.
*
* @param str the initial contents of the buffer.
* @exception NullPointerException if <code>str</code> is <code>null</code>
*/
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
StringBuilder的部分原始碼
* @author Michael McCloskey
* @see java.lang.StringBuffer
* @see java.lang.String
* @since 1.5
*/
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
/**
* Constructs a string builder with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuilder() {
super(16);
}
/**
* Constructs a string builder with no characters in it and an
* initial capacity specified by the <code>capacity</code> argument.
*
* @param capacity the initial capacity.
* @throws NegativeArraySizeException if the <code>capacity</code>
* argument is less than <code>0</code>.
*/
public StringBuilder(int capacity) {
super(capacity);
}
/**
* Constructs a string builder initialized to the contents of the
* specified string. The initial capacity of the string builder is
* <code>16</code> plus the length of the string argument.
*
* @param str the initial contents of the buffer.
* @throws NullPointerException if <code>str</code> is <code>null</code>
*/
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
StringBuffer和StringBuilder的這部分原始碼基本一樣,建構函式初始化大小都為16,都是繼承了AbstractStringBuilder。
看看AbstractStringBuilder定義成員變數的原始碼
/**
* A mutable sequence of characters.
* <p>
* Implements a modifiable string. At any point in time it contains some
* particular sequence of characters, but the length and content of the
* sequence can be changed through certain method calls.
*
* @author Michael McCloskey
* @author Martin Buchholz
* @author Ulf Zibis
* @since 1.5
*/
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
AbstractStringBuilder中定義的變數value,是個位元組陣列,和String的成員變數value相比,String的value是final修飾的,所以StringBuffer和StringBuilde的內容可以變。
在對比下StingBuffer和StringBuilder的實現其他細節,以append()方法為例。
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
兩者最大的區別是:StingBuffer所有的實現的方法都是sychronized修飾的,StringBuilder則不是。2.String、StringBuffer和StringBuilder的總結
1). String和StringBuffer、StringBuilder相比,String是不可變的,String的每次修改操作都是在記憶體中重新new一個物件出來,而StringBuffer、StringBuilder則不用,並且提供了一定的快取功能,預設16個位元組陣列的大小,超過預設的陣列長度時,則擴容為原來位元組陣列的長度*2+2。2). StringBuffer和StringBuilder相比,StringBuffer是synchronized的,是執行緒安全的,而StringBuilder是非執行緒安全的,單執行緒情況下效能更好一點;使用StringBuffer和StringBuilder時,可以適當考慮下初始化大小,較少擴容的次數,提高程式碼的高效性。