🎓 Do your best and obey the destiny. The blogger is a postgraduate student in Southeast University. She loves fitness and basketball, and is preparing for the autumn recruitment in two years. She is willing to share what she sees and gains in technology, follow the public account @flying Veal, and get the update of the article as soon as possible

🎁 This article has been included in CS-Wiki (Gitee official recommended project, now 1.0K + STAR), is committed to creating a perfect back-end knowledge system, in the road of technology to avoid detour, welcome friends to come to exchange and study

String manipulation is unquestionably one of the most common behaviors in computer programming, especially in Web systems where Java has taken over.

The mind map of the whole text is as follows:

1. First of the three Musketeers: immutable String

An overview of the

Java does not have a built-in String type, but instead provides a predefined String class in the standard Java class library. Each String enclosed in double quotes is an instance of the String class:

String e = ""; / / an empty string
String str = "hello";
Copy the code

In Java 8, strings use char arrays to store data inside them.

public final class String
    implements java.io.Serializable.Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
}
Copy the code

As you can see, the String class is final, so the String class is not allowed to be inherited.

After Java 9, implementations of the String class changed to byte arrays to store strings, and used the coder to identify which encoding was used.

public final class String
    implements java.io.Serializable.Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final byte[] value;

    /** The identifier of the encoding used to encode the bytes in {@code value}. */
    private final byte coder;
}
Copy the code

However, in both Java 8 and Java 9, the char or byte array value used to store data is always declared final, which means that once the value array is initialized, it cannot reference any other array. And there is no way to change the value array inside a String, so we say that a String is immutable.

By immutable, just as the number 3 is always the number 3, the string “hello” always contains a sequence of code units for the characters H, E, 1, 1, and O, none of which can be modified. Of course, you can change the string variable STR to refer to another string, just as you can change a numeric variable that holds 3 to hold 4.

Let’s look at an example:

String str = "asdf";
String x = str.toUpperCase();
Copy the code

ToUpperCase is used to convert all strings toUpperCase characters. If we go into toUpperCase, we find that the method that appears to modify the String value actually ends up creating a new String, leaving the original String untouched.

An empty string with Null

An empty string “” is a string of length zero. You can call the following code to check if a string is empty:

if(str.length() == 0) {// todo
}
Copy the code

or

if(str.equals("")) {// todo
}
Copy the code

An empty string is a Java object with its own string length (0) and content (empty), that is, the value array is empty.

The String variable can also hold a special value called NULL, which means that no object is currently associated with the variable. To check whether a string is null, do the following:

if(str == null) {// todo
}
Copy the code

Sometimes you want to check that a string is neither null nor empty, in which case the following conditions are used:

if(str ! =null&& str.length() ! =0) {// todo
}
Copy the code

Some of you might think, well, what are you talking about? Yes, this is simple, but there is a catch: we must first check if STR is null, because the compiler will give an error if a method is called on a null value.

String splicing

The String is immutable. Why is the String a changed?

String a = "hello";
String b = "world";
a = a + b; // a = "helloworld"
Copy the code

In fact, when using + for string concatenation, the JVM initializes a StringBuilder to concatenate. Equivalent to compiled code as follows:

String a = "hello";
String b = "world";
StringBuilder builder = new StringBuilder();
builder.append(a);
builder.append(b);
a = builder.toString();
Copy the code

StringBuilder will be explained in more detail below, but the only thing you need to know now is that StringBuilder is a mutable string. ToString () = builder.toString()

Obviously, the toString method also generates a new String, rather than making changes to the contents of the old String, which is equivalent to referring to the new String. That’s why the string A changes.

In addition, we need to know that when concatenating a string with a non-string value, the latter is automatically converted to a string (any Java object can be converted to a string). Such as:

int age = 13;
String rating = "PG" + age; // rating = "PG13"
Copy the code

This feature is usually used in output statements. Such as:

int a = 12;
System.out.println("a = " + a);
Copy the code

Combining the above two features, let’s take a quick question: what is the result of null string and NULL concatenation?

String str = null;
str = str + "";
System.out.println(str);
Copy the code

The answer is null and you can guess that, but why null? As mentioned above, using + to concatenate is actually converted to StringBuilder using the append method. The compiled code looks like this:

String str = null;
str = str + "";
StringBuilder builder = new StringBuilder();
builder.append(str);
builder.append("");
str = builder.toString();
Copy the code

Take a look at the append source:

As you can see, when the string passed in is null, the appendNull method is called, which returns NULL.

Checks whether strings are equal

You can use the equals method to check whether two strings are equal. Such as:

String str = "hello";
System.out.println("hello".equals(str)); // true
Copy the code

Equals is actually a method of the Object class, from which all classes inherit. Before we get to equals, let’s review the use of the equals operator. It can be used in two ways:

  • For basic data types,= =The comparison is whether the values are the same;
  • For reference data types,= =The comparison is whether the memory address is the same.

Here’s an example:

String str1 = new String("hello"); 
String str2 = new String("hello");
System.out.println(str1 == str2); // false
Copy the code

For those still wondering about data storage areas in Java, go back to chapter 1, Everything Is An Object. For the above code, str1 and str2 create two different strings using the constructor new String(), starting with String str1 = new String(“hello”); For example, a new object is stored in heap memory, with a reference to str1 pointing to the address of the object, and the reference to str1 is stored in stack memory. Str1 and str2 are two different objects with different addresses, so the result of the == comparison is false.

In fact, the original equals method on the Object class still calls the operator == internally to determine whether two objects have the same reference (address), which has the same effect as == :

That is, if your new class doesn’t override equals, it compares the addresses of the objects. The String method overrides equals.

As you can see, String overrides equals to compare the contents of objects, not their addresses.

To summarize two uses of equals() :

  • Case 1: The class is not overriddenequals()Methods. byequals()Comparing two objects of this class is equivalent to passing= =Compare the two objects (addresses are compared).
  • Case 2: Class overridesequals()Methods. In general, we cover bothequals()Method to determine whether the contents of two objects are equal, for exampleStringClasses do just that. Of course, you don’t have to.

Here’s an example:

String a = new String("ab"); // a is a string reference
String b = new String("ab"); // b is another string reference. The contents of the two objects are the same

if (a.equals(b)) // true
    System.out.println("aEQb");
if (a == b) // false, not the same object, different address
    System.out.println("a==b");
Copy the code

String constant pool

As a class in Java, String, like other object allocation, requires high time and space costs. As the most basic and most commonly used data type, a large number of frequently created strings will greatly affect the performance of the program. To this end, the JVM implements some optimizations when instantiating string constants to improve performance and reduce memory overhead:

  • A String constant Pool, String Pool, is created for strings
  • When you create a string constant, you first check to see if the string exists in the string constant pool
  • If the string exists in the string constant pool, the reference instance is returned without re-instantiation. If not, the string is instantiated and pooled.

Here’s an example:

String str1 = "hello";
String str2 = "hello"; System. The out. Printl ("str1 == str2" : str1 == str2 ) //true 
Copy the code

For this code, String str1 = “hello”; The compiler first creates a reference to str1 on the stack, then looks for a reference to “hello” in the string constant pool. If no reference is found, it creates an address in the string constant pool for the string “hello”, and points the reference str1 to “hello”.

Note that the location of the string constant pool changed in JDK 1.7:

  • Prior to JDK 1.7, string Constant pools existed in Constant Storage
  • After JDK 1.7, string constant pools exist in Heap memory.

Alternatively, we can manually add strings to the String Pool at run time using the String intern() method. The specific process is as follows:

When a String calls intern(), a reference to the String in the String Pool is returned if there is already a String in the String Pool that is equal to the value of that String. Otherwise, a new String is added to the String Pool and a reference to the new String is returned.

Look at this example:

String str1 = new String("hello"); 
String str3 = str1.intern();
String str4 = str1.intern();
System.out.println(str3 == str4); // true
Copy the code

For str3, str1.intern() checks the String Pool to see if there is already a String equal to str1. If there is not, a new String equal to str1 is added to the String Pool. And returns a reference to the new string.

For STR4, str1.intern() finds a String in the String Pool that has the same value as STR1 and returns a reference to the String. Thus S3 and S4 refer to the same string, which means they have the same address, so str3 == str4 results in true.

🚩 summary:

  • String STR = “I”, the Java virtual machine automatically allocates it to the constant pool;

  • String STR = new String(” I “) is allocated to the heap. You can manually add the constant pool using the INTERN method

New String(“hello”) creates several String objects

How many string objects does this line of code create? Just create one in the heap?

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

Apparently not. For STR1, new String(“hello”) takes two steps:

  • First, “hello” is a String literal, so compile time looks for a reference to “hello” in the String Pool. If it doesn’t find one, it creates an address space in the String constant Pool and creates a String object that points to the “Hello” String literal.
  • Then, usenew Creates a string object in the heap.

Therefore, two String objects are created in this way (provided there is no “hello” String object in the String Pool).

2. Twins: Mutable StringBuffers and StringBuilders

String String concatenation problem

Sometimes, strings need to be constructed from shorter strings, such as keys or words from files. It is inefficient to use string concatenation to achieve this goal. Because the object contents of the String class are immutable, a new object is always created in memory whenever String concatenation is performed. It’s time-consuming and a waste of space. Such as:

String s = "Hello";
s += "World";
Copy the code

This simple code actually produces three strings, namely “Hello”, “World” and “HelloWorld”. Hello” and “World” are created as String constants in the String Pool, and concatenation + creates an object to hold “HelloWorld”.

Use the StringBuilder/ StringBuffer class to avoid this problem, since String + is the underlying implementation of StringBuilder. StringBuilder and StringBuffer have the same parent class:

However, StringBuilder is not thread-safe, and data inconsistencies can occur when used in a multithreaded environment, whereas StringBuffer is thread-safe. This is because common methods in the StringBuffer class use the synchronized keyword and are thread-safe. StringBuilder doesn’t. This is why StringBuilder is faster than StringBuffer. Therefore, StringBuilder is preferred if you are working with a single thread.

Initialization operation

The StringBuilder and StringBuffer classes have the same API, so we’ll use StringBuilder as an example to show how to initialize them.

StringBuiler/StringBuffer cannot like String with a String value directly, so also can’t do that initialization. It needs to be initialized via a constructor. First, build an empty string builder:

StringBuilder builder = new StringBuilder();
Copy the code

The append method is called each time a piece of content needs to be added:

char ch = 'a';
builder.append(ch);

String str = "ert"
builder.append(str);
Copy the code

When we call toString when we need to construct a String, we get a String object:

String mystr = builder.toString(); // aert
Copy the code

3. Compare String, StringBuffer, and StringBuilder

variability Thread safety
String immutable It is thread-safe because it is immutable
StringBuffer variable Thread-safe, because most of its internal methods are usedsynchronized Synchronize. It is less efficient
StringBuilder variable It is not thread-safe because it is not usedsynchronized Synchronizes, which is why it’s more efficient than StringBuffer. In single-thread mode, StringBuilder is preferred.

We’ll cover the issue of thread safety with synchronized in a future article.

📚 References

  • Java Core Technologies – Volume 1 Basics – 10th Edition
  • Thinking In Java – Edition 4

| flying veal 🎉 pay close attention to the public, get updates immediately

  • The blogger is a postgraduate student in Southeast University. In her spare time, she runs a public account “Flying Veal”, which was opened on December 29, 2020/29. Focus on sharing computer basics (data structure + algorithm + computer network + database + operating system + Linux), Java basics and interview guide related original technical good articles. The purpose of this public account is to let you can quickly grasp the key knowledge, targeted. I hope you can support me and grow with veal 😃
  • Cs-wiki (Gitee recommended project, now 1.0K + STAR) is committed to creating a perfect back-end knowledge system, less detours on the road of technology, welcome friends to come to exchange and learn ~ 😊