These two classes seem to me to be twins, so I put them together this time.

1. An overview

Let’s look at the definitions of two classes:

public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable.CharSequence{

public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable.CharSequence{
Copy the code

On the surface, the two classes are identical except for the class name.

  • Final, indicating that the String class cannot be inherited.

  • CharSequence provides read-only access to character arrays of different types.

  • Implement Serializable interface, indicating Serializable.

AbstractStringBuilder > > < AbstractStringBuilder > > < AbstractStringBuilder > > < AbstractStringBuilder > > < AbstractStringBuilder > >

abstract class AbstractStringBuilder implements Appendable.CharSequence {
Copy the code

The Appndable interface defines methods to append char sequences and values to objects that are reachable for CharSequence.

Although this class is abstract, there is only one abstract method, just a toString() method, which is obviously a general-purpose utility method class. Subclasses of different features simply inherit and override the required methods.

AbstractStringBuilder class annotations show that this abstract class implements a mutable sequence of characters that can be changed by calling certain methods. Like String, there is an array of type char.

Since this is a generic tool method, the specific differences should be in the subclasses.

Method 2.

I’m going to focus on Stringbuilder here, and unless otherwise specified, the default implementation is to add Synchronized to StringBuffer.(Because in single-threaded situations, Stringbuilder is faster, and StringBuffer is sometimes not Is’ synchronous ‘).

The constructor is the default length of 16, or the length of the pass-argument string plus 16.

The main ones are append() and insert(), one for append and one for insert. In abstract classes, there are many overloaded methods for append() and insert(). Here are just a few of the key ones. The rest are pretty much the same.

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
Copy the code
  1. If yes, it will be addedn,u,l,lFour characters, this method click in to see.
  2. The main isensureCapacityInternal(count + len)Capacity expansion method.
private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value,newCapacity(minimumCapacity)); }}Copy the code

Determine whether there is sufficient capacity for the content to be added. NewCapacity ():

private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)? hugeCapacity(minCapacity) : newCapacity; }Copy the code

If the required capacity is greater than the original double plus two, the new capacity is whichever is required, otherwise it is double plus two. In the last step, the check length, there may be memory overflow.

After capacity expansion, the next class is to add values:

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
Copy the code

The method of system.arrayCopy () is used for copying.

The insert() method does much the same, except that at the end of system.arrayCopy (), the parameters are passed differently.

Here’s another interesting point:

@Override
public synchronized String toString(a) {
    if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache, true);
}
Copy the code

Using a cache array in toString of a StringBuffer can speed up retrieving strings because the String constructor it uses does not involve copying arrays.

3. The gossip

Both classes have constructor patterns in the methods of append() so that they can be chained.

The StringBuffer described above is sometimes not really ‘synchronized ‘, and it is mainly in the case of multiple calls to different methods of the StringBuffer, which does not guarantee atomicity.

In practice,StringBuffer is used sparingly, and another official recommendation is for single threads. Mainly used in a large number of string concatenation scenarios, similar to circular concatenation and so on. Using String will result in frequent String instances being created, which will have an impact on memory.

It is not hard to see that in the source code, the methods are well written and well thought out (overloading is good), including some abstract superclass design.