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.