Java Strings

1. What is a String?

A String in Java is a sequence of characters. It is an object of the java.lang.String class, which is automatically imported in every Java program. Strings are one of the most frequently used types in Java and have special language-level support.

Three key characteristics of Java Strings:

  • Immutable: Once a String object is created, its content cannot be changed. Any operation that seems to modify a String actually creates a new one.
  • String Pool: Java maintains a special area of heap memory called the String Constant Pool. When you create a string literal, Java checks the pool first; if the value already exists, it reuses that object instead of creating a new one.
  • Object vs Literal: Strings can be created as literals (stored in the pool) or using the new keyword (always creates a new heap object, bypassing the pool).
// String as an object in memory:
//
//  String Pool (inside heap):
//  +------------------+
//  |  "Hello"  <------+--- s1 (literal)
//  |                  |+-- s2 (literal — same object reused!)
//  +------------------+
//
//  Regular Heap:
//  +------------------+
//  |  "Hello"  <------+--- s3 (new keyword — different object)
//  +------------------+

2. Creating Strings

String Literal (Preferred)

String s1 = "Hello";
String s2 = "Hello";   // reuses the same pool object as s1

System.out.println(s1 == s2);       // true  (same reference in pool)
System.out.println(s1.equals(s2));  // true  (same content)
true
true

Using the new Keyword

String s3 = new String("Hello");
String s4 = new String("Hello");

System.out.println(s3 == s4);       // false (different heap objects)
System.out.println(s3.equals(s4));  // true  (same content)
false
true

Other Ways to Create Strings

// From a char array
char[] chars = {'J', 'a', 'v', 'a'};
String fromChars = new String(chars);
System.out.println(fromChars);   // Java

// From an integer / other type
String fromInt    = String.valueOf(42);
String fromDouble = String.valueOf(3.14);
System.out.println(fromInt + " " + fromDouble);  // 42 3.14
Java
42 3.14

Best Practice: Always use string literals instead of new String(...). Literals benefit from pool sharing, reducing memory usage. Reserve new String() for rare cases where you explicitly need a distinct heap object.

String Constant Pool (SCP)

The String Constant Pool (SCP) is a special area managed by the JVM for reusing string literals. A literal such as "Java" is checked in the pool before a new object is created. If the same value already exists, Java reuses the existing object.

String first = "Java";
String second = "Java";
String third = new String("Java");

first == second; // true: both references point to the SCP object
first == third;  // false: third points to a separate heap object

// Add a runtime string to the SCP when appropriate:
String pooled = new String("Spring").intern();
                JVM heap
  +-------------------------------------------+
  | String Constant Pool (SCP)                |
  |  +---------+                              |
  |  | "Java"  | <----- first, second          |
  |  +---------+                              |
  |                                           |
  | Ordinary heap                             |
  |  +---------+                              |
  |  | "Java"  | <----- third                 |
  |  +---------+                              |
  +-------------------------------------------+

Remember: the SCP is about sharing literal objects; it is not a separate physical memory area outside the heap. The exact internal layout is JVM-specific, but thinking of it as a shared pool is the easiest way to understand it.

How to Create an Immutable Class

An immutable object cannot change after construction. Every method that appears to update it must return a new object. Java's String class follows this design.

Follow these beginner-friendly rules:

  1. Declare the class final.
  2. Make all fields private final.
  3. Set fields only through the constructor.
  4. Do not provide setters or methods that mutate state.
  5. Make defensive copies of mutable input and output objects.
public final class UserProfile {
    private final String username;
    private final List<String> roles;

    public UserProfile(String username, List<String> roles) {
        this.username = username;
        this.roles = List.copyOf(roles); // defensive copy
    }

    public String getUsername() {
        return username;
    }

    public List<String> getRoles() {
        return roles; // List.copyOf made it unmodifiable
    }
}

UserProfile profile = new UserProfile("anand", List.of("ADMIN"));
// profile.username = "other"; // not allowed: no setter
// profile.getRoles().add("USER"); // UnsupportedOperationException

Why String is safer: a String has no setter and its internal character data is never exposed for modification. This makes it safe to share between methods, threads, caches, and security checks.

3. Why Strings are Immutable

Java designers made the String class immutable for three important reasons:

1. Memory Efficiency (String Pool)

Because strings are immutable, the JVM can safely share a single instance of a string value across many references. If strings were mutable, one reference changing the content would corrupt all other references pointing to the same pool entry.

String a = "Java";
String b = "Java";
// Both 'a' and 'b' safely point to the same pool object.
// No risk of corruption because neither can change it.

2. Thread Safety

Immutable objects are inherently thread-safe. Multiple threads can read the same String simultaneously without synchronization, because no thread can alter its state.

// Safe to share across threads — no synchronization needed
String config = "database=localhost;port=5432";
// Thread 1, Thread 2, Thread 3 all read 'config' safely

3. Security

Strings are used for sensitive data such as file paths, network URLs, database connection strings, and passwords. If strings were mutable, malicious code could alter the value after a security check but before the actual use.

// Hypothetical security issue if String were mutable:
String filename = "/secure/data.txt";
// ... security check passes on "/secure/data.txt" ...
// ... if mutable, attacker mutates to "/etc/passwd" ...
// ... file is opened with attacker's path — security breach!

// Because String is immutable, this attack is impossible.

Practical impact: The immutability of String means any operation like toUpperCase() or replace() returns a new String. The original is never altered. Always capture the result: s = s.toUpperCase();