Java String Basics
Java String Basics
Every Java program deals with text — usernames, messages, product names, addresses, error messages. All of that text is handled using String. It is the most used class in Java, and also one of the most misunderstood.
A String in Java is a sequence of characters. What makes it different from a plain char[] is that String comes with built-in methods for searching, comparing, transforming, and combining text — and it has a special behaviour called immutability that every developer must understand to avoid bugs and write efficient code.
Creating Strings — Two Ways
There are two ways to create a String in Java, and they behave differently in memory.
// File: StringCreationDemo.java
public class StringCreationDemo {
public static void main(String[] args) {
// Way 1 — String literal (most common, recommended)
// Java stores this in the String Pool — a special memory area
String name1 = "Priya";
String name2 = "Priya"; // reuses the same object from the pool
// Way 2 — new keyword (creates a new object every time)
String name3 = new String("Priya");
String name4 = new String("Priya"); // another new object
// == compares references (memory addresses)
System.out.println("name1 == name2 : " + (name1 == name2)); // true — same pool object
System.out.println("name1 == name3 : " + (name1 == name3)); // false — different objects
System.out.println("name3 == name4 : " + (name3 == name4)); // false — both new objects
// .equals() compares content — always use this for comparison
System.out.println("name1.equals(name3) : " + name1.equals(name3)); // true — same content
System.out.println("name3.equals(name4) : " + name3.equals(name4)); // true — same content
}
}Output:
name1 == name2 : true
name1 == name3 : false
name3 == name4 : false
name1.equals(name3) : true
name3.equals(name4) : true
Memory picture:
String Pool (special area in heap):
┌──────────────┐
│ "Priya" │ ◄── name1 and name2 both point here
└──────────────┘
Heap (regular area):
┌──────────────┐ ┌──────────────┐
│ "Priya" │ │ "Priya" │
└──────────────┘ └──────────────┘
▲ ▲
name3 name4
Use String literals (String s = "hello") in almost all situations. They are more memory-efficient because Java reuses the same object for identical strings in the pool. Use new String(...) only in rare cases where you explicitly need a separate object.
String Immutability — The Most Important Concept
A String object cannot be changed after it is created. This is called immutability. When you call methods like toUpperCase(), replace(), or trim() on a String, they do not modify the original — they return a brand new String object with the result.
// File: ImmutabilityDemo.java
public class ImmutabilityDemo {
public static void main(String[] args) {
String city = "mumbai";
System.out.println("Original : " + city);
// toUpperCase() does NOT change 'city' — it returns a NEW String
String upperCity = city.toUpperCase();
System.out.println("city after toUpperCase() : " + city); // still "mumbai"
System.out.println("upperCity : " + upperCity); // "MUMBAI"
// Common beginner mistake — forgetting to capture the result
city.toUpperCase(); // result is thrown away — city is unchanged
System.out.println("city after ignored call : " + city); // still "mumbai"
// Correct — capture the returned string
city = city.toUpperCase(); // reassign to update the variable
System.out.println("city after reassignment : " + city); // "MUMBAI"
System.out.println();
// Chaining is safe because each step returns a new String
String messy = " Hello, World! ";
String cleaned = messy.trim().toLowerCase().replace(",", "");
System.out.println("messy : '" + messy + "'");
System.out.println("cleaned: '" + cleaned + "'");
// messy is completely unchanged
}
}Output:
Original : mumbai
city after toUpperCase() : mumbai
upperCity : MUMBAI
city after ignored call : mumbai
city after reassignment : MUMBAI
messy : ' Hello, World! '
cleaned: 'hello world!'
The most common beginner mistake is calling a String method and ignoring the return value — and then wondering why the String did not change. The rule is: always capture the result. String methods never modify the object they are called on.
String Length and Character Access
// File: StringLengthCharDemo.java
public class StringLengthCharDemo {
public static void main(String[] args) {
String message = "Hello, India!";
// length() — number of characters including spaces and punctuation
System.out.println("String : " + message);
System.out.println("Length : " + message.length()); // 13
// charAt(index) — character at a specific position (0-based index)
System.out.println("charAt(0) : " + message.charAt(0)); // H
System.out.println("charAt(7) : " + message.charAt(7)); // I
System.out.println("Last char : " + message.charAt(message.length() - 1)); // !
System.out.println();
// Loop through every character
System.out.println("Characters one by one:");
for (int i = 0; i < message.length(); i++) {
System.out.print(message.charAt(i) + " ");
}
System.out.println();
System.out.println();
// isEmpty() — true only if length is 0
String empty = "";
String spaces = " ";
String text = "hello";
System.out.println("empty.isEmpty() : " + empty.isEmpty()); // true
System.out.println("spaces.isEmpty() : " + spaces.isEmpty()); // false — has spaces
System.out.println("text.isEmpty() : " + text.isEmpty()); // false
// isBlank() — true if empty OR only whitespace (Java 11+)
System.out.println("empty.isBlank() : " + empty.isBlank()); // true
System.out.println("spaces.isBlank() : " + spaces.isBlank()); // true — only spaces
System.out.println("text.isBlank() : " + text.isBlank()); // false
}
}Output:
String : Hello, India!
Length : 13
charAt(0) : H
charAt(7) : I
Last char : !
Characters one by one:
H e l l o , I n d i a !
empty.isEmpty() : true
spaces.isEmpty() : false
text.isEmpty() : false
empty.isBlank() : true
spaces.isBlank() : true
text.isBlank() : false
isEmpty() only returns true for a string with zero characters. isBlank() (Java 11+) also returns true for strings that contain only spaces, tabs, or other whitespace — which is more useful when validating user input where someone might enter just spaces.
Common String Methods
These are the methods every Java developer uses daily.
// File: CommonStringMethods.java
public class CommonStringMethods {
public static void main(String[] args) {
String sentence = " Welcome to DevStackFlow ";
// trim() — removes leading and trailing whitespace
System.out.println("Original : '" + sentence + "'");
System.out.println("trimmed : '" + sentence.trim() + "'");
// strip() — same as trim() but handles Unicode whitespace (Java 11+)
System.out.println("stripped : '" + sentence.strip() + "'");
System.out.println();
String product = "Wireless Headphones - Premium Edition";
// toUpperCase() / toLowerCase()
System.out.println("upper: " + product.toUpperCase());
System.out.println("lower: " + product.toLowerCase());
System.out.println();
// contains() — checks if a substring exists anywhere
System.out.println("contains 'Headphones': " + product.contains("Headphones")); // true
System.out.println("contains 'laptop' : " + product.contains("laptop")); // false
// startsWith() / endsWith()
System.out.println("starts 'Wireless': " + product.startsWith("Wireless")); // true
System.out.println("ends 'Edition' : " + product.endsWith("Edition")); // true
System.out.println();
// replace() — replaces all occurrences
String original = "I love Java. Java is great. Java forever.";
String replaced = original.replace("Java", "Python");
System.out.println("original : " + original);
System.out.println("replaced : " + replaced);
System.out.println();
// substring() — extracts a part of the string
String fullName = "Priya Sharma";
System.out.println("Full name : " + fullName);
System.out.println("First name : " + fullName.substring(0, 5)); // "Priya"
System.out.println("Last name : " + fullName.substring(6)); // "Sharma"
System.out.println();
// indexOf() — position of first occurrence (-1 if not found)
String email = "priya@flipkart.com";
int atPos = email.indexOf('@');
System.out.println("Email : " + email);
System.out.println("@ at pos : " + atPos); // 5
System.out.println("Username : " + email.substring(0, atPos)); // priya
System.out.println("Domain : " + email.substring(atPos + 1)); // flipkart.com
}
}Output:
Original : ' Welcome to DevStackFlow '
trimmed : 'Welcome to DevStackFlow'
stripped : 'Welcome to DevStackFlow'
upper: WIRELESS HEADPHONES - PREMIUM EDITION
lower: wireless headphones - premium edition
contains 'Headphones': true
contains 'laptop' : false
starts 'Wireless': true
ends 'Edition' : true
original : I love Java. Java is great. Java forever.
replaced : I love Python. Python is great. Python forever.
Full name : Priya Sharma
First name : Priya
Last name : Sharma
@ at pos : 5
Username : priya
Domain : flipkart.com
String Comparison — equals() vs equalsIgnoreCase() vs ==
// File: StringComparisonDemo.java
public class StringComparisonDemo {
public static void main(String[] args) {
String input = "Admin";
String stored = "admin";
String ref = input;
// == compares references — almost always wrong for content comparison
System.out.println("== check:");
System.out.println(" input == stored : " + (input == stored)); // false
System.out.println(" input == ref : " + (input == ref)); // true (same object)
System.out.println();
// equals() — exact content match, case-sensitive
System.out.println("equals():");
System.out.println(" 'Admin'.equals('admin') : " + input.equals(stored)); // false
System.out.println(" 'Admin'.equals('Admin') : " + input.equals("Admin")); // true
// equalsIgnoreCase() — content match, case-insensitive
System.out.println();
System.out.println("equalsIgnoreCase():");
System.out.println(" 'Admin'.equalsIgnoreCase('admin') : "
+ input.equalsIgnoreCase(stored)); // true
// compareTo() — lexicographic comparison (like dictionary order)
// Returns 0 if equal, negative if first < second, positive if first > second
System.out.println();
System.out.println("compareTo():");
String a = "Apple";
String b = "Banana";
String c = "Apple";
System.out.println(" 'Apple' compareTo 'Banana' : " + a.compareTo(b)); // negative
System.out.println(" 'Apple' compareTo 'Apple' : " + a.compareTo(c)); // 0
System.out.println(" 'Banana' compareTo 'Apple' : " + b.compareTo(a)); // positive
System.out.println();
// Best practice — put the known string first to avoid NullPointerException
String userInput = null;
// Risky — throws NullPointerException if userInput is null
// if (userInput.equals("admin")) { ... }
// Safe — known non-null string calls equals
boolean isAdmin = "admin".equals(userInput);
System.out.println("Safe null check: " + isAdmin); // false — no NPE
}
}Output:
== check:
input == stored : false
input == ref : true
equals():
'Admin'.equals('admin') : false
'Admin'.equals('Admin') : true
equalsIgnoreCase():
'Admin'.equalsIgnoreCase('admin') : true
compareTo():
'Apple' compareTo 'Banana' : -1
'Apple' compareTo 'Apple' : 0
'Banana' compareTo 'Apple' : 1
Safe null check: false
Always use equals() or equalsIgnoreCase() for string content comparison — never ==. When comparing against a known string constant, put the constant on the left side: "admin".equals(userInput). This way, even if userInput is null, no NullPointerException is thrown.
String Concatenation
// File: ConcatenationDemo.java
public class ConcatenationDemo {
public static void main(String[] args) {
// + operator — simplest, works anywhere
String firstName = "Rohan";
String lastName = "Mehta";
String fullName = firstName + " " + lastName;
System.out.println("Full name : " + fullName);
// concat() method — same as +, but only works with Strings
String greeting = "Hello, ".concat(firstName).concat("!");
System.out.println("Greeting : " + greeting);
// String.format() — structured output with placeholders
String orderId = "ORD-501";
double amount = 1299.50;
String status = "PLACED";
String summary = String.format("Order %s | Rs.%.2f | %s", orderId, amount, status);
System.out.println("Summary : " + summary);
System.out.println();
// Concatenation with non-String types — Java calls toString() automatically
int age = 24;
double salary = 45000.0;
boolean active = true;
System.out.println("Age: " + age);
System.out.println("Salary: " + salary);
System.out.println("Active: " + active);
System.out.println();
// + with numbers — beware of operator precedence
int x = 10, y = 20;
System.out.println("Result: " + x + y); // "Result: 1020" — string concat
System.out.println("Result: " + (x + y)); // "Result: 30" — arithmetic first
System.out.println(x + y + " is the sum"); // "30 is the sum" — left to right
System.out.println();
// StringBuilder — efficient for building strings in loops
StringBuilder sb = new StringBuilder();
String[] items = {"Laptop", "Mouse", "Keyboard", "Monitor"};
for (int i = 0; i < items.length; i++) {
sb.append(items[i]);
if (i < items.length - 1) sb.append(", ");
}
System.out.println("Cart items : " + sb.toString());
}
}Output:
Full name : Rohan Mehta
Greeting : Hello, Rohan!
Summary : Order ORD-501 | Rs.1299.50 | PLACED
Age: 24
Salary: 45000.0
Active: true
Result: 1020
Result: 30
30 is the sum
Cart items : Laptop, Mouse, Keyboard, Monitor
The + operator creates a new String object every time. Inside a loop that concatenates many strings, this creates many temporary objects. StringBuilder avoids this — it accumulates all parts in a mutable buffer and converts to a String only once at the end with toString().
String Splitting and Joining
// File: SplitJoinDemo.java
import java.util.Arrays;
public class SplitJoinDemo {
public static void main(String[] args) {
// split() — breaks a string at a delimiter, returns String array
String csvRow = "Priya,28,Bengaluru,Engineering";
String[] parts = csvRow.split(",");
System.out.println("CSV row : " + csvRow);
System.out.println("Parts : " + Arrays.toString(parts));
System.out.println("Name : " + parts[0]);
System.out.println("Age : " + parts[1]);
System.out.println("City : " + parts[2]);
System.out.println("Dept : " + parts[3]);
System.out.println();
// split with limit — limits number of resulting parts
String sentence = "one:two:three:four:five";
String[] limited = sentence.split(":", 3); // max 3 parts
System.out.println("Limited split: " + Arrays.toString(limited));
System.out.println();
// String.join() — joins elements with a separator
String joined = String.join(", ", "Mumbai", "Delhi", "Bengaluru", "Pune");
System.out.println("Joined cities: " + joined);
// join from an array
String[] skills = {"Java", "SQL", "Spring Boot", "Git"};
String skillSet = String.join(" | ", skills);
System.out.println("Skills : " + skillSet);
System.out.println();
// Practical use — parse a full name
String fullName = "Ananya Krishnan Iyer";
String[] nameParts = fullName.split(" ");
System.out.println("First : " + nameParts[0]);
System.out.println("Middle: " + nameParts[1]);
System.out.println("Last : " + nameParts[2]);
}
}Output:
CSV row : Priya,28,Bengaluru,Engineering
Parts : [Priya, 28, Bengaluru, Engineering]
Name : Priya
Age : 28
City : Bengaluru
Dept : Engineering
Limited split: [one, two, three:four:five]
Joined cities: Mumbai, Delhi, Bengaluru, Pune
Skills : Java | SQL | Spring Boot | Git
First : Ananya
Middle: Krishnan
Last : Iyer
split() accepts a regular expression as the delimiter — not just a plain character. Splitting on . (dot) does not work as expected because . means "any character" in regex. To split on a literal dot, use split("\\.").
String vs StringBuilder vs StringBuffer — Comparison Table
| Aspect | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutability | Immutable — cannot change | Mutable — can modify | Mutable — can modify |
| Thread safety | Yes — immutable is always thread-safe | No — not thread-safe | Yes — all methods synchronized |
| Performance | Slow in loops — creates new object each time | Fast — modifies in place | Slower than StringBuilder — sync overhead |
| When to use | Single values, constants, comparisons | Building strings in loops or one thread | Building strings shared across multiple threads |
| Main methods | Hundreds of read methods | append(), insert(), delete(), reverse() | Same as StringBuilder |
Can use + | Yes | No — use append() | No — use append() |
| Storage | String Pool or Heap | Heap only | Heap only |
| Java version | Since Java 1.0 | Since Java 1.5 | Since Java 1.0 |
Basic Example
public class Main {
public static void main(String[] args) {
String firstName = "Priya";
String lastName = "Sharma";
// Concatenation
String fullName = firstName + " " + lastName;
System.out.println("Full name : " + fullName);
// Common methods
System.out.println("Length : " + fullName.length());
System.out.println("Uppercase : " + fullName.toUpperCase());
// Comparison
System.out.println("Is Priya? : " + firstName.equals("Priya"));
}
}Output
Full name : Priya Sharma Length : 12 Uppercase : PRIYA SHARMA Is Priya? : true
How it works:
- ›
firstName + " " + lastNamejoins three strings into one newString,fullName. - ›
length()counts every character infullName, including the space. - ›
toUpperCase()returns a newStringwith every letter capitalised —fullNameitself is unchanged. - ›
equals()compares the actual text content of two strings, returningtruebecausefirstNameholds exactly"Priya".
Best Practices
Always use .equals() to compare strings, never ==. The == operator checks whether two variables point to the same object in memory. Two strings with identical content stored as different objects will return false with ==. This is a bug that is easy to write and hard to spot. Use "constant".equals(variable) with the constant on the left to also avoid NullPointerException if the variable is null.
Always capture the result of a String method. String methods return new strings — they do not modify the original. name.trim() does nothing useful unless you write name = name.trim() or String trimmedName = name.trim(). Forgetting this is the single most common String mistake.
Use StringBuilder inside loops. Every + inside a loop creates a new String object. In a loop that runs 1,000 times, this creates 1,000 temporary String objects. StringBuilder.append() modifies the same buffer the entire time. For loops, StringBuilder is always the right choice.
Use String.format() for structured output. "Rs." + amount + " for " + qty + " items" becomes hard to read as the pieces multiply. String.format("Rs.%.2f for %d items", amount, qty) is readable, predictable, and formats numbers cleanly.
Common Mistakes
Mistake 1 — Comparing Strings With ==
String s1 = new String("hello");
String s2 = new String("hello");
if (s1 == s2) { // false — different objects
System.out.println("Equal"); // never prints
}
if (s1.equals(s2)) { // true — same content
System.out.println("Equal"); // prints
}Mistake 2 — Ignoring the Return Value of String Methods
String username = " rohan_mehta ";
username.trim(); // result thrown away — username unchanged
username.toUpperCase(); // same mistake
System.out.println(username); // still " rohan_mehta "
// Correct
username = username.trim().toUpperCase();
System.out.println(username); // "ROHAN_MEHTA"Mistake 3 — NullPointerException From Calling Methods on a Null String
String input = null;
System.out.println(input.length()); // NullPointerException
// Safe pattern — check null first
if (input != null && !input.isBlank()) {
System.out.println(input.length());
}
// Or — put the known value first in equals()
boolean isAdmin = "admin".equals(input); // false — no NPE even if input is nullMistake 4 — Building Strings With + Inside a Loop
// Inefficient — creates a new String object on every iteration
String result = "";
for (int i = 1; i <= 10000; i++) {
result = result + i + ","; // 10,000 temporary String objects created
}
// Efficient — one mutable buffer for the whole loop
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 10000; i++) {
sb.append(i).append(","); // modifies the same object every time
}
String result2 = sb.toString();Interview Questions
Q1. What is a String in Java and why is it immutable?
A String is a sequence of characters represented by the java.lang.String class. It is immutable — once a String object is created, its content cannot be changed. Immutability enables the String pool optimisation, where identical string literals can share the same object in memory without risk of one caller changing the content that another caller is reading. It also makes strings inherently thread-safe and safe to use as HashMap keys, since their hash code never changes.
Q2. What is the String pool in Java?
The String pool is a special region inside the heap where Java stores string literals. When you write String s = "hello", the JVM checks the pool first. If "hello" already exists there, it returns the existing reference — no new object is created. If not, it creates a new entry. This makes string literal reuse memory-efficient. Strings created with new String("hello") bypass the pool and always create a new object. You can add a new String to the pool manually using the intern() method.
Q3. What is the difference between equals() and == for Strings?
== compares references — it returns true only when both variables point to the exact same object in memory. equals() compares content — it returns true when both strings contain the same sequence of characters, regardless of whether they are the same object. Two strings created as literals with the same value may return true for == due to pool reuse, but two strings created with new String(...) containing the same value will return false for ==. Always use equals() for content comparison.
Q4. What is the difference between String, StringBuilder, and StringBuffer?
String is immutable — every operation produces a new object. StringBuilder is mutable and not thread-safe — use it for building strings in single-threaded code, especially inside loops. StringBuffer is mutable and thread-safe — all its methods are synchronised — use it when the same StringBuilder is accessed from multiple threads. For almost all common development scenarios, String for individual values and StringBuilder for building are the right choices. StringBuffer is rarely needed in modern Java where thread-local string building is preferred.
Q5. What does str.split(".") return?
An empty array — not what most beginners expect. split() takes a regular expression, and . in regex means "match any character". So "hello.world".split(".") matches every character and returns an empty array. To split on a literal dot, escape it: str.split("\\."). This is one of the most common beginner traps with split().
Q6. Why should you put the string constant on the left side of equals()?
"constant".equals(variable) is safe even when variable is null — calling .equals() on a non-null constant cannot throw NullPointerException. If you write variable.equals("constant") and variable is null, it throws NullPointerException. Since user input, database values, and API responses can all be null, putting the known non-null constant on the left is a defensive habit that prevents unnecessary null checks.
FAQs
Can you change a String after creating it in Java?
No. A String object's character content is fixed at creation. What you can do is reassign the variable to point to a new String — name = name.toUpperCase() — but the original "priya" object still exists in memory untouched until the garbage collector removes it. If you need to build or modify text frequently, use StringBuilder instead.
Is String a primitive type in Java?
No. String is a class — java.lang.String. It is a reference type, just like ArrayList or any custom class. However, Java gives it special syntax: you can create String literals with double quotes ("hello") without using the new keyword, and you can use + for concatenation. These conveniences make it feel primitive-like, but it is a full object with methods.
What is the difference between length for arrays and length() for Strings?
For arrays, length is a field — no parentheses: arr.length. For Strings, length() is a method — with parentheses: str.length(). Using str.length on a String causes a compile error, and arr.length() on an array also causes a compile error. The distinction exists because arrays are a special Java feature handled differently from objects, while String is a regular class.
What happens when you concatenate a String with null?
When null is concatenated with a String using +, Java converts null to the string "null" — no exception is thrown. So "Hello, " + null produces "Hello, null". However, calling .equals() or any method on a null reference throws NullPointerException.
What is the intern() method in Java?
intern() adds a String to the String pool (or returns the existing pool entry if one already exists with the same content). new String("hello").intern() returns the pooled reference for "hello". After calling intern(), you can compare with == reliably. In practice, intern() is rarely used in application code — it is more relevant in frameworks that process massive volumes of repeated strings.
Summary
String is Java's most used class, and its core behaviours are few and memorable. Strings are immutable — every method returns a new String, never modifying the original. String literals are pooled — identical literals share one object. Always compare with equals(), never ==. Always capture the return value of String methods.
The methods you will use most often are trim(), toLowerCase(), toUpperCase(), contains(), startsWith(), endsWith(), replace(), substring(), indexOf(), split(), equals(), equalsIgnoreCase(), and String.format(). These cover the vast majority of text processing tasks in real-world Java development.
For interviews, be ready to explain immutability and why it exists, describe the String pool, explain the difference between equals() and ==, and explain when to use StringBuilder over +. These appear in nearly every Java interview at every level.
What to Read Next
Learn the most useful built-in methods for working with text.
Practice Programs
Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.
Reverse a string using a loop.
Check whether a string reads the same forwards and backwards.
Counts how many vowels appear in a string.
Check whether two strings are anagrams of each other.
Counts how many words a sentence contains.
Counts how many times each character appears in a string.
Counts how many vowels and consonants a string contains, skipping spaces and punctuation.
Counts the total number of characters in a string without using length().
Counts how many consonants appear in a string.
Counts how many digit characters appear in a string.
Show all 15 practice programs ↓Show fewer ↑
Counts how many space characters appear in a string.
Counts how many special characters appear in a string.
Determines whether a string is made up entirely of letters.
Determines whether a string is made up entirely of digits.
Determines whether a sentence contains every letter of the alphabet.