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:
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) // +------------------+
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)
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)
// 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
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.
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.
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:
final.private final.
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.
Java designers made the String class
immutable for three important reasons:
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.
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
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();