Java Notes
Complete guide to the String class in Java covering String Pool, immutability, creation methods, memory management, comparison techniques, and internal working with detailed examples.
What is the String Class?
The String class in Java represents an immutable sequence of characters. It is one of the most frequently used classes in Java programming. Every piece of text you work with — names, messages, file paths, URLs — is a String object.
Key Facts about String class:
- Belongs to
java.langpackage (automatically imported) - Declared as
public final class(cannot be subclassed) - Implements
Serializable,Comparable<String>, andCharSequenceinterfaces - Strings are immutable — once created, their content cannot be changed
- Stored in a special memory area called the String Pool (for literals)
// String class declaration in Java source code
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// Internal storage (Java 9+: byte array with encoding flag)
private final byte[] value;
// ...
}Creating Strings
There are two primary ways to create strings in Java, and understanding the difference is crucial.
Method 1: String Literal (Using String Pool)
String s1 = "Hello";
String s2 = "Hello";What happens in memory:
- JVM checks the String Pool (a special area in heap memory)
- If "Hello" already exists in the pool → reuse it (return same reference)
- If "Hello" doesn't exist → create it in the pool
String Pool (inside Heap)
+-------------------+
| "Hello" | ← Both s1 and s2 point here
+-------------------+
Stack
+------+
| s1 |───→ (same "Hello" in pool)
+------+
| s2 |───→ (same "Hello" in pool)
+------+
Method 2: Using new Keyword (Heap Object)
String s3 = new String("Hello");What happens in memory:
- A new String object is always created in heap (outside the pool)
- If "Hello" doesn't exist in the pool, it's also added there (as the literal)
s3points to the NEW heap object, NOT the pool object
| "Hello" | ← Created from the literal "Hello" |
|---|---|
| "Hello" | ← New object created by new keyword |
| +-----------+ | s3 points HERE |
Demonstration
public class StringCreation {
public static void main(String[] args) {
// Literal creation — uses String Pool
String s1 = "Java";
String s2 = "Java";
// new keyword — creates separate object in heap
String s3 = new String("Java");
String s4 = new String("Java");
// Reference comparison (==)
System.out.println("s1 == s2: " + (s1 == s2)); // Same pool object
System.out.println("s1 == s3: " + (s1 == s3)); // Pool vs Heap — different
System.out.println("s3 == s4: " + (s3 == s4)); // Two different heap objects
// Content comparison (.equals())
System.out.println("s1.equals(s3): " + s1.equals(s3)); // Same content
System.out.println("s3.equals(s4): " + s3.equals(s4)); // Same content
}
}s1 == s2: true s1 == s3: false s3 == s4: false s1.equals(s3): true s3.equals(s4): true
The String Pool (String Constant Pool)
The String Pool is a special memory region inside the heap where Java stores string literals. Its purpose is to save memory by sharing identical strings.
How the String Pool Works
public class StringPoolDemo {
public static void main(String[] args) {
String a = "Hello"; // Creates "Hello" in pool
String b = "Hello"; // Reuses existing "Hello" from pool
String c = "World"; // Creates "World" in pool
System.out.println("a == b: " + (a == b)); // true — same pool object
System.out.println("a == c: " + (a == c)); // false — different strings
// String concatenation of literals is optimized by compiler
String d = "Hello" + "World"; // Compiler sees: "HelloWorld"
String e = "HelloWorld";
System.out.println("d == e: " + (d == e)); // true — compiler optimization
// But concatenation with VARIABLES creates a new object
String f = a + c; // Cannot be optimized at compile time
System.out.println("f == \"HelloWorld\": " + (f == "HelloWorld")); // false!
System.out.println("f.equals(\"HelloWorld\"): " + f.equals("HelloWorld")); // true
}
}a == b: true
a == c: false
d == e: true
f == "HelloWorld": false
f.equals("HelloWorld"): trueThe intern() Method
The intern() method forces a String to use the pool version (or adds it to the pool if not present).
public class InternDemo {
public static void main(String[] args) {
String s1 = new String("Hello"); // Heap object
String s2 = "Hello"; // Pool object
System.out.println("Before intern:");
System.out.println("s1 == s2: " + (s1 == s2)); // false
String s3 = s1.intern(); // Returns pool reference
System.out.println("\nAfter intern:");
System.out.println("s3 == s2: " + (s3 == s2)); // true — same pool object
System.out.println("s1 == s3: " + (s1 == s3)); // false — s1 is still heap
}
}Before intern: s1 == s2: false After intern: s3 == s2: true s1 == s3: false
== vs .equals() for Strings
This is one of the most asked interview questions in Java.
| Operator/Method | What it compares | When true |
|---|---|---|
== | References (memory addresses) | Both variables point to same object |
.equals() | Content (character by character) | Both strings have same characters |
public class EqualsVsDoubleEquals {
public static void main(String[] args) {
String literal1 = "Hello";
String literal2 = "Hello";
String obj1 = new String("Hello");
String obj2 = new String("Hello");
System.out.println("=== Reference comparison (==) ===");
System.out.println("literal1 == literal2: " + (literal1 == literal2)); // true
System.out.println("literal1 == obj1: " + (literal1 == obj1)); // false
System.out.println("obj1 == obj2: " + (obj1 == obj2)); // false
System.out.println("\n=== Content comparison (.equals()) ===");
System.out.println("literal1.equals(literal2): " + literal1.equals(literal2)); // true
System.out.println("literal1.equals(obj1): " + literal1.equals(obj1)); // true
System.out.println("obj1.equals(obj2): " + obj1.equals(obj2)); // true
}
}=== Reference comparison (==) === literal1 == literal2: true literal1 == obj1: false obj1 == obj2: false === Content comparison (.equals()) === literal1.equals(literal2): true literal1.equals(obj1): true obj1.equals(obj2): true
Rule: ALWAYS use .equals() for comparing String content. Use == only when you intentionally want to check if two variables reference the exact same object.
String Constructors
public class StringConstructors {
public static void main(String[] args) {
// 1. From char array
char[] chars = {'J', 'a', 'v', 'a'};
String s1 = new String(chars);
System.out.println("From char[]: " + s1);
// 2. From char array (subset)
String s2 = new String(chars, 1, 2); // Start at index 1, length 2
System.out.println("From char[] subset: " + s2);
// 3. From byte array
byte[] bytes = {72, 101, 108, 108, 111}; // ASCII values
String s3 = new String(bytes);
System.out.println("From byte[]: " + s3);
// 4. From another String
String s4 = new String("Hello");
System.out.println("From String: " + s4);
// 5. From StringBuilder
StringBuilder sb = new StringBuilder("Dynamic");
String s5 = new String(sb);
System.out.println("From StringBuilder: " + s5);
// 6. From StringBuffer
StringBuffer sbuf = new StringBuffer("Thread-safe");
String s6 = new String(sbuf);
System.out.println("From StringBuffer: " + s6);
}
}From char[]: Java From char[] subset: av From byte[]: Hello From String: Hello From StringBuilder: Dynamic From StringBuffer: Thread-safe
String is Immutable — What Does That Mean?
Immutability means that once a String object is created, its internal character data cannot be modified. Any operation that appears to modify a String actually creates a new String object.
public class ImmutabilityDemo {
public static void main(String[] args) {
String original = "Hello";
System.out.println("Original: " + original);
System.out.println("HashCode: " + System.identityHashCode(original));
// This does NOT modify original — it creates a new String
String modified = original.concat(" World");
System.out.println("\nAfter concat:");
System.out.println("Original: " + original); // Unchanged!
System.out.println("Modified: " + modified);
System.out.println("Original HashCode: " + System.identityHashCode(original));
System.out.println("Modified HashCode: " + System.identityHashCode(modified));
// toUpperCase also creates a new String
String upper = original.toUpperCase();
System.out.println("\nOriginal still: " + original);
System.out.println("Upper: " + upper);
}
}Original: Hello HashCode: 1283928880 After concat: Original: Hello Modified: Hello World Original HashCode: 1283928880 Modified HashCode: 295530567 Modified HashCode: 295530567 Original still: Hello Upper: HELLO
Converting To/From Strings
public class StringConversion {
public static void main(String[] args) {
// Primitive to String
int num = 42;
String s1 = String.valueOf(num);
String s2 = Integer.toString(num);
String s3 = "" + num; // Concatenation trick
System.out.println("int to String: " + s1);
// String to Primitive
String numStr = "123";
int parsed = Integer.parseInt(numStr);
double parsedD = Double.parseDouble("3.14");
System.out.println("String to int: " + parsed);
System.out.println("String to double: " + parsedD);
// String to char array
String text = "Hello";
char[] charArr = text.toCharArray();
System.out.println("String to char[]: " + java.util.Arrays.toString(charArr));
// char array to String
String fromChars = new String(charArr);
System.out.println("char[] to String: " + fromChars);
}
}int to String: 42 String to int: 123 String to double: 3.14 String to char[]: [H, e, l, l, o] char[] to String: Hello
String Comparison Methods
public class StringComparison {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "hello";
String s3 = "Hello";
// equals — case sensitive
System.out.println("equals: " + s1.equals(s2)); // false
System.out.println("equals: " + s1.equals(s3)); // true
// equalsIgnoreCase — case insensitive
System.out.println("equalsIgnoreCase: " + s1.equalsIgnoreCase(s2)); // true
// compareTo — lexicographic comparison
System.out.println("compareTo: " + s1.compareTo(s2)); // negative (H < h)
System.out.println("compareTo: " + s1.compareTo(s3)); // 0 (equal)
// compareToIgnoreCase
System.out.println("compareToIgnoreCase: " + s1.compareToIgnoreCase(s2)); // 0
// startsWith and endsWith
String url = "https://www.java.com";
System.out.println("startsWith https: " + url.startsWith("https"));
System.out.println("endsWith .com: " + url.endsWith(".com"));
// contains
System.out.println("contains java: " + url.contains("java"));
}
}equals: false equals: true equalsIgnoreCase: true compareTo: -32 compareTo: 0 compareToIgnoreCase: 0 startsWith https: true endsWith .com: true contains java: true
How Many Objects Are Created?
This is a classic interview question. Let's analyze:
public class ObjectCount {
public static void main(String[] args) {
// Case 1: String literal
String s1 = "Hello";
// Objects created: 1 (in String Pool, if not already there)
// Case 2: new String with literal
String s2 = new String("World");
// Objects created: Up to 2
// 1. "World" in String Pool (if not there)
// 2. New String object in heap (always)
// Case 3: Concatenation of literals
String s3 = "Hello" + "World";
// Objects at runtime: 1 ("HelloWorld" in pool)
// Compiler optimizes literal concatenation at compile time
// Case 4: Concatenation with variable
String s4 = s1 + "World";
// Objects created: 1 new String in heap (result of concat)
// Uses StringBuilder internally: new StringBuilder(s1).append("World").toString()
System.out.println(s3);
System.out.println(s4);
System.out.println(s3 == s4); // false
System.out.println(s3.equals(s4)); // true
}
}HelloWorld HelloWorld false true
Common Mistakes
1. Using == Instead of .equals()
String a = new String("test");
String b = new String("test");
if (a == b) { } // WRONG — this is false!
if (a.equals(b)) { } // CORRECT — this is true2. NullPointerException with .equals()
String s = null;
// s.equals("hello"); // NullPointerException!
"hello".equals(s); // Safe — returns false
// Or: Objects.equals(s, "hello"); // Safe3. Thinking String Operations Modify the Original
String s = "hello";
s.toUpperCase(); // Returns new String, original unchanged!
// Must assign: s = s.toUpperCase();4. String Concatenation in Loops (Performance)
5. Comparing Strings from User Input with ==
Best Practices
- Always use .equals() for String comparison (never ==)
- Put literal on left in equals:
"expected".equals(variable)(null-safe) - Use StringBuilder for concatenation in loops
- Use String.format() or printf for complex formatting
- Prefer String literals over
new String()(saves memory, uses pool) - Use equalsIgnoreCase() when case doesn't matter
- Check for null before calling String methods
- Use isEmpty() or isBlank() instead of comparing with ""
- Immutability is thread-safe — Strings can be safely shared between threads
- Use intern() judiciously — only when you know strings will be compared with ==
Interview Questions
Q1: Why is String immutable in Java? Security (URL, file paths can't be changed), thread-safety (no synchronization needed), String Pool optimization (sharing requires immutability), and caching (hashcode is computed once).
Q2: Where is String stored in memory? String literals are stored in the String Pool (inside heap, since Java 7). Strings created with new are in regular heap memory.
Q3: What is String Pool? A special memory area in heap that stores unique string literals. When you create a literal, JVM checks if it exists in the pool before creating a new one.
Q4: How many objects does String s = new String("Hello") create? Up to 2: one in the String Pool (if "Hello" isn't there yet) and one in regular heap (from new).
Q5: What is the intern() method? It returns the canonical (pool) representation of the string. If the string isn't in the pool, it adds it. Allows == comparison of intern'd strings.
Q6: Why is String final? To prevent subclasses from overriding methods and breaking immutability guarantees.
Q7: String vs StringBuffer vs StringBuilder? String: immutable. StringBuffer: mutable, thread-safe (synchronized). StringBuilder: mutable, not thread-safe, faster.
Q8: Can we create our own immutable class? Yes — make class final, all fields private final, no setters, return deep copies of mutable fields.
Q9: What interfaces does String implement? Serializable, Comparable<String>, CharSequence.
Q10: Is String a primitive or reference type? Reference type (object), but Java provides special syntax support (literals, + operator).
Q11: What happens to String Pool in garbage collection? Since Java 7, the pool is in heap memory and eligible for GC if strings have no references. Before Java 7, it was in PermGen (not GC'd easily).
Q12: What is the difference between String.valueOf() and toString()? String.valueOf(null) returns the string "null". null.toString() throws NullPointerException.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for String Class in Java.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Java Master Course topic.
Search Terms
java-master-course, java master course, java, master, course, fundamentals, strings, string
Related Java Master Course Topics