preface

Recently, I spent two days to sort out the source code of String. This collation is not exhaustive but covers most of the methods in Spring source code. The remaining uncleaned methods will be updated to this article if time permits. Convenient later review and interview use. Please point out any problems in the article.

The paper

Strings are widely used in Java programming. In Java, strings are objects, and Java provides the String class to create and manipulate strings. String buffers support mutable strings. Because strings are immutable, they can be shared.

The String class represents strings, and all String literals such as “ABC” in Java programs are instance objects of this class. The String class is immutable, so once you create a String, its value cannot be changed. If you need to make a lot of changes to a string, you should use StringBuilder or StringBuffer.

The simplest way to create a String: String qc = “qiu chan” The compiler will use this value to create an object. We can also create strings using the keyword New. String constant pools are special. There are two main ways to use it: Strings declared directly in double quotes are stored directly in the constant pool. If a String object is not declared in double quotes, you can use the intern method provided by String. The intern method queries the string constant pool to see if the current string exists, and if it does not, puts the current string into the constant pool.

Inheritance/implementation relationships

Public final class String implements Java.io.Serializable, Comparable<String>, CharSequence {// omits}Copy the code

Strings are final and cannot be inherited or modified.

The source code

The bottom layer of String uses a char array for storage.

private final char value[];
Copy the code

The default hash code for cached strings is 0

private int hash;
Copy the code

No argument constructor

public String() {
		this.value = "".value;
}
Copy the code

Parses: Initializes a newly created String to represent a null character sequence. Note that since String is immutable, you do not need to use this constructor.

A constructor that takes a string

public String(String original) {
	this.value = original.value;
	this.hash = original.hash;
}
Copy the code

Parses: Initializes a newly created String to represent the same character sequence as the argument. In other words, the newly created string is a copy of the argument string. You don’t need to use this constructor unless you want an explicit copy of the argument String, because strings are immutable.

Constructor that takes a char array

public String(char value[]) {
		this.value = Arrays.copyOf(value, value.length);
}
Copy the code

Parse: Assigns a new String that represents the sequence of characters contained in the current character array parameter. The contents of character Arrays are copied using the array.copyof method. Subsequent changes to the character array do not affect the newly created string.

A constructor that takes a char array and takes an offset

// value[] : an array of character sources,offset: the offset, subscripts starting from 0 and including offset,count: the number of elements taken from the array. Public String (char value [], int offset, int count) {/ / if the offset is less than 0 thrown IndexOutOfBoundsException anomaliesif(offset < 0) { throw new StringIndexOutOfBoundsException(offset); } // Determine whether the number of elements to be taken is less than or equal to 0if(count < = 0) {/ / to take the number of the elements in the less than zero, throw IndexOutOfBoundsException anomaliesif(count < 0) { throw new StringIndexOutOfBoundsException(count); } // Determine whether the offset is less than or equal to the length of the array when the number of elements to be fetched is equal to 0if(offset <= value.length) {// The offset is less than or equal to the length of the array. Return an array of empty strings in the form this.value ="".value;
			return; }} / / if the offset value is greater than the length of the array minus the number of elements in throwing IndexOutOfBoundsException anomaliesif(offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); This. value = array. copyOfRange(value, offset, offset+count); }Copy the code

Allocate a new Sting from the characters in the given char array. The offset argument is the index of the first character in the subarray, and the count argument specifies the length of the subarray. After the subarray is copied, changes to the character array do not affect the newly created string.

Constructor of StringBuffer

Public String(StringBuffer buffer) {// Here StringBuffer is locked and then copied. It is locked here to ensure that only one thread can manipulate the StringBuffer object in a multithreaded environment. synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); }}Copy the code

Parse: Allocates a new string containing the sequence of characters contained in the current string buffer argument. The arrays. copyOf method copies the contents of the string buffer. Here the StringBuffer is locked and then copied. It is locked here to ensure that only one thread can manipulate the StringBuffer object in a multithreaded environment.

Constructor of StringBuilder

public String(StringBuilder builder) {
	this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
Copy the code

The StringBuilder argument is thread-unsafe, but has a significant performance improvement over StringBuffer. The source code notes that retrieving strings from the StringBuilder using the toString method may run faster, which is usually preferred.

Length approach

public boolean isEmpty() {// The length of the underlying char array is 0returnvalue.length == 0; } // Example @test public voidtest_string_isEmpty(){
	System.out.println("".isEmpty()); //false
  System.out.println("".isEmpty()); //true
}
Copy the code

Parse: Returns the length of this string. The value of this string is a char array. The value of this string is a char array.

IsEmpty method

public boolean isEmpty() {// The length of the underlying char array is 0returnvalue.length == 0; } // Example @test public voidtest_string_isEmpty(){
	System.out.println("".isEmpty()); //false
  System.out.println("".isEmpty()); //true
}
Copy the code

The underlying implementation determines whether the given string is empty based on whether the length of the char array is 0.

CharAt method

Public char charAt(int index) {// The given index is less than 0 or the given index is greater than the length of the char array corresponding to the stringif((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } // Get the current char character at the specified positionreturn value[index];
}
Copy the code

Parse: Gets the current char character at the specified position based on the given index. Raises an out-of-bounds exception if the given index is not less than 0, or if the given index is greater than the length of the char array corresponding to the string. Index starts at 0 and ends at Length-1. The first char value of the sequence is at index 0, the next value is at index 1, and so on, as with array indexes.

GetChars method

// srcBegin: index of the first character in the string to be copied. SrcEnd: Index of strings to be copied after the last character. DST [] : destination array. DstBegin: start offset in the target array. Public void getChars(int srcBegin, int srcEnd, char DST [], int dstBegin) {public void getChars(int srcBegin, int srcEnd, char DST [], int dstBegin)if(srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } // Checkend index greater than the length of the original string throws a corner out of bounds exceptionif(srcEnd > value.length) { throw new StringIndexOutOfBoundsException(srcEnd); } // Check that the end index is greater than the start indexif(srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); System. Arraycopy (Value, srcBegin, DST, dstBegin, srcEnd - srcBegin); } @test public voidtest_string_codePointAt(){// Original String h ="ahelloworld"; Char [] data = new char[4]; // copy h.gettars (2, 6, data, 0); System.out.println(data); }Copy the code

Parse: Copies characters from a string into the target character array. Index contains srcBegin, not srcEnd.

The equals method

// anObject: the object to be compared with this String. Public Boolean equals(Object anObject) {// Return the same reference directlytrue
	if (this == anObject) {
		return true; } // Determine if the given object is of type Stringif(anObject instanceof String) {// The given object is converted to a String of type String anotherString = (String)anObject; Int n = value.length; // Determine whether the length of the given string equals the length of the current stringif(n = = anotherString. Value. Length) {/ / v1 [] represent the current string corresponding char array char v1 [] = value; Char v2[] = anotherString.value; // v2[] = anotherString.value; // Iterate over the original char array and compare it to the array corresponding to the given string int I = 0;while(n-- ! = 0) {if(v1[i] ! = v2[I]) // The value is not equal at any of the positionsfalse
					return false; i++; } // all equal returnstrue
			return true; }} // Is not a String, or the length is inconsistentfalse
	return false;
}
Copy the code

This method overrides the equals method of Object. Method to compare this string to the specified object. This is followed by a hand-written String equals method.

Handwritten Equals method

Private Boolean mineEquals(String srcObject, Object anObject){// Compare references to be the sameif (srcObject == anObject){
		return true; } // reference different comparison contentif (anObject instanceof String){
		String ans = (String) anObject;
		char[] srcChar = srcObject.toCharArray();
		char[] anChar = ans.toCharArray();
		int n = srcChar.length;
		if (n == anChar.length){
			int i = 0;
			while(n-- ! = 0) {if(srcChar[i] ! = anChar[i])return false;
				i++;
			}
			return true; }}return false; } // Test the equals method @test public voidtest_string_mine(){
		String s = new String("aaa"); System.out.println(s.quals (s)); //trueboolean b = mineEquals(s, s); System.out.println(b); //true
	}
Copy the code

Method equalsIgnoreCase

Public Boolean equalsIgnoreCase(String anotherString) {// Return if the same reference is usedtrue. References vary in length, and whether the char is the same at each positionreturn (this == anotherString) ? true: (anotherString ! = null) && (anotherString.value.length == value.length) && regionMatches(true, 0, anotherString, 0, value.length);
}
Copy the code

Parsing: Compares this string to another string, ignoring case considerations. The source code for the regionMatches method is very interesting. There is a while loop which determines whether the size is not ignored and then whether the size is ignored. In this judgment, the first line is converted to uppercase for comparison, but it may fail. So after the uppercase conversion the comparison fails, a lowercase conversion comparison is performed.

Method startsWith

Public Boolean startsWith(String prefix) {// 0 indicates search from the startreturn startsWith(prefix, 0);
}
Copy the code

The endsWith method

Public Boolean endsWith(String suffix) {// Start with [value.length.value.length], This method calls the startsWith method againreturn startsWith(suffix, value.length - suffix.value.length);
}
Copy the code

The final implementation of startsWith and endsWith

// prefix: Tests whether the string starts with the specified prefix. Toffset: Where to start looking for this string. Public Boolean startsWith(String prefix, int toffSet) {char[] char ta[] = value; Int to = toffset; Char [] char pa[] = prefix. Value; int po = 0; Char [] int PC = prefix.value. Length; // The starting position is less than 0, or the starting position is greater than the length to be searchedfalse.if ((toffset < 0) || (toffset > value.length - pc)) {
		return false; } // Compare each element in the char[] of the given string to the same element in the char array of the original stringwhile (--pc >= 0) {
		if(ta[to++] ! = pa[po++]) {// there is a char not the same returnfalse
			return false; }} // Same returntrue
	return true;
}
Copy the code

The substring method

// Returns a string that is a substring of the string. BeginIndex Indicates the index to start intercepting. Public String subString (int beginIndex) {// Validates the specified indexif(beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } // Substring length int subLen = value.length - beginIndex; // The length of the substring is less than 0if(subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } // start position 0, return the current string, not 0, create a new substring object and returnreturn (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
Copy the code

Parsing: Returns a string that is a substring of the string. The substring begins with the character at the specified index and extends to the end of the string.

The substring method

// Returns a string that is a substring of the string. BeginIndex Indicates the index to start intercepting. Public String subString (int beginIndex) {// Validates the specified indexif(beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } // Substring length int subLen = value.length - beginIndex; // The length of the substring is less than 0if(subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } // start position 0, return the current string, not 0, create a new substring object and returnreturn (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
Copy the code

Parsing: Returns a string that is a substring of the string. The substring starts with the specified beginIndex [inclusive] and extends to the character [inclusive] at index endindex-1.

Concat method

Public String concat(String STR) {// Get the given String length int otherLen = str.length(); // A string of length 0 returns the current stringif (otherLen == 0) {
		returnthis; } // Get the current string length int len = value.length; Char buf[] = array.copyof (value, len + otherLen); char buf[] = Array.copyof (value, len + otherLen); // The underlying call is system. arrayCopy. The processing of this method is str.getchars (buf, len) written in C;return new String(buf, true);
}
Copy the code

Concatenates the specified string to the end of the string. String concatenation.

The format method

// Returns a formatted string with the specified format string and arguments. public static String format(String format, Object... args) {returnnew Formatter().format(format, args).toString(); } // this is the case where %s is used to replace the following as"-a-"
@Test
public void test_start(){
	System.out.println(String.format("ha %s hh %s a %s h"."-a-"."-b-"."-c-"));
}
Copy the code

The trim method

public String trim() {// Specify the length of the string int len = value.length; Int st = 0; Char [] val = value; char[] val = value; char[] val = value; // Remove whitespace from the beginning of the string and record the indexwhile ((st < len) && (val[st] <= ' ')) { st++; } // Remove whitespace from the end of the string and record the index. This index is the index after whitespace is removedwhile ((st < len) && (val[len - 1] <= ' ')) { len--; } // Determine whether to intercept a string based on the length of the previous recordreturn ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
Copy the code

Returns a string whose value is the string, with any leading and trailing whitespace removed.

The join method

// Returns a new String consisting of the given delimiter and the element to concatenate. Delimiter: Delimiter that separates each element. Elements: connected elements. public static String join(CharSequence delimiter, CharSequence... Elements) {// delimiter and elements are null throw null pointer exception,null will be intercepted,""Objects. RequireNonNull (Delimiter); Objects.requireNonNull(elements); // StringJoiner joiner = new StringJoiner(delimiter); // Iterate over the given element to be concatenated. Concatenated elements are allowed to be NULLfor (CharSequence cs: elements) {
    // 执行拼接方法
		joiner.add(cs);
	}
	returnjoiner.toString(); } Public StringJoiner add(CharSequence newElement) {prepareBuilder() creates a StringBuilder object when it is first called. Subsequent calls execute the prepareBuilder().append(newElement) separator;returnthis; } // Create StringBuilder object without concatenation, value! = NULL Perform a concatenation delimiter private StringBuilderprepareBuilder() {// Check whether the concatenated value is nullif(value ! = null) {// Not null execute the concatenation delimiter value.append(delimiter); }else{// Call this method to create an empty StringBuilder object with only one call value = new StringBuilder().append(prefix); }returnvalue; Override public StringBuilder appEnd (CharSequence s) {Override public StringBuilder appEnd (CharSequence s) {Override public StringBuilder appEnd (CharSequence s) { Design mode is builder mode super.append(s);returnthis; } // The prepareBuilder method above is a splitter separator, @override public AbstractStringBuilder appEnd (CharSequence s) {Override public AbstractStringBuilder append(CharSequence s)if (s == null)
		return appendNull();
	if (s instanceof String)
		return this.append((String)s);
	if (s instanceof AbstractStringBuilder)
		returnthis.append((AbstractStringBuilder)s); / / stitchingreturn this.append(s, 0, s.length());
}
Copy the code

Splits the given string with the given separator and returns the delimited string.

The replace method

// target: the target string to be replaced. Replacement: replacement public String replace(CharSequence target, CharSequence replacement) {return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
			this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
Copy the code

Parse: Replaces each matching substring in this string with the specified string. Substituting from the beginning to the end of the string, for example, replacing “aa “with “b” in the string “aaa “will result in “ba” instead of “ab”.

ReplaceAll method

// regex: This supports regular expressions and can also be a target string to be replaced. public String replaceAll(String regex, String replacement) {return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
Copy the code

Question: What is the difference between replace and replaceAll methods? ReplaceAll supports regular expressions.

Replace method for char

Public String replace(char oldChar, char newChar) {// oldChar is not the same as newCharif(oldChar ! = newChar) {// The current string length int len = value.length; // This is used for the followingwhileThe condition comparison in the loop, val[I] I is 0 int I = -1; Char [] val = value; char[] val = value; char[] val = value; /* Avoid getField opcode */ / This is used to record the value of the I and determine if there is any replacement. This loop is good for performancewhile(++ I < len) {// the I in val[I] starts at 0if(val[I] == oldChar) {// If there is something to replace, it jumps out of the loopbreak; }} // AbovewhileIf there's an I that's going to be replaced in the loop that's going to be less than len, it's not going to be executed without this judgmentifChar buf[] = new char[len]; char buf[] = new char[len]; // The I above is the index of the first replaceable char. The following loop fills the index before the I into the buf[] array that does not need to be replacedfor(int j = 0; j < i; J ++) {// Fill buf[] array buf[j] = val[j]; } // Fill the remaining characters into buf[] one by one, starting with the replaceable index Iwhile(I < len) {// Get the character to be replaced char c = val[I]; Buf [I] = (c == oldChar) buf[I] = (c == oldChar) newChar : c; i++; } // Return the replaced stringreturn new String(buf, true); } // oldChar equals newChar directly returns the current stringreturn this;
}
Copy the code

case

@Test
public void test_matches(){
	String a = "adddfdefe";
	System.out.println(a.replace('d'.'b')); // abbbfbefe} copies the replace method argument to charCopy the code

Copy write

// And the source code to the only difference is the parameter pass, the other are the same as the source code, their own writing can be a deep memory and reference programming thinkingsource, char oldChar, char newChar) {
  char[] value = source.toCharArray();
	if(oldChar ! = newChar) { int len = value.length; int i = -1; char[] val = value; /* avoid getfield opcode */while (++i < len) {
			if (val[i] == oldChar) {
				break; }}if (i < len) {
			char buf[] = new char[len];
			for (int j = 0; j < i; j++) {
				buf[j] = val[j];
			}
			while (i < len) {
				char c = val[i];
				buf[i] = (c == oldChar) ? newChar : c;
				i++;
			}
			returnnew String(buf); }}return new String(value);
}
Copy the code

Intern method

public native String intern();
Copy the code

This is a native method. When the String#intern method is called, the String from the pool is returned if the pool already contains a String equal to the String as determined by equals. Otherwise, add the String to the pool and return a reference to the String.