@[Toc]

This article is based on JDK1.8


The String class is by far the most used class in our development. For its understanding, just limited to the API understanding is not enough, must be a certain source of its study.


A, pre –

The String class is a very special class in Java. Although it is not a basic data type, it is a special class in reference data types through some processing. Before we learn about it, let’s learn a little about the JVM.

  • Method Area: Method, when the virtual machine to load a class file, it will be from the class files contain binary data parsing type information, then put these types of information (including information static variables, constants, etc.) on the method of area, the memory area Shared by all threads, method of local area is a special area of memory, This is called the Constant Pool.
  • Heap: The Heap is the largest chunk of memory managed by the Java virtual machine. The Java heap is an area of memory shared by all threads in Java.
  • Stack: a Stack, also called a Stack or virtual machine Stack. The JVM allocates a stack for each newly created thread. In other words, for a Java program, its execution is done through operations on the stack. The stack holds the state of the thread in frames. The JVM performs only two operations on the stack: the frame-by-frame push and the off-stack operation. We know that the method that a thread is executing is called the current method of that thread.
  • Program Count Register: a Program Count Register. The JVM supports multiple threads running at the same time, and when each new thread is created, it gets its own PC register (program counter). If the thread is executing a Java method (non-native), the value of the PC register will always point to the next instruction to be executed. If the method is native, the value of the program counter register is not defined. The JVM’s program counter register is wide enough to hold a pointer to a return address or native.
  • Native Stack: a Stack of methods that stores the call status of local methods.

The constant pool is the data that is identified at compile time and stored in a compiled.class file. It includes constants in classes, methods, interfaces, and so on, as well as string constants. Java divides memory into heap memory for objects and stack memory for primitive type variables and references to objects.


Second, String class source code analysis

1, String class inheritance

public final class String
    implements java.io.Serializable.Comparable<String>, CharSequence {
Copy the code

Look at the definition of the String class:

  • String is a final class that cannot be inherited
  • The String class implements the Java.io.Serializable interface, which can be serialized
  • The String class implements Comparable

    , which can be used to compare sizes (ASCII codes of individual characters in order)
  • The String class implements the CharSequence interface, which represents an ordered sequence of characters, because String is essentially a char array

The String class inheritance relationship is shown as follows:


The detailed inheritance relationship of String class is shown as follows:



2. Member variables

Let’s start with what member variables the String class has:

// An immutable character array for character storage
private final char value[];
// Cache the hash code of the string
private int hash;   // Default is 0
// Implement serialized identity
private static final long serialVersionUID = -6849794470754667710L;
Copy the code

Among these member variables, focus should be on:

  • Private Final char Value [] Private final char Value [] Private final char Value []


3. Construction method

  • Parameterless constructor
   /** * initializes a String by assigning the value of the "" empty String to the value of the instance, which is also a null character. Since strings are immutable, this method is not required ** /
    public String(a) {
        this.value = "".value;
    }

Copy the code

Example:

String str = new String()
str = "hello";
Copy the code
  • 1. Create an empty String
  • 2. Then create a “hello” in the constant pool and assign it to the second String
  • 3. Pass a reference to the second String to the first String

This actually creates two objects


  • A parameterized constructor that takes String
  /** * Assigns the value and hash of the parameter to the instance object as initializations * is equivalent to making a deep copy of the parameter String */
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
Copy the code

Example:

String str=new String("hello")
Copy the code

An object is created.


  • A parameterized constructor that takes an array of characters
   /** * the argument to an array of char characters * assigns the value of the array to an immutable array * why not assign the value directly? * Because the char value[] argument is mutable, assigning it directly will affect the newly generated String when the array of arguments changes, thus breaking the "immutability" of the String. * /
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }
Copy the code


  • A parameterized constructor that takes an array of characters, specifying the start and end positions
   /** * The argument is an array of char characters,offset(start position,offset),count(number) ** based on the char array,count from offset to form a new String **/
    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return; }}// Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }
Copy the code


  • A parameterised constructor that takes an int array and specifies the start and end positions
  	/** * the argument is an int array of characters,offset,count * int the elements of the array are the corresponding ASCII integer values */
    public String(int[] codePoints, int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= codePoints.length) {
                this.value = "".value;
                return; }}// Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count;

        // Pass 1: Compute precise size of char[]
        int n = count;
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                continue;
            else if (Character.isValidCodePoint(c))
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }
Copy the code


Some of them are labeled obsolete and we don’t look at them anymore.


  • A parameterized constructor that takes a byte array, specifying the start and end positions and character encoding
  	/** * The argument is a byte array,offset(start position,offset), length, and character encoding ** Pass a byte array, length from offset, the character encoding format is charsetName, such as UTF-8 */
    public String(byte bytes[], int offset, int length, String charsetName)
            throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        // Check whether the byte array is out of bounds
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(charsetName, bytes, offset, length);
    }

Copy the code

Decode methods for StringCoding classes:

    static char[] decode(String charsetName, byte[] ba, int off, int len)
        throws UnsupportedEncodingException
    {
        StringDecoder sd = deref(decoder);
        String csn = (charsetName == null)?"ISO-8859-1" : charsetName;
        if ((sd == null) | |! (csn.equals(sd.requestedCharsetName()) || csn.equals(sd.charsetName()))) { sd =null;
            try {
                Charset cs = lookupCharset(csn);
                if(cs ! =null)
                    sd = new StringDecoder(cs, csn);
            } catch (IllegalCharsetNameException x) {}
            if (sd == null)
                throw new UnsupportedEncodingException(csn);
            set(decoder, sd);
        }
        return sd.decode(ba, off, len);
    }
Copy the code

The rest of the constructors that construct strings from byte arrays call this method, which I won’t go into here.


  • A parameterized constructor that takes a StringBuffer
   /** * The argument type is StringBuffer, and copies the StringBuffer value array to the String value array * thread safe **/
    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); }}Copy the code

Using StringBuilder for parameters is similar, but not thread-safe.


4. Length/void

  • length()
   /** * returns the length of the value array */
    public int length(a) {
        return value.length;
    }
Copy the code
  • isEmpty()
     /** * value Specifies whether the array length is 0 */
    public boolean isEmpty(a) {
        return value.length == 0;
    }
Copy the code


5. Take characters

  • charAt(int index)
   /** * get the index value array character */
    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }
Copy the code
  • codePointAt(int index)
   /** * Mandatory String ASSIC code (int) */
    public int codePointAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointAtImpl(value, index, value.length);
    }
Copy the code
  • codePointBefore(int index)
   /** * Returns the ASSIC code (int) of the previous element in the index position */
    public int codePointBefore(int index) {
        int i = index - 1;  // Get the index position of the element preceding index
        if ((i < 0) || (i >= value.length)) { // Index cannot equal 0 because I = 0 -1 = -1
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointBeforeImpl(value, index, 0);
    }
Copy the code
  • getChars(char dst[], int dstBegin)
   /** * copies the characters from dstBegin to DST */
    void getChars(char dst[], int dstBegin) {
        System.arraycopy(value, 0, dst, dstBegin, value.length);
    }
Copy the code


6, comparison,

  • equals(Object anObject)
   /** * The equals method of strings overrides the equals method of objects (case sensitive) * the equals method compares the values of two strings * The argument is an Object instead of a String. This is because we are overriding the equals method of Object, so Object */
    public boolean equals(Object anObject) {
        // If the address is equal, it is the same object
        if (this == anObject) {
            return true;
        }
        // Check whether anObject is a String
        if (anObject instanceof String) {
           // Convert anObject to String
            String anotherString = (String)anObject;
            int n = value.length;
            // Check whether anotherString is the same length as the value array of the current String
            if (n == anotherString.value.length) {
                 //v1 is the value of the current String, v2 is the value of the parameter object anotherString
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                // The length of each loop is -1 until the length is exhausted and the loop ends
                while(n-- ! =0) {
                   // Compare strings with the same subscript position, if there is a difference, return false
                    if(v1[i] ! = v2[i])return false;
                    i++;
                }
                // If there are no problems during the comparison, it is equal, and returns true
                return true; }}return false;
    }
Copy the code
  • equalsIgnoreCase(String anotherString)
   	/** * this is also a String's equals method, not the same as the previous one, which, as its name implies, is a complement to String's equals method. * this is a String Object, not an Object, because this is a method of String itself, not overriding someone's method */
    public boolean equalsIgnoreCase(String anotherString) {
       // Check whether it is the same object
        return (this == anotherString) ? true: (anotherString ! =null)
                // Check whether the length is the same
                && (anotherString.value.length == value.length)
                // Call the regionMatchs method again
                && regionMatches(true.0, anotherString, 0, value.length);
    }
Copy the code
  • compareTo(String anotherString)
	/** * This is a function that compares the size of characters in strings. Since String implements the Comparable
      
        interface, the compareTo method is overridden. If a class implements the Comparable interface, that means it supports sorting. * Lists or Arrays of objects of classes that implement the Comparable interface can be automatically sorted via collections.sort or arrays.sort. The * * argument is an int returned by another String that needs to be compared. Positive numbers are large and negative numbers are small, which is a classic code comparison based on characters
      
    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if(c1 ! = c2) {return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }
Copy the code

7, contains

  • startsWith(String prefix, int toffset)
   	/** * the string fragment in the toffset,toffset + prefix.value. Lenght interval is equal to prefix * Prefix is the prefix string to determine, and toffset is the starting position of the current object to determine */
    public boolean startsWith(String prefix, int toffset) {
      // Get the value of the current object
        char ta[] = value;
        // Get the starting position and offset to determine
        int to = toffset;
        // Get the value of the prefix string
        char pa[] = prefix.value;
        int po = 0;
        int pc = prefix.value.length;
        // Note: toffset might be near -1>>>1.
        if ((toffset < 0) || (toffset > value.length - pc)) {
            return false;
        }
         // Loop PC times, i.e. the length of prefix
        while (--pc >= 0) {
        // Each time the string of the current object is compared to the same character as prefix
            if(ta[to++] ! = pa[po++]) {// PC --,to++,po++, false if one is different
                return false; }}return true;
    }
Copy the code
  • startsWith(String prefix)
   // Determine that the string fragment of the current object [0, prefix.value. Lenght] is equal to prefix.
    public boolean startsWith(String prefix) {
        return startsWith(prefix, 0);
    }
Copy the code
  • endsWith(String suffix)
   // Determine whether the current string object ends with the string prefix
    public boolean endsWith(String suffix) {
        return startsWith(suffix, value.length - suffix.value.length);
    }
Copy the code
    / / if the child elements contain a CharSequence, usually used for StrngBuffer, StringBuilder
    public boolean contains(CharSequence s) {
        return indexOf(s.toString()) > -1;
    }
Copy the code


8 hashCode.

     /** * String overrides the hashCode method of Object. * Hash table to implement data structures, such as String objects to be put into a HashMap. * * /
    public int hashCode(a) {
       //hash is a member variable, so it defaults to 0
        int h = hash;
        //// If hash is 0 and string object length is greater than 0, not ""
        if (h == 0 && value.length > 0) {
            // Get the value of the current object
            char val[] = value;
            S [0]31^(n-1) + s[1]31^(n-2) + s[1]31^(n-2) +... + s[n-1] Computes the hash value
            for (int i = 0; i < value.length; i++) {
                // Each time is 31 * the h + i-th character ASSIC code obtained in each loop
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
Copy the code


9, query index

  • indexOf(int ch, int fromIndex)
	/** * index returns the first occurrence of the ch character in the string ** fromIndex, the first occurrence of the ch character in the string * -1 means that the string does not have the ch character. The integer represents the first occurrence of a character in a string at the position */
 public int indexOf(int ch, int fromIndex) {
        final int max = value.length;
        if (fromIndex < 0) {
            fromIndex = 0;
        } else if (fromIndex >= max) {
            // Note: fromIndex might be near -1>>>1.
            return -1;
        }
       //// a char takes up two bytes, and if ch is less than 2 to the power of 16 (65536), most characters are in that range
        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            // Iterate over the value array from fromIndex
            for (int i = fromIndex; i < max; i++) {
                // There are equal characters, return the index position of the first occurrence of the character, and terminate the loop
                if (value[i] == ch) {
                    returni; }}return -1;
        } else {
            returnindexOfSupplementary(ch, fromIndex); }}public int indexOf(int ch) {
        return indexOf(ch, 0);// Start the search from the first character
      }
Copy the code
  • indexOf(String str)

/** * this is a non-public static function ** source is the original string, sourceOffset is the original string offset, the starting position. * sourceCount is the length of the original string and target is the string to look for. * fromIndex is traversed from the first fromIndex of the original string
    static int indexOf(char[] source, int sourceOffset, int sourceCount,
            String target, int fromIndex) {
        return indexOf(source, sourceOffset, sourceCount,
                       target.value, 0, target.value.length,
                       fromIndex);
    }

/** ** iterates from fromIndex and returns the position where the STR string first appears ** /
    public int indexOf(String str, int fromIndex) {
        return indexOf(value, 0, value.length,
                str.value, 0, str.value.length, fromIndex);
    }

	/** * returns the position of the first occurrence of the string ** /
    public int indexOf(String str) {
        return indexOf(str, 0);
    }
Copy the code
  • lastIndexOf(int ch)
   /** * returns the first occurrence of cn from the end of the string. Value. Length - 1 is the starting point
    public int lastIndexOf(int ch) {
        return lastIndexOf(ch, value.length - 1);
    }
Copy the code
  • public int lastIndexOf(int ch, int fromIndex)
  /** * traverses from tail to head, starting from fromIndex, returns the first occurrence of ch * and the last occurrence of cn ** /
    public int lastIndexOf(int ch, int fromIndex) {
        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            int i = Math.min(fromIndex, value.length - 1);
            for (; i >= 0; i--) {
                if (value[i] == ch) {
                    returni; }}return -1;
        } else {
            returnlastIndexOfSupplementary(ch, fromIndex); }}Copy the code


Get the substring

  • substring(int beginIndex)
   /** * cut a fragment of the current string object to form a new string object * beginIndex is the initial position of the cut, the default position is len-1 */
    public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        // Use the constructor to generate a new String
        return (beginIndex == 0)?this : new String(value, beginIndex, subLen);
    }
Copy the code
  • substring(int beginIndex, int endIndex)
   	/** * truncates a range * [beginIndex,endIndex), excluding endIndex */
    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);
    }
Copy the code


11, joining together

  • concat(String str)
   /** * String concatenation function * for example :String STR = "ABC "; str.concat("def") output: "abcdef" * */
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        // Get the length of the current String
        int len = value.length;
        // Copy the value array to the buf array with len + str.lenght
        char buf[] = Arrays.copyOf(value, len + otherLen);
        // Then overwrite the STR string from len in the buF character array to get a complete buF character array
        str.getChars(buf, len);
        // Generate a new Strintg object
        return new String(buf, true);
    }
Copy the code
  • join(CharSequence delimiter, CharSequence… elements)
   /** * Splices CharSequence, including String, StringBuilder, StringBuffer */
    public static String join(CharSequence delimiter, CharSequence... elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // Number of elements not likely worth Arrays.stream overhead.
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }

Copy the code


12, replace,

  • replace(char oldChar, char newChar)
   // Replace all oldChar characters in the string with newChar
    public String replace(char oldChar, char newChar) {
        if(oldChar ! = newChar) {int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */
            // loop len times
            while (++i < len) {
                // Find the first old character, break the loop
                if (val[i] == oldChar) {
                    break; }}// If the position of the first old character is less than len
            if (i < len) {
               // New an array of characters, len length
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                   // Copy all the characters before the old character to the new character array
                    buf[j] = val[j];
                }
                 // start traversal at position I
                while (i < len) {
                    char c = val[i];
                    // Replace old characters and copy irrelevant ones
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                // Refactor a new String from the new character array buf
                return new String(buf, true); }}return this;
    }
Copy the code
  • replaceAll(String regex, String replacement)
   // When not a normal expression, the same effect as replace is all. If the regular expression is a string, the regular expression is replaced altogether
    public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    }
Copy the code


13, cutting

  • split(String regex, int limit)
   /** * Cuts the string */ according to the cut symbol
    public String[] split(String regex, int limit) {
      / * 1, a single character, and not ". $| () [{^? * + \ \ "two characters, one of the * 2, the first is the" \ ", the second case letters or Numbers * /
        char ch = 0;
        if (((regex.value.length == 1 &&
             ". $| () [{^? * + \ \".indexOf(ch = regex.charAt(0= = -))1) ||
             (regex.length() == 2 &&
              regex.charAt(0) = ='\ \' &&
              (((ch = regex.charAt(1)) -'0') | ('9'-ch)) < 0 &&
              ((ch-'a') | ('z'-ch)) < 0 &&
              ((ch-'A') | ('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))
        {
            int off = 0;
            int next = 0;
            > 0, limited==true, otherwise limited==false
            boolean limited = limit > 0;
            ArrayList<String> list = new ArrayList<>();
            while((next = indexOf(ch, off)) ! = -1) {
            // When limit<=0 or the length of list is less than limit-1
                if(! limited || list.size() < limit -1) {
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
                    List.size () == limit-1
                    list.add(substring(off, value.length));
                    off = value.length;
                    break; }}// If none matches, a new string is returned with the same content as the original
            if (off == 0)
                return new String[]{this};

           // When limit<=0, limited==false, or the set length is less than limit is, cut and add the rest of the string
            if(! limited || list.size() < limit) list.add(substring(off, value.length));// When limit == 0, if the element added at the end is empty (length 0), then the set length decreases by 1 until the end is not empty
            int resultSize = list.size();
            if (limit == 0) {
                while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }
Copy the code
  • split(String regex)
    public String[] split(String regex) {
        return split(regex, 0);
    }
Copy the code


14. Case conversion

  • toLowerCase(Locale locale)
  /** * converts uppercase characters to lowercase */
  public String toLowerCase(Locale locale) {
        if (locale == null) {
            throw new NullPointerException();
        }

        int firstUpper;
        final int len = value.length;

        /* Now check if there are any characters that need to be changed. */
        scan: {
            for (firstUpper = 0 ; firstUpper < len; ) {
                char c = value[firstUpper];
                // Determine whether the character is uppercase
                if ((c >= Character.MIN_HIGH_SURROGATE)
                        && (c <= Character.MAX_HIGH_SURROGATE)) {
                    int supplChar = codePointAt(firstUpper);
                    if(supplChar ! = Character.toLowerCase(supplChar)) {break scan;
                    }
                    firstUpper += Character.charCount(supplChar);
                } else {
                    if(c ! = Character.toLowerCase(c)) {breakscan; } firstUpper++; }}return this;
        }

        char[] result = new char[len];
        int resultOffset = 0;  /* result may grow, so i+resultOffset * is the write location in result */

        /* Just copy the first few lowerCase characters. */
        System.arraycopy(value, 0, result, 0, firstUpper);

        String lang = locale.getLanguage();
        boolean localeDependent =
                (lang == "tr" || lang == "az" || lang == "lt");
        char[] lowerCharArray;
        int lowerChar;
        int srcChar;
        int srcCount;
        for (int i = firstUpper; i < len; i += srcCount) {
            srcChar = (int)value[i];
            if ((char)srcChar >= Character.MIN_HIGH_SURROGATE
                    && (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
                srcChar = codePointAt(i);
                srcCount = Character.charCount(srcChar);
            } else {
                srcCount = 1;
            }
            if (localeDependent ||
                srcChar == '\u03A3' || // GREEK CAPITAL LETTER SIGMA
                srcChar == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE
                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
            } else {
                lowerChar = Character.toLowerCase(srcChar);
            }
            if ((lowerChar == Character.ERROR)
                    || (lowerChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
                if (lowerChar == Character.ERROR) {
                    lowerCharArray =
                            ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
                } else if (srcCount == 2) {
                    resultOffset += Character.toChars(lowerChar, result, i + resultOffset) - srcCount;
                    continue;
                } else {
                    lowerCharArray = Character.toChars(lowerChar);
                }

                /* Grow result if needed */
                int mapLen = lowerCharArray.length;
                if (mapLen > srcCount) {
                    char[] result2 = new char[result.length + mapLen - srcCount];
                    System.arraycopy(result, 0, result2, 0, i + resultOffset);
                    result = result2;
                }
                for (int x = 0; x < mapLen; ++x) {
                    result[i + resultOffset + x] = lowerCharArray[x];
                }
                resultOffset += (mapLen - srcCount);
            } else {
                result[i + resultOffset] = (char)lowerChar; }}return new String(result, 0, len + resultOffset);
    }
Copy the code


15. Go blank

  • trim()
   / * * * null to remove the string fore and aft part, such as, "or" ", the principle of "*" are implemented through the substring, fore and aft the head pointer to a pointer * found null value + +, the tail pointer found a null value is - * Int value is 32 ', actually is not only to the role of the empty, It should be an integer less than or equal to 32 to get rid of */
    public String trim(a) {
        // the size of the tail pointer is +1
        int len = value.length;
        // represents the header pointer
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */
        //st
        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        // Len - 1 is the true tail pointer. If the integer value of the tail element is <=32, then len-- is null
        while ((st < len) && (val[len - 1] < =' ')) {
            len--;
        }
        //// intercepts st to len (not including len position)
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }
Copy the code


16, character/string conversion

  • toString()
    // Return yourself
    public String toString(a) {
        return this;
    }
Copy the code
  • toCharArray()
   /** * returns a copy of the value array */
    public char[] toCharArray() {
        // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }
Copy the code
  • valueOf(Object obj)
   // Convert Object to a string
    public static String valueOf(Object obj) {
        return (obj == null)?"null" : obj.toString();
    }
Copy the code


17. Formatting

  • format(String format, Object… args)
   //JAVA string formatting
	// New string generates a formatted new string using the local locale, specifying the string format and parameters.
    public static String format(String format, Object... args) {
        return new Formatter().format(format, args).toString();
    }
Copy the code
  • format(Locale l, String format, Object… args)
   Generate a formatted string using the specified locale, specifying the string format and parameters.
    public static String format(Locale l, String format, Object... args) {
        return new Formatter(l).format(format, args).toString();
    }

Copy the code


18, intern ()

A local method.

When the intern method is called, if the pool already contains an Equals (Object) String that is the same as the String identified by the String, the equals(Object) String is returned. Otherwise, the String object is added to the pool and a reference to the object is returned.

  public native String intern(a);
Copy the code


Constant pool

When we look at constructors, we know that there are two most common ways to declare a string object:

(1) Direct assignment via “literal” form

String str = "hello";
Copy the code

Create object by calling the constructor with the new keyword

String str = new String("hello");
Copy the code

What’s the difference between these two statements? Let’s start with the memory distribution of JVMS prior to JDK1.7 (excluding 1.7) :

  • ①, program counter: Also known as PC register, save the currently executing programs that is the address of the instruction (or save the next instruction in the address of the storage unit), when the CPU needs to execute instructions, need from the program counter current needs to execute instructions where the address of the storage unit, then to get the address of the access to the command, after get the instruction, The program counter is automatically incremented by one or the address of the next instruction based on the transfer pointer, and so on until all instructions have been executed. Thread private.

  • (2) Virtual machine stack: Basic data types and object references are stored here. Thread private.

  • (3) Native Method stack: The virtual machine stack is used for executing Java methods, while the Native Method stack is used for executing Native methods. The JVM specification does not mandate the implementation of the local method stack or the data structure, and the virtual machine is free to implement it. The HotSopt virtual machine combines the local method stack with the virtual machine stack directly.

  • (4) Method area: stores the information of each class (including the name of the class, method information, field information), static variables, constants and the code compiled by the compiler. Note: In addition to the description of the Class’s fields, methods, interfaces, and so on, the Class file contains a constant pool for storing literal and symbolic references generated during compilation.
  • The heap is used to store the objects themselves and arrays (of course, array references are stored in the Java stack).

After JDK1.7, the constant pool of the method area was removed and placed on the heap as follows:

Constant Pool: The Java runtime maintains a String Pool, also known as a String buffer. The String pool is used to hold various strings generated at runtime, and the contents of the strings in the pool are not repeated.

  • If there is no equivalent object in the string pool, create the object in the string pool. If there is no equivalent object, create the object in the string pool. If so, use the reference directly from the pool to avoid creating objects repeatedly.
  • When the new keyword is created, a new object is created directly in the heap. All variables refer to the address of the new object, but if the string content created by the new keyword exists in the constant pool, the heap will refer to the corresponding character in the constant pool. On the other hand, string objects created by the new keyword are not maintained in the constant pool if they are not in the constant pool.
  • If you create a String object using an expression containing variables, you will not only check the maintenance of the String pool, but also create the object in the heap, and finally point to the heap memory.
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println(str1==str2);//true
System.out.println(str1==str3);//fasle
System.out.println(str2==str3);//fasle
System.out.println(str1.equals(str2));//true
System.out.println(str1.equals(str3));//true
System.out.println(str2.equals(str3));//true
Copy the code

String str1 = “hello”; String str1 = “hello”; String str1 = “hello”;

The second literal String str2 = “hello”, which is detected in the constant pool, assigns the reference directly to str2; The third one is an object created with the new keyword, it’s in the constant pool, it doesn’t have to be created in the constant pool, it’s created in the heap, it assigns a reference to the object in the heap to STR3, and it points to the constant pool. As shown below:

Note: Looking at the red arrow in the figure above, string objects created by the new keyword, if present in the constant pool, point to the object created in the heap as a reference to the constant pool.

Create an object using an expression containing variables:

String str1 = "hello";
String str2 = "helloworld";
String str3 = str1+"world";// The compiler cannot determine that it is a constant (it creates a String in the heap)
String str4 = "hello"+"world";// The compiler determines it to be a constant and references it directly to the constant pool

System.out.println(str2==str3);//fasle
System.out.println(str2==str4);//true
System.out.println(str3==str4);//fasle
Copy the code

Str3 contains str1, which the compiler cannot determine is a constant, and creates a String in the heap. Str4 adds two constants and refers directly to objects in the constant pool.


4. Other extensions

1. Is String really immutable?

The String class is decorated with the final keyword and cannot be inherited, that’s all.

As we know from reading the source code, strings are made up of characters in an array of values.

private final char value[];
Copy the code

When a value is final, the reference is not changed, but the array in the heap to which the value points is the real data. As long as the array in the heap can be manipulated, the data can still be changed. Value is a primitive type, so it must be mutable. Even if it is declared private, it can be changed by reflection.

So String immutability is just normal immutability, but by no means completely immutable.


2. Why is String designed to be immutable?

  • Easy to implement String pool
  • Multithreading safety
  • Avoid security issues
  • Speed up string processing


String overloading “+”

In the API documentation, you can find:

The Java language provides special support for string concatenation (“+”) and for converting other objects to strings. String concatenation is implemented through the StringBuilder (or StringBuffer) class and its Append method. String conversions are implemented through the toString method, which is defined by the Object class and can be inherited by all classes in Java. For more information on string concatenation and conversion, see The Java Language Specification by Gosling, Joy, and Steele.

This can be verified by decompiling the code:

public class StringDemo01 {

    public static void main(String[] args) {
        String a = "abc";
        String b = "def";
        System.out.println("abcdef"== a+b); }}Copy the code

Analysis of Java assembly instructions by Javap command shows that StringBuilder is used at the bottom


	
javap -v StringDemo.class
Copy the code
Classfile/home/qiao/desktop/course/spring_study/java_study/spring_cloud_demo java_demo/SRC/main/Java/string/StringDemo01 class Last modified2020-6-6; size 730 bytes
  MD5 checksum 8847314e26430be9703f9490a6d8ecf3
  Compiled from "StringDemo01.java"
public class string.StringDemo01
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC.ACC_SUPER
Constant pool# 1:= Methodref          #12.#25        // java/lang/Object."<init>":()V
   #2 = String             #26            // abc
   #3 = String             #27            // def
   #4 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
   #5 = String             #30            // abcdef
   #6 = Class              #31            // java/lang/StringBuilder
   #7 = Methodref          #6.#25         // java/lang/StringBuilder."<init>":()V
   #8 = Methodref          #6.#32         // java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
   #9 = Methodref          #6.#33         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Methodref          #34.#35        // java/io/PrintStream.println:(Z)V
  #11 = Class              #36            // string/StringDemo01
  #12 = Class              #37            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               StackMapTable
  #20 = Class              #38            // "[Ljava/lang/String;"
  #21 = Class              #39            // java/lang/String
  #22 = Class              #40            // java/io/PrintStream
  #23 = Utf8               SourceFile
  #24 = Utf8               StringDemo01.java
  #25 = NameAndType        #13: #14        // "<init>":()V
  #26 = Utf8               abc
  #27 = Utf8               def
  #28 = Class              #41            // java/lang/System
  #29 = NameAndType        #42: #43        // out:Ljava/io/PrintStream;
  #30 = Utf8               abcdef
  #31 = Utf8               java/lang/StringBuilder
  #32 = NameAndType        #44: #45        // append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
  #33 = NameAndType        #46: #47        // toString:()Ljava/lang/String;
  #34 = Class              #40            // java/io/PrintStream
  #35 = NameAndType        #48: #49        // println:(Z)V
  #36 = Utf8               string/StringDemo01
  #37 = Utf8               java/lang/Object
  #38 = Utf8               [Ljava/lang/String;
  #39 = Utf8               java/lang/String
  #40 = Utf8               java/io/PrintStream
  #41 = Utf8               java/lang/System
  #42 = Utf8               out
  #43 = Utf8               Ljava/io/PrintStream;
  #44 = Utf8               append
  #45= Utf8 (Ljava/lang/String;) Ljava/lang/StringBuilder; #46 = Utf8               toString
  #47 = Utf8               ()Ljava/lang/String;
  #48 = Utf8               println
  #49 = Utf8               (Z)V
{
  public string.StringDemo01();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 11: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=1
         0: ldc           #2                  // String abc
         2: astore_1
         3: ldc           #3                  // String def
         5: astore_2
         6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: ldc           #5                  // String abcdef
        11: new           #6                  // class java/lang/StringBuilder
        14: dup
        15: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        18: aload_1
        19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
        22: aload_2
        23: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
        26: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        29: if_acmpne     36
        32: iconst_1
        33: goto          37
        36: iconst_0
        37: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        40: return
      LineNumberTable:
        line 14: 0
        line 15: 3
        line 17: 6
        line 21: 40
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 36
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals  = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ] stack = [ class java/io/PrintStream, int ] } SourceFile: "StringDemo01.java"
Copy the code

String concatenation, which generates a StringBuilder object when a String variable is involved (StringBuffer before JDK1.5)








Reference:

[1] : String source code analysis [2] : Java source code analysis of Java8 String source code analysis [3] : Java String API [4] : Java source code learning series 1 — String [5] : Java.lang.String class [6] : Why is a String in Java designed to be immutable? [7] : String\ “+\” splicing underlying implementation principle