What are Strings in Java?
Strings represent a sequence of characters. In Java, String is not a primitive data type like int or long, they are objects and the characters are stored internally as a char array.
The String class is used to represent strings and provides some methods that can be used to manipulate them and perform various operations.
How can you create Strings in Java?
The most common way to create a String is to use a literal like so:
String myString = "Hello World";
You will use this 99% of the time, but there can be cases when you would like to use one of the constructors of the String class (like when creating a String from a character array).
What is important to remember here is not to use this constructor:
new String("Hello World")
This will first create a new String from the literal that you specified (“Hello World”), it will then pass this to the constructor and that will create another String object with the same value. You have 2 objects instead of one, this is really inefficient.
How to check if two Strings are equal?
There are two ways to check for String equality.
The first way is to use the double equal sign (==). In most cases however this will not provide the expected result, because it will compare the references of the two Strings and those might not be the same. Two different String objects could exist with the same value, but their reference will not be the same.
The recommended way is to use the equals()
method. It will work as expected because it will only compare the value of the two String objects.
Why there are classes like StringBuilder or StringBuffer?
Performing a lot of operations (like concatenation) on a String until you reach it’s final form can result in creating a lot of String objects, because each of these operations have the chance of creating a new object because of String’s immutability.
StringBuilder
and StringBuffer
helps in this situation by keeping track of the String you are building as a character array. It will only produce a String object when you are done with the building of the String and ask StringBuilder
/StringBuffer
to return the result.
Let’s see an example. Without StringBuilder
:
String stringByConcatenation = "Hello"; stringByConcatenation += " "; stringByConcatenation += "World"; stringByConcatenation += "!";
After executing these lines we will have the following String objects:
- 4 String literals we see in the code.
- 3 intermediate results during the concatenation.
With StringBuilder
:
StringBuilder builder = new StringBuilder("Hello"); builder.append(" "); builder.append("World"); builder.append("!"); String stringFromBuilder = builder.toString();
After executing these lines we will have the following String objects:
- 4 String literals we see in the code.
- 1 extra String when we call
toString()
.
As you can see in this example we have 5 instead of 7 strings. In more complex examples, the win would be even higher. For 1000 strings, it would be 1999 (without StringBuilder
) vs 1001 (with StringBuilder
).
What is the difference between StringBuilder and StringBuffer?
StringBuffer
is synchronized, StringBuilder
is not.
What does it mean that a String is immutable?
It means that once a String object is created, it’s value cannot be changed. More on that here: What is the difference between final and immutable in Java?
Are Strings thread safe?
Strings are thread safe, because they are immutable so their state cannot change after they are created.
What is String interning?
See our separate article on this topic: What is String interning in Java?
Bonus: Why could String’s substring() method cause a memory leak in JDK6?
In JDK6 the String class contained three fields: value, offset, count.
- value – The characters of the String as a char array.
- offset – The first index of the array.
- count – The number of characters in the String.
When the substring()
method was called, it just created a new String object that contained the same value, but used a different offset and count. As a result the new object referenced the old object and made it impossible for it to be garbage collected. If you had a lot of huge Strings, this wasted memory could add up to large amounts.
Here is the String constructor that causes the mentioned problem in JDK6.
String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
In JDK7, this issue has been resolved. The String class no longer has the offset and count fields. When the substring()
method is used, a new String is created by copying the required characters to a new object.