1. 程式人生 > >java面試題String,StringBuilder,StringBuffer

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的部分註解

 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>.
上面解釋了java為String提供了特殊的支援,例如:String a="a";  String b="b" ;當執行String c=a+b操作時,實際上是建立一個StringBuilder物件,再通過apend()進行拼接,最後呼叫toStirng()生成一個新的物件給c

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時,可以適當考慮下初始化大小,較少擴容的次數,提高程式碼的高效性。