//------------------------------------------------------------------------
It is not easy to write a blog. Please respect the author's Labor achievements. Reprinted please indicate the source: http://blog.csdn.net/chdjj
//------------------------------------------------------------------------
I think to study a class through the source code, we should first understand this class as a whole. For example, the inheritance system of this class, what superclasses are there, what interfaces are inherited, and what methods are provided. Then, we will go to the source code to understand the specific implementation process, so that it will not be messy ~
Note: The following source code is based on jdk1.7.0 _ 11
Let's take a look at the inheritance relationship between stringbuilder and stringbuffer:
Public final class stringbuilder // 1.5 start extends abstractstringbuilder implements java. Io. serializable, charsequencepublic final class stringbuffer // 1.0 has extends abstractstringbuilder implements java. Io. serializable, charsequence
Surprisingly consistent, both classes inherit abstractstringbuilder and implement the charsequence and serializable interfaces. The serializable interface is familiar to everyone and is a mark of serialization. Let's first look at the charsquence interface:
package java.lang;/** * @author Mike McCloskey * @since 1.4 * @spec JSR-51 */public interface CharSequence { int length(); char charAt(int index); CharSequence subSequence(int start, int end); public String toString();}
There are only four methods. This interface provides a uniform specification for different character sequence classes. We can see that this class has a tostring method, indicating it.
Use the default tostring instead of the default tostring.The other three methods are also easy to understand, and even the methods are often used in string, so we will not mention them here. After analyzing the charsequence interface, let's take a look at the abstractstringbuilder class. The name is an abstract class.
This class is only available in 1.5, obviously because of the addition of stringbuilder, the designer feels that the stringbuilder and stringbuffer can be generalized to extract the common part, which reflects the object-oriented design concept.
abstract class AbstractStringBuilder implements Appendable, CharSequence
Abstractstringbuilder implements the charsequence and appendable interfaces. The appendable's name must be related to the variability of stringbuilder and stringbuffer. Let's take a look at the appendable interface:
package java.lang;import java.io.IOException;public interface Appendable { Appendable append(CharSequence csq) throws IOException; Appendable append(CharSequence csq, int start, int end) throws IOException; Appendable append(char c) throws IOException;}
Sure enough, this interface provides different reload forms of the append method, and the returned values are all themselves (Note: here, I came up with a sentence,
Abstract classes are abstract classes, while interfaces are abstract behaviors.) Since abstractstringbuilder implements this interface, it must provide implementation. Next we will go back to the abstractstringbuilder class to see what this class has done. First look at the member variables:
/*** The value is used for character storage. */Char [] value; // array used to store characters/*** the count is the number of characters used. */INT count; // The total number of current characters
Note that the value array here is not clear as final (the value array of the string class is final), indicating that this array can be changed.Let's look at the constructor again:
AbstractStringBuilder() {} AbstractStringBuilder(int capacity) { value = new char[capacity]; }
One non-argument constructor provides the initial capacity of the char array, and the value array creates an object based on the capacity. Next, let's look at several important methods. First, the resizing method:
public void ensureCapacity(int minimumCapacity) { if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity); } /** * This method has the same contract as ensureCapacity, but is * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); }
The ensurecapacity method ensures the minimum capacity of the current character array is minimumcapacity. First, it determines whether this parameter is negative. If so, it returns. Otherwise, the ensurecapacityinternal method is called, this method internally determines whether the current capacity is smaller than minimumcapacity. If yes, it will be resized; otherwise, it will be returned. Expansion is implemented by calling the expandcapacity method. The internal implementation logic of this method is as follows:
First, try to expand the current array capacity to 2 times the original array capacity plus 2. If the new capacity is still smaller than the minimum value (minimumcapacity), set the new capacity to (minimumcapacity ), finally, determine whether the data overflows. If the data overflows, set the capacity to the maximum integer value 0x7fffffff.After the capacity is set, an array copy is performed.
Through this series of steps, we should be inspired. When creating stringbuilder or stringbuffer, we should try to estimate the length of the string as much as possible and give a reasonable initial value to avoid the efficiency problems caused by multiple resizing operations.Next, let's look at the append method (there are too many reloads. Here we only list ONE ):
public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }
The above append method accepts a string-type parameter, which is also the most frequently used overload mode. The internal logic is relatively simple. First, we need to judge the parameters (
When writing code, we should always pay attention to the robustness of the code and the value of parameters.). If the parameter is null, use the null character as the parameter. Then, determine whether to scale up. After the parameter is complete, perform a copy and then update the count. There is also a method called trimtosize, which can reduce the use of memory space. The array is replicated internally to release unused space:
public void trimToSize() { if (count < value.length) { value = Arrays.copyOf(value, count); } }
So far, abstractstringbuilder has completed the analysis. Okay,
Now we can analyze stringbuilder and stringbuffer! Let's take a look at this stringbuilder:The first is the constructor:
public StringBuilder() { super(16); } public StringBuilder(int capacity) { super(capacity); } public StringBuilder(String str) { super(str.length() + 16); append(str); } public StringBuilder(CharSequence seq) { this(seq.length() + 16); append(seq); }
We know through this Constructor
The default size of stringbuilder is 16.This is critical. You should remember that it would be excellent if you could say it during the interview ~ In addition to the default capacity, the constructor can also
Manually set the capacity. To avoid resizing, we strongly recommend that you estimate the approximate length of the string.~
The next step is the append method. We suppose we should call the append in abstractstringbuilder:
public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } public StringBuilder append(String str) { super.append(str); return this; }
This is actually the case. Let's take a look at the tostring method:
public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
Note: Here A New String object is returned!
Finally, let's take a look at the readobject and writeobject methods. Both methods are private, and their functions should be related to serialization.
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeInt(count); s.writeObject(value); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); count = s.readInt(); value = (char[]) s.readObject(); }
After the stringbuilder analysis is complete, it can be seen that there is no complicated logic in stringbuilder, And the implementation code is mainly in abstractstringbuilder. The following is an analysis of stringbuffer. We all know that the biggest difference between stringbuffer and stringbuilder is stringbuffer thread security. As you can imagine, the stringbuffer method should be locked. With this conjecture, open the stringbuffer source code: the constructor skipped because
Stringbuffer is exactly the same as stringbuilder, and the default capacity is 16.Here are a few methods:
public synchronized int length() { return count; }public synchronized void trimToSize() { super.trimToSize(); } public synchronized StringBuffer append(String str) { super.append(str); return this; }
Look, it's all locked. You may see that this method is not locked:
public StringBuffer append(CharSequence s) { // Note, synchronization achieved via other invocations if (s == null) s = "null"; if (s instanceof String) return this.append((String)s); if (s instanceof StringBuffer) return this.append((StringBuffer)s); return this.append(s, 0, s.length()); }
Well, I did not add it, but I did not note it. The designer said that the locking operation is implemented through other methods called internally, so there is no need to lock again here (the lock will waste resources ).
The rest of the code is useless. Except for the synchronized keyword, it is the same as stringbuilder.
The analysis is complete. The following is a summary:
1. stringbuilder was introduced by jdk1.5, and stringbuffer was available in 1.0;
2. The stringbuilder and stringbuffer are variable strings. You can modify the content of the string through append or insert;
3. stringbuffer is thread-safe, but stringbuilder is not. Therefore, stringbuffer is preferred in a multi-threaded environment, while stringbuilder is recommended in other cases because it is faster;
4. Both stringbuilder and stringbuffer inherit from the abstractstringbuilder class. abstractstringbuilder mainly implements the expansion, append, and insert methods. The parent classes directly called by the related methods of strngbuilder and stringbuffer.
5. The initial capacity of stringbuilder and stringbuffer is 16. programmers should manually set the initial value to avoid performance problems caused by multiple resizing operations;6. the resizing mechanism of stringbuilder and stringbuffer is as follows: First, try to expand the current array capacity to 2 times of the original array capacity plus 2. If the new capacity is still less than the predefined minimum value (minimumcapacity ), the new capacity is set to (minimumcapacity), and the overflow is determined. If yes, the capacity is set to the maximum integer value 0x7fffffff.