A String is an immutable object that represents a sequence of characters.
If you come from C or C++, you probably remember the difficulties of managing character arrays (char*), pointers, and the null terminator \0.
In Java, they decided that text was so important that it deserved VIP treatment. That’s why they created the java.lang.String class.
At first glance, it seems like just another primitive type. You use it with double quotes "Hello" and you forget about it. But under the hood, String is an Object with unique and fascinating behavior designed to optimize memory to the fullest.
Today, we’re going to look under the hood of text in Java and avoid the trap that all beginners fall into.
What is a String really?
In Java, a String is a sequence of Unicode characters. But technically, it is an Object.
However, Java gives it special treatment (syntactic sugar) to make it easy to use:
// "Literal" form (Recommended)
String greeting = "Hello World";
// "Object" form (Technically valid, but DON'T use it)
String uglyGreeting = new String("Hello World");
Why do two forms exist? To understand the difference, we first need to know the key property of strings.
The key property: immutability
String objects in Java are immutable.
This means that once a String is created in memory, it can never be modified. Not even a single letter can be changed.
“But Luis!,” you’ll say, “I spend all day modifying Strings!”
String text = "Hello";
text = text + " World"; // Didn't I just modify it?
No. What happened in memory is this:
Java creates the object "Hello".
Java creates the object " World".
Java creates a THIRD new object "Hello World".
Your variable text now points to the new object.
The old "Hello" becomes “orphaned” in memory until the Garbage Collector deletes it.
Performance Impact:
If you do this inside a loop of 1,000 iterations, you’ll be creating and discarding 1,000 intermediate objects. To modify text efficiently, we’ll use StringBuilder (we’ll see it in the next article).
Why make them immutable?
It seems inefficient, but it has enormous advantages:
- Security: Since they don’t change, they are safe for storing passwords, URLs, or using in multithreaded environments.
- HashCode Caching: Since it doesn’t change, its hash code is calculated once and stored. This makes
HashMaps with String keys extremely fast. - The String Pool: The big optimization.
The String Pool
Because Strings are immutable and used very frequently, Java has a special area in the Heap memory called the String Pool.
When you create a String using quotes String s = "Hello";:
- Java looks in the pool: “Does the text ‘Hello’ already exist?”
- If it exists: It creates nothing. It simply returns a reference to the object that already existed. It reuses the memory.
- If it doesn’t exist: It creates the object in the pool and gives you the reference.
Let’s see the impact of this:
String s1 = "Java";
String s2 = "Java"; // Java detects it's the same and REUSES the reference from s1.
// They are the SAME physical object in memory!
System.out.println(s1 == s2); // true
The exception: new String()
If you use the new keyword, you are forcing Java to create a new object in the normal Heap, bypassing the Pool check.
String s3 = new String("Java"); // Creates a new, duplicated, useless object.
System.out.println(s1 == s3); // false (They are different objects with the same text)
Advice: Never use new String("text") unless you have a very obscure and specific reason. Always use literals.
The deadly trap: == vs .equals()
This is where 99% of students fail their first exam.
==: Compares memory references (Are they the same object?)..equals(): Compares content (Do they have the same letters?).
Due to the String Pool, sometimes == returns true (as we saw with s1 and s2), which gives you a false sense of security. But in the real world, Strings come from user input, databases, or calculations, and there the Pool is not guaranteed.
String a = "Hello";
String b = "Hello";
String c = new Scanner(System.in).next(); // User types "Hello"
System.out.println(a == b); // true (Thanks to the Pool)
System.out.println(a == c); // FALSE (Danger!)
System.out.println(a.equals(c)); // true (compares content)
To compare the content of two strings, use .equals(). The == operator is still useful when you want to check if both variables hold the same reference.