String, StringBuilder, StringBuffer


String (Immutable, Thread Safe)

🔹 1. What is a String in Java?

  • In Java, String is a class (from java.lang) and not a primitive type.

  • Strings are immutable, meaning once created, the value cannot be changed.

  • Any operation like concatenation, substring, replace, etc. always creates a new String object.

String s1 = "Hello"; s1.concat(" World"); System.out.println(s1); // "Hello" (not "Hello World")

🔹 2. String Literals

  • A string literal is any sequence of characters written in double quotes " ".

String s1 = "Java";
  • When you write "Java", Java checks in the String Pool (inside the heap).

  • If "Java" already exists, the reference is reused.

  • If not, a new object is created in the pool.


🔹 3. String Pool (a.k.a. Intern Pool)

  • A special area in the heap memory where string literals are stored.

  • Purpose → save memory by avoiding multiple copies of the same string.

String s1 = "Java"; String s2 = "Java"; System.out.println(s1 == s2); // true (same reference in string pool) String s3 = new String("Java"); String s4 = new String("Java"); System.out.println(s3 == s4); // false (two different heap objects)

🔹 4. new String() vs String Literal

  • Literal (String s = "Hello";)

    • Stored in String Pool.

    • Reuses objects if the same literal already exists.

  • new keyword (String s = new String("Hello");)

    • Always creates a new object in heap (outside pool).

    • Even if "Hello" already exists in the pool.


🔹 5. intern() Method

  • Moves a string to the pool (or returns reference if already exists).

String s1 = new String("Java"); String s2 = s1.intern(); String s3 = "Java"; System.out.println(s1 == s2); // false System.out.println(s2 == s3); // true

🔹 6. Memory Layout of String

  • When you create a string, it can exist in two places:

    • Heap (non-pool area) → objects created with new.

    • Heap (String Pool) → only one instance of each literal.


🔹 7. Why Strings are Immutable?

  • Security – Strings are used in class loading, file paths, database connections. If mutable, hackers could change values.

  • Thread Safety – Multiple threads can share strings safely.

  • Caching & Hashing – String is heavily used in HashMap keys. Immutability guarantees consistent hashCode().


🔹 8. String Comparison

String a = "Java"; String b = "Java"; String c = new String("Java"); System.out.println(a == b); // true (same pool object) System.out.println(a == c); // false (different objects) System.out.println(a.equals(c)); // true (same content)
  • == → compares reference (memory location).

  • .equals() → compares content.


🔹 9. String Operations

  • Concatenation

String s = "Hello" + " World"; // compile-time optimized into "Hello World"
  • Methods: substring(), toUpperCase(), toLowerCase(), replace(), trim(), etc.

  • Interfaces: String implements Comparable and CharSequence.


🔹 10. Performance Considerations

  • Since String is immutable:

    • Repeated concatenation (+) creates multiple objects → costly.

  • Solution:

    • Use StringBuilder (faster, non-thread-safe).

    • Or StringBuffer (slower, thread-safe).

StringBuilder sb = new StringBuilder("Hello"); sb.append(" World"); System.out.println(sb.toString()); // "Hello World"

🔹 11. Key Points to Remember

  • Strings are immutable.

  • String literals go into the String Pool.

  • new String("abc") creates object in heap, not in pool.

  • Use intern() to move string to pool.

  • Use .equals() for content comparison.

  • Use StringBuilder/StringBuffer for heavy modifications.


🔹 12. When to Use (Use Cases)

  • When the value does not change often.

  • For constants, configuration values, keys in maps, messages, tokens, identifiers.

  • When security, caching, or immutability is important.

        Examples

  • Example 1: Database URL (Security)

    String dbUrl = "jdbc:mysql://localhost:3306/mydb";

    This should never change after creation → String is best.

  • Example 2: HashMap Keys (Immutability)

    Map<String, String> userRoles = new HashMap<>(); userRoles.put("admin", "Full Access"); userRoles.put("guest", "Read Only");

    If keys were mutable, changing them would break the hashcode contract.



StringBuilder (Mutable, Not Thread Safe)

🔹 1. What is StringBuilder?

  • StringBuilder is a mutable class introduced in Java 5.

  • Provides an efficient way to manipulate strings (append, insert, delete).

  • Not synchronized → faster, but not thread-safe.


🔹 2. Why StringBuilder?

  • Solves performance issue of immutable Strings.

  • Designed for single-threaded string modifications.


🔹 3. Creating StringBuilder

StringBuilder sb = new StringBuilder("Hello");

🔹 4. Mutability Example

StringBuilder sb = new StringBuilder("Hello"); sb.append(" World"); System.out.println(sb); // Hello World

🔹 5. Important Methods

  • append(String str) → add at end

  • insert(int index, String str) → insert at position

  • replace(int start, int end, String str) → replace substring

  • delete(int start, int end) → delete substring

  • reverse() → reverse characters

  • capacity() → total buffer capacity

  • length() → current number of characters


🔹 6. Capacity Management

  • Default capacity = 16.

  • Expansion formula: (oldCapacity * 2) + 2.


🔹 7. Performance Considerations

  • Faster than String and StringBuffer.

  • Ideal for loops and frequent string modifications.


🔹 8. Thread Safety

  • Not thread-safe.

  • If used across threads, must be externally synchronized.


🔹 9. Conversion

StringBuilder sb = new StringBuilder("Hello"); String str = sb.toString(); // Convert back to String

🔹 10. Key Points to Remember

  • StringBuilder is mutable.

  • Faster than StringBuffer.

  • Not safe in multithreaded environments.

  • Expands automatically when capacity is exceeded.


🔹 11. When to Use (Use Cases)

  • When working in a single-threaded environment.

  • For heavy concatenation/modification in loops.

  • When performance is critical and thread-safety is not required.

        Examples

  • Example 1: Building JSON Response

StringBuilder json = new StringBuilder(); json.append("{") .append("\"name\":\"Siraj\",") .append("\"role\":\"Developer\"") .append("}"); System.out.println(json.toString());

Efficient, since many modifications happen.

  • Example 2: Generating Report in Loop

StringBuilder report = new StringBuilder(); for (int i = 1; i <= 5; i++) { report.append("Employee ").append(i).append("\n"); } System.out.println(report);

Much faster than using String in loops.



StringBuffer (Mutable, Thread Safe)

🔹 1. What is StringBuffer?

  • StringBuffer is a mutable class introduced in Java 1.0.

  • Similar to StringBuilder but thread-safe (all methods synchronized).


🔹 2. Why StringBuffer?

  • Designed for multi-threaded string modifications.

  • Ensures thread safety with synchronization.


🔹 3. Creating StringBuffer

StringBuffer sbf = new StringBuffer("Hello");

🔹 4. Mutability Example

StringBuffer sbf = new StringBuffer("Hello"); sbf.append(" World"); System.out.println(sbf); // Hello World

🔹 5. Important Methods
(Same as StringBuilder) → append(), insert(), replace(), delete(), reverse(), capacity(), length().


🔹 6. Capacity Management

  • Default capacity = 16.

  • Expands as (oldCapacity * 2) + 2.


🔹 7. Thread Safety

  • All methods are synchronized.

  • Slower compared to StringBuilder due to synchronization overhead.


🔹 8. Performance Considerations

  • Slower than StringBuilder.

  • Used when multiple threads modify the same string.


🔹 9. Conversion

StringBuffer sbf = new StringBuffer("Hello"); String str = sbf.toString();

🔹 10. Key Points to Remember

  • StringBuffer is mutable.

  • Thread-safe due to synchronization.

  • Slower than StringBuilder.

  • Still relevant in legacy applications.


🔹 11. When to Use (Use Cases)

  • When multiple threads might modify the same string.

  • For legacy code (before Java 5).

  • When synchronization is explicitly needed.

    Examples

  • Example 1: Multi-threaded Logging System

class Logger { private static StringBuffer log = new StringBuffer(); public void addLog(String message) { log.append(Thread.currentThread().getName()) .append(": ") .append(message) .append("\n"); } public String getLogs() { return log.toString(); } }

Thread-safe logging without data corruption.

  • Example 2: Shared Counter (Unsafe with StringBuilder, Safe with StringBuffer)

StringBuffer counter = new StringBuffer("0"); Runnable task = () -> { for (int i = 0; i < 1000; i++) { counter.append("1"); // safe due to synchronization } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final length: " + counter.length());

Synchronization ensures no data inconsistency. 



Comparision

Feature

String

StringBuilder

StringBuffer

Mutability

Immutable

Mutable

Mutable

Thread Safety

Safe (immutable)

Not safe

Safe (synchronized)

Performance

Slowest

Fastest

Slower than Builder

Memory Use

String Pool + Heap

Heap buffer

Heap buffer

Use Case

Few modifications, constants

Many modifications, single-threaded

Many modifications, multi-threaded



Memory Diagrams
  • String

String s = "Hello"; s = s + " World";

Pool: "Hello", " World"
Heap: "Hello World" (new object).

  • StringBuilder

StringBuilder sb = new StringBuilder("Hello"); sb.append(" World");

Pool: "Hello", " World"
Heap: buffer → modified in-place → "Hello World".

  • StringBuffer
    Same as StringBuilder but synchronized operations.



Performance Benchmark Example
public class StringPerformanceTest { public static void main(String[] args) throws InterruptedException { int iterations = 100_000; // 1. String (Immutable) long start = System.currentTimeMillis(); String str = "Hello"; for (int i = 0; i < iterations; i++) { str += " World"; // creates new object every time } long end = System.currentTimeMillis(); System.out.println("Time taken by String: " + (end - start) + " ms"); // 2. StringBuilder (Mutable, Not Thread-Safe) start = System.currentTimeMillis(); StringBuilder sb = new StringBuilder("Hello"); for (int i = 0; i < iterations; i++) { sb.append(" World"); // modifies in-place } end = System.currentTimeMillis(); System.out.println("Time taken by StringBuilder: " + (end - start) + " ms"); // 3. StringBuffer (Mutable, Thread-Safe) start = System.currentTimeMillis(); StringBuffer sbf = new StringBuffer("Hello"); for (int i = 0; i < iterations; i++) { sbf.append(" World"); // modifies in-place with synchronization } end = System.currentTimeMillis(); System.out.println("Time taken by StringBuffer: " + (end - start) + " ms"); } }

Expected Output (approximate, will vary):

  • Time taken by String: 1200 ms

  • Time taken by StringBuilder: 5 ms

  • Time taken by StringBuffer: 10 ms

Explanation

  • String → very slow because each concatenation creates a new object in heap.

  • StringBuilder → fastest because it modifies in-place without synchronization.

  • StringBuffer → slightly slower than StringBuilder because methods are synchronized.

Tip:
“In my test, String concatenation was hundreds of times slower than StringBuilder. That’s why for loops or heavy modifications, we always prefer StringBuilder or StringBuffer.”




Q&A 


🔹 Java String Q&A

Q1. Why are Strings immutable in Java?
Answer:

  • Security → Strings store sensitive data (DB URLs, file paths, class names). If mutable, malicious code could modify them.

    String db = "jdbc:mysql://localhost:3306/db"; // If mutable → attacker could change "db" to point elsewhere
  • Caching → String Pool stores unique objects. If mutable, one change would affect all references.

  • Thread Safety → Immutable objects are inherently safe for multi-threading.

  • Hashcode Consistency → Strings are used as keys in HashMap. If mutable, hashCode() would change → breaking lookups.


Q2. Difference between == and .equals() for Strings?

String s1 = "Java"; String s2 = "Java"; String s3 = new String("Java"); System.out.println(s1 == s2); // true (same pool object) System.out.println(s1 == s3); // false (heap vs pool) System.out.println(s1.equals(s3)); // true (content check)
  • == → compares reference (memory address).

  • .equals() → compares content.


Q3. How does String Pool work?

  • Special memory area inside Heap.

  • Stores unique string literals.

  • Process:

    1. JVM checks pool.

    2. If literal exists → reuse reference.

    3. If not → create new object in pool.

String a = "Hello"; String b = "Hello"; System.out.println(a == b); // true

Q4. What does intern() do?

String s1 = new String("Java"); String s2 = s1.intern(); String s3 = "Java"; System.out.println(s1 == s2); // false System.out.println(s2 == s3); // true
  • Moves string into the pool (if not already).

  • Returns pooled reference.


Q5. Why is String final in Java?

  • Prevents subclassing → no overriding of equals(), hashCode().

  • Ensures immutability.

  • Provides security + performance consistency.


Q6. Difference: String vs StringBuilder vs StringBuffer

FeatureStringStringBuilderStringBuffer
MutabilityImmutableMutableMutable
Thread-SafeYesNoYes
PerformanceSlowestFastestSlower
Use CaseFew changesMany changes in single-threadMany changes in multi-thread

Q7. Why is String concatenation using + sometimes efficient?

  • Compiler optimization:

String s = "Hello" + "World"; // Compiled as → "HelloWorld"
  • But for runtime:

String s = a + b; // Compiled as → new StringBuilder().append(a).append(b).toString();

Q8. How many objects will this create?

String s = new String("Hello");
  • "Hello" → String Pool.

  • new String("Hello") → new Heap object.
    Total = 2 objects.


Q9. Can String be used as a key in HashMap? Why?
Yes 

  • Because it’s immutable.

  • Hashcode stays consistent → no lookup failures.


Q10. Substring memory leak (before Java 7)?

  • Java ≤ 6 → substring shared same char[] as original string.

  • Large string → kept in memory even if small substring needed.

  • Java 7+ → substring creates new char[] → no leak.


Q11. Difference: String.valueOf() vs toString()

Object o = null; System.out.println(String.valueOf(o)); // "null" System.out.println(o.toString()); // NPE
  • toString() → instance method, can throw NullPointerException.

  • String.valueOf() → static, returns "null" if object is null.


Q12. Is String thread-safe?
Yes, because it’s immutable. Multiple threads can safely share the same string.


Q13. Why prefer StringBuilder in loops?

String result = ""; for(int i = 0; i < 1000; i++) { result += i; // creates 1000 temporary strings } StringBuilder sb = new StringBuilder(); for(int i = 0; i < 1000; i++) { sb.append(i); // modifies in-place }
  • String → inefficient, creates multiple objects.

  • StringBuilder → efficient, modifies buffer in-place.


Q14. Difference: new String("abc") vs "abc"

  • "abc" → goes to String Pool.

  • new String("abc") → always creates new Heap object.


Q15. Can String be made mutable using Reflection?
Yes (hacky). Example → modify internal value[] via reflection.
But dangerous, breaks immutability guarantees → not recommended.


Q16. Difference: String.join() vs concat()

String s1 = "Hello".concat("World"); // "HelloWorld" String s2 = String.join("-", "Java", "Python", "C++"); // "Java-Python-C++"
  • concat() → joins two strings.

  • join() → joins multiple strings with a delimiter.


Q17. How does String.hashCode() work?

  • Formula:

    hash = 31 * hash + charAt(i);
  • Uses prime 31 for better distribution & fewer collisions.



🔹 Java StringBuilder Q&A

Q1. Why prefer StringBuilder over String in loops?

  • String → creates new object on every modification → memory heavy.

  • StringBuilder → modifies in-place → better performance.


Q2. Difference between capacity() and length()?

  • length() → number of characters stored.

  • capacity() → total buffer size before resizing.


Q3. Is StringBuilder thread-safe?
No 

  • If multiple threads access same instance → must add synchronization manually.


Q4. How does resizing of StringBuilder work?

  • New capacity = (oldCapacity * 2) + 2.


Q5. Can we convert StringBuilder to String?

StringBuilder sb = new StringBuilder("Hello"); String str = sb.toString();

Yes → by calling toString().



🔹 Java StringBuffer Q&A

Q1. Difference between String, StringBuilder, and StringBuffer?

  • String → Immutable.

  • StringBuilder → Mutable, fast, not thread-safe.

  • StringBuffer → Mutable, thread-safe (synchronized), slower.


Q2. When to use StringBuffer?

  • When multiple threads modify the same string.

StringBuffer sb = new StringBuffer("Log: "); sb.append(Thread.currentThread().getName());

Q3. Is StringBuffer still relevant today?

  • Rarely used in modern code.

  • Developers often prefer StringBuilder with explicit synchronization.

  • Still useful in legacy apps.