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.
1// File: StringCreationDemo.java
2
3public class StringCreationDemo {
4
5 public static void main(String[] args) {
6
7 // Way 1 — String literal (most common, recommended)
8 // Java stores this in the String Pool — a special memory area
9 String name1 = "Priya";
10 String name2 = "Priya"; // reuses the same object from the pool
11
12 // Way 2 — new keyword (creates a new object every time)
13 String name3 = new String("Priya");
14 String name4 = new String("Priya"); // another new object
15
16 // == compares references (memory addresses)
17 System.out.println("name1 == name2 : " + (name1 == name2)); // true — same pool object
18 System.out.println("name1 == name3 : " + (name1 == name3)); // false — different objects
19 System.out.println("name3 == name4 : " + (name3 == name4)); // false — both new objects
20
21 // .equals() compares content — always use this for comparison
22 System.out.println("name1.equals(name3) : " + name1.equals(name3)); // true — same content
23 System.out.println("name3.equals(name4) : " + name3.equals(name4)); // true — same content
24 }
25}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.
1// File: ImmutabilityDemo.java
2
3public class ImmutabilityDemo {
4
5 public static void main(String[] args) {
6
7 String city = "mumbai";
8
9 System.out.println("Original : " + city);
10
11 // toUpperCase() does NOT change 'city' — it returns a NEW String
12 String upperCity = city.toUpperCase();
13
14 System.out.println("city after toUpperCase() : " + city); // still "mumbai"
15 System.out.println("upperCity : " + upperCity); // "MUMBAI"
16
17 // Common beginner mistake — forgetting to capture the result
18 city.toUpperCase(); // result is thrown away — city is unchanged
19 System.out.println("city after ignored call : " + city); // still "mumbai"
20
21 // Correct — capture the returned string
22 city = city.toUpperCase(); // reassign to update the variable
23 System.out.println("city after reassignment : " + city); // "MUMBAI"
24
25 System.out.println();
26
27 // Chaining is safe because each step returns a new String
28 String messy = " Hello, World! ";
29 String cleaned = messy.trim().toLowerCase().replace(",", "");
30 System.out.println("messy : '" + messy + "'");
31 System.out.println("cleaned: '" + cleaned + "'");
32 // messy is completely unchanged
33 }
34}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
1// File: StringLengthCharDemo.java
2
3public class StringLengthCharDemo {
4
5 public static void main(String[] args) {
6
7 String message = "Hello, India!";
8
9 // length() — number of characters including spaces and punctuation
10 System.out.println("String : " + message);
11 System.out.println("Length : " + message.length()); // 13
12
13 // charAt(index) — character at a specific position (0-based index)
14 System.out.println("charAt(0) : " + message.charAt(0)); // H
15 System.out.println("charAt(7) : " + message.charAt(7)); // I
16 System.out.println("Last char : " + message.charAt(message.length() - 1)); // !
17
18 System.out.println();
19
20 // Loop through every character
21 System.out.println("Characters one by one:");
22 for (int i = 0; i < message.length(); i++) {
23 System.out.print(message.charAt(i) + " ");
24 }
25 System.out.println();
26
27 System.out.println();
28
29 // isEmpty() — true only if length is 0
30 String empty = "";
31 String spaces = " ";
32 String text = "hello";
33
34 System.out.println("empty.isEmpty() : " + empty.isEmpty()); // true
35 System.out.println("spaces.isEmpty() : " + spaces.isEmpty()); // false — has spaces
36 System.out.println("text.isEmpty() : " + text.isEmpty()); // false
37
38 // isBlank() — true if empty OR only whitespace (Java 11+)
39 System.out.println("empty.isBlank() : " + empty.isBlank()); // true
40 System.out.println("spaces.isBlank() : " + spaces.isBlank()); // true — only spaces
41 System.out.println("text.isBlank() : " + text.isBlank()); // false
42 }
43}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.
1// File: CommonStringMethods.java
2
3public class CommonStringMethods {
4
5 public static void main(String[] args) {
6
7 String sentence = " Welcome to DevStackFlow ";
8
9 // trim() — removes leading and trailing whitespace
10 System.out.println("Original : '" + sentence + "'");
11 System.out.println("trimmed : '" + sentence.trim() + "'");
12
13 // strip() — same as trim() but handles Unicode whitespace (Java 11+)
14 System.out.println("stripped : '" + sentence.strip() + "'");
15
16 System.out.println();
17
18 String product = "Wireless Headphones - Premium Edition";
19
20 // toUpperCase() / toLowerCase()
21 System.out.println("upper: " + product.toUpperCase());
22 System.out.println("lower: " + product.toLowerCase());
23
24 System.out.println();
25
26 // contains() — checks if a substring exists anywhere
27 System.out.println("contains 'Headphones': " + product.contains("Headphones")); // true
28 System.out.println("contains 'laptop' : " + product.contains("laptop")); // false
29
30 // startsWith() / endsWith()
31 System.out.println("starts 'Wireless': " + product.startsWith("Wireless")); // true
32 System.out.println("ends 'Edition' : " + product.endsWith("Edition")); // true
33
34 System.out.println();
35
36 // replace() — replaces all occurrences
37 String original = "I love Java. Java is great. Java forever.";
38 String replaced = original.replace("Java", "Python");
39 System.out.println("original : " + original);
40 System.out.println("replaced : " + replaced);
41
42 System.out.println();
43
44 // substring() — extracts a part of the string
45 String fullName = "Priya Sharma";
46 System.out.println("Full name : " + fullName);
47 System.out.println("First name : " + fullName.substring(0, 5)); // "Priya"
48 System.out.println("Last name : " + fullName.substring(6)); // "Sharma"
49
50 System.out.println();
51
52 // indexOf() — position of first occurrence (-1 if not found)
53 String email = "priya@flipkart.com";
54 int atPos = email.indexOf('@');
55 System.out.println("Email : " + email);
56 System.out.println("@ at pos : " + atPos); // 5
57 System.out.println("Username : " + email.substring(0, atPos)); // priya
58 System.out.println("Domain : " + email.substring(atPos + 1)); // flipkart.com
59 }
60}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 ==
1// File: StringComparisonDemo.java
2
3public class StringComparisonDemo {
4
5 public static void main(String[] args) {
6
7 String input = "Admin";
8 String stored = "admin";
9 String ref = input;
10
11 // == compares references — almost always wrong for content comparison
12 System.out.println("== check:");
13 System.out.println(" input == stored : " + (input == stored)); // false
14 System.out.println(" input == ref : " + (input == ref)); // true (same object)
15
16 System.out.println();
17
18 // equals() — exact content match, case-sensitive
19 System.out.println("equals():");
20 System.out.println(" 'Admin'.equals('admin') : " + input.equals(stored)); // false
21 System.out.println(" 'Admin'.equals('Admin') : " + input.equals("Admin")); // true
22
23 // equalsIgnoreCase() — content match, case-insensitive
24 System.out.println();
25 System.out.println("equalsIgnoreCase():");
26 System.out.println(" 'Admin'.equalsIgnoreCase('admin') : "
27 + input.equalsIgnoreCase(stored)); // true
28
29 // compareTo() — lexicographic comparison (like dictionary order)
30 // Returns 0 if equal, negative if first < second, positive if first > second
31 System.out.println();
32 System.out.println("compareTo():");
33 String a = "Apple";
34 String b = "Banana";
35 String c = "Apple";
36 System.out.println(" 'Apple' compareTo 'Banana' : " + a.compareTo(b)); // negative
37 System.out.println(" 'Apple' compareTo 'Apple' : " + a.compareTo(c)); // 0
38 System.out.println(" 'Banana' compareTo 'Apple' : " + b.compareTo(a)); // positive
39
40 System.out.println();
41
42 // Best practice — put the known string first to avoid NullPointerException
43 String userInput = null;
44
45 // Risky — throws NullPointerException if userInput is null
46 // if (userInput.equals("admin")) { ... }
47
48 // Safe — known non-null string calls equals
49 boolean isAdmin = "admin".equals(userInput);
50 System.out.println("Safe null check: " + isAdmin); // false — no NPE
51 }
52}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
1// File: ConcatenationDemo.java
2
3public class ConcatenationDemo {
4
5 public static void main(String[] args) {
6
7 // + operator — simplest, works anywhere
8 String firstName = "Rohan";
9 String lastName = "Mehta";
10 String fullName = firstName + " " + lastName;
11 System.out.println("Full name : " + fullName);
12
13 // concat() method — same as +, but only works with Strings
14 String greeting = "Hello, ".concat(firstName).concat("!");
15 System.out.println("Greeting : " + greeting);
16
17 // String.format() — structured output with placeholders
18 String orderId = "ORD-501";
19 double amount = 1299.50;
20 String status = "PLACED";
21 String summary = String.format("Order %s | Rs.%.2f | %s", orderId, amount, status);
22 System.out.println("Summary : " + summary);
23
24 System.out.println();
25
26 // Concatenation with non-String types — Java calls toString() automatically
27 int age = 24;
28 double salary = 45000.0;
29 boolean active = true;
30
31 System.out.println("Age: " + age);
32 System.out.println("Salary: " + salary);
33 System.out.println("Active: " + active);
34
35 System.out.println();
36
37 // + with numbers — beware of operator precedence
38 int x = 10, y = 20;
39 System.out.println("Result: " + x + y); // "Result: 1020" — string concat
40 System.out.println("Result: " + (x + y)); // "Result: 30" — arithmetic first
41 System.out.println(x + y + " is the sum"); // "30 is the sum" — left to right
42
43 System.out.println();
44
45 // StringBuilder — efficient for building strings in loops
46 StringBuilder sb = new StringBuilder();
47 String[] items = {"Laptop", "Mouse", "Keyboard", "Monitor"};
48 for (int i = 0; i < items.length; i++) {
49 sb.append(items[i]);
50 if (i < items.length - 1) sb.append(", ");
51 }
52 System.out.println("Cart items : " + sb.toString());
53 }
54}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
1// File: SplitJoinDemo.java
2
3import java.util.Arrays;
4
5public class SplitJoinDemo {
6
7 public static void main(String[] args) {
8
9 // split() — breaks a string at a delimiter, returns String array
10 String csvRow = "Priya,28,Bengaluru,Engineering";
11 String[] parts = csvRow.split(",");
12
13 System.out.println("CSV row : " + csvRow);
14 System.out.println("Parts : " + Arrays.toString(parts));
15 System.out.println("Name : " + parts[0]);
16 System.out.println("Age : " + parts[1]);
17 System.out.println("City : " + parts[2]);
18 System.out.println("Dept : " + parts[3]);
19
20 System.out.println();
21
22 // split with limit — limits number of resulting parts
23 String sentence = "one:two:three:four:five";
24 String[] limited = sentence.split(":", 3); // max 3 parts
25 System.out.println("Limited split: " + Arrays.toString(limited));
26
27 System.out.println();
28
29 // String.join() — joins elements with a separator
30 String joined = String.join(", ", "Mumbai", "Delhi", "Bengaluru", "Pune");
31 System.out.println("Joined cities: " + joined);
32
33 // join from an array
34 String[] skills = {"Java", "SQL", "Spring Boot", "Git"};
35 String skillSet = String.join(" | ", skills);
36 System.out.println("Skills : " + skillSet);
37
38 System.out.println();
39
40 // Practical use — parse a full name
41 String fullName = "Ananya Krishnan Iyer";
42 String[] nameParts = fullName.split(" ");
43 System.out.println("First : " + nameParts[0]);
44 System.out.println("Middle: " + nameParts[1]);
45 System.out.println("Last : " + nameParts[2]);
46 }
47}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 |
Real-World Example 1 — User Registration Validator
The Business Problem
Every app has a registration form. Before saving a new user to the database, the server must validate that the name is not empty, the email is in the correct format, and the phone number is exactly 10 digits. String methods handle all of this cleanly.
1// File: RegistrationValidator.java
2
3public class RegistrationValidator {
4
5 // Validates a full name — not blank, at least two words
6 public static String validateName(String name) {
7 if (name == null || name.isBlank()) {
8 return "Name cannot be empty.";
9 }
10 String trimmed = name.trim();
11 if (!trimmed.contains(" ")) {
12 return "Please enter both first and last name.";
13 }
14 if (trimmed.length() < 4) {
15 return "Name is too short.";
16 }
17 return "OK";
18 }
19
20 // Validates email format — must contain @ and a domain with dot
21 public static String validateEmail(String email) {
22 if (email == null || email.isBlank()) {
23 return "Email cannot be empty.";
24 }
25 String trimmed = email.trim().toLowerCase();
26 int atIndex = trimmed.indexOf('@');
27
28 if (atIndex <= 0) {
29 return "Email must contain '@'.";
30 }
31 String domain = trimmed.substring(atIndex + 1);
32 if (!domain.contains(".") || domain.startsWith(".") || domain.endsWith(".")) {
33 return "Invalid email domain.";
34 }
35 return "OK";
36 }
37
38 // Validates phone — exactly 10 digits, no spaces or dashes
39 public static String validatePhone(String phone) {
40 if (phone == null || phone.isBlank()) {
41 return "Phone cannot be empty.";
42 }
43 String digits = phone.trim().replaceAll("[\\s\\-]", ""); // remove spaces/dashes
44 if (digits.length() != 10) {
45 return "Phone must be exactly 10 digits.";
46 }
47 for (char ch : digits.toCharArray()) {
48 if (!Character.isDigit(ch)) {
49 return "Phone must contain only digits.";
50 }
51 }
52 return "OK";
53 }
54
55 // Formats a display name from a raw full name input
56 public static String formatDisplayName(String rawName) {
57 if (rawName == null || rawName.isBlank()) return "";
58
59 String[] parts = rawName.trim().toLowerCase().split("\\s+");
60 StringBuilder formatted = new StringBuilder();
61
62 for (String part : parts) {
63 if (part.length() > 0) {
64 // Capitalise first letter of each word
65 formatted.append(Character.toUpperCase(part.charAt(0)));
66 formatted.append(part.substring(1));
67 formatted.append(" ");
68 }
69 }
70 return formatted.toString().trim();
71 }
72}1// File: RegistrationDemo.java
2
3public class RegistrationDemo {
4
5 public static void main(String[] args) {
6
7 System.out.println("=== Registration Validation ===\n");
8
9 // Test cases for name
10 String[] names = {null, "", " ", "Priya", " priya sharma ", "PriyaSharma"};
11 System.out.println("Name Validation:");
12 for (String name : names) {
13 System.out.printf(" %-25s → %s%n",
14 "'" + name + "'",
15 RegistrationValidator.validateName(name));
16 }
17
18 System.out.println();
19
20 // Test cases for email
21 String[] emails = {"", "priya", "priya@", "priya@gmail", "priya@gmail.com", "PRIYA@MEESHO.IN"};
22 System.out.println("Email Validation:");
23 for (String email : emails) {
24 System.out.printf(" %-25s → %s%n",
25 "'" + email + "'",
26 RegistrationValidator.validateEmail(email));
27 }
28
29 System.out.println();
30
31 // Test cases for phone
32 String[] phones = {"", "98765", "9876543210", "98765 43210", "abcdefghij"};
33 System.out.println("Phone Validation:");
34 for (String phone : phones) {
35 System.out.printf(" %-25s → %s%n",
36 "'" + phone + "'",
37 RegistrationValidator.validatePhone(phone));
38 }
39
40 System.out.println();
41
42 // Format display name
43 System.out.println("Display Name Formatting:");
44 String[] rawNames = {"priya sharma", "ROHAN MEHTA", " sneha rao ", "deepak"};
45 for (String raw : rawNames) {
46 System.out.printf(" %-25s → '%s'%n",
47 "'" + raw + "'",
48 RegistrationValidator.formatDisplayName(raw));
49 }
50 }
51}Output:
=== Registration Validation ===
Name Validation:
'null' → Name cannot be empty.
'' → Name cannot be empty.
' ' → Name cannot be empty.
'Priya' → Please enter both first and last name.
' priya sharma ' → OK
'PriyaSharma' → Please enter both first and last name.
Email Validation:
'' → Email cannot be empty.
'priya' → Email must contain '@'.
'priya@' → Invalid email domain.
'priya@gmail' → Invalid email domain.
'priya@gmail.com' → OK
'PRIYA@MEESHO.IN' → OK
Phone Validation:
'' → Phone cannot be empty.
'98765' → Phone must be exactly 10 digits.
'9876543210' → OK
'98765 43210' → OK
'abcdefghij' → Phone must contain only digits.
Display Name Formatting:
'priya sharma' → 'Priya Sharma'
'ROHAN MEHTA' → 'Rohan Mehta'
' sneha rao ' → 'Sneha Rao'
'deepak' → 'Deepak'
Real-World Example 2 — Order Confirmation Message Builder
The Business Problem
After a customer places an order on an app like Swiggy or Amazon, the system generates a personalised confirmation message to send via SMS or push notification. The message includes the customer name, order ID, total amount, and estimated delivery time. String formatting and building techniques power this.
1// File: OrderMessageBuilder.java
2
3public class OrderMessageBuilder {
4
5 // Builds an SMS confirmation message
6 public static String buildSmsMessage(String customerName,
7 String orderId,
8 double totalAmount,
9 String deliveryTime) {
10
11 // Use StringBuilder for multi-part message construction
12 StringBuilder msg = new StringBuilder();
13
14 // Extract first name only — friendly tone
15 String firstName = customerName.contains(" ")
16 ? customerName.substring(0, customerName.indexOf(' '))
17 : customerName;
18
19 msg.append("Hi ").append(firstName).append("! ");
20 msg.append("Your order ").append(orderId).append(" ");
21 msg.append("worth Rs.").append(String.format("%.2f", totalAmount)).append(" ");
22 msg.append("is confirmed. ");
23 msg.append("Estimated delivery: ").append(deliveryTime).append(". ");
24 msg.append("Track your order in the app.");
25
26 return msg.toString();
27 }
28
29 // Builds a detailed receipt header
30 public static String buildReceiptHeader(String storeName,
31 String orderId,
32 String customerName) {
33 int width = 44;
34 String line = "=".repeat(width);
35 String storeDisplay = storeName.toUpperCase();
36
37 // Centre the store name
38 int padding = (width - storeDisplay.length()) / 2;
39 String centred = " ".repeat(Math.max(0, padding)) + storeDisplay;
40
41 return String.format("%s%n%s%n%s%n%s: %s%n%s: %s%n%s",
42 line, centred, line,
43 "Order ID", orderId,
44 "Customer", customerName,
45 line);
46 }
47
48 // Masks sensitive part of an email for display
49 public static String maskEmail(String email) {
50 if (email == null || !email.contains("@")) return "***";
51 int atIdx = email.indexOf('@');
52 String user = email.substring(0, atIdx);
53 String domain = email.substring(atIdx);
54
55 // Show first 2 chars, mask the rest of username
56 if (user.length() <= 2) return user + "***" + domain;
57 return user.substring(0, 2) + "*".repeat(user.length() - 2) + domain;
58 }
59}1// File: OrderConfirmationDemo.java
2
3public class OrderConfirmationDemo {
4
5 public static void main(String[] args) {
6
7 // SMS confirmation for 3 different orders
8 System.out.println("=== SMS Confirmations ===\n");
9
10 String[][] orders = {
11 {"Priya Sharma", "ORD-9921", "1299.50", "30-35 mins"},
12 {"Rohan Mehta", "ORD-9922", "499.00", "2-3 days"},
13 {"Ananya Krishnan", "ORD-9923", "8499.00", "1-2 days"},
14 };
15
16 for (String[] order : orders) {
17 String sms = OrderMessageBuilder.buildSmsMessage(
18 order[0], order[1],
19 Double.parseDouble(order[2]),
20 order[3]);
21 System.out.println(sms);
22 System.out.println();
23 }
24
25 // Receipt header
26 System.out.println("=== Receipt Header ===\n");
27 System.out.println(OrderMessageBuilder.buildReceiptHeader(
28 "Swiggy Instamart", "ORD-9921", "Priya Sharma"));
29
30 System.out.println();
31
32 // Email masking
33 System.out.println("=== Email Masking ===\n");
34 String[] emails = {"priya@gmail.com", "ro@flipkart.in",
35 "ananyakrishnan@meesho.in"};
36 for (String email : emails) {
37 System.out.printf(" %-30s → %s%n", email,
38 OrderMessageBuilder.maskEmail(email));
39 }
40 }
41}Output:
=== SMS Confirmations ===
Hi Priya! Your order ORD-9921 worth Rs.1299.50 is confirmed. Estimated delivery: 30-35 mins. Track your order in the app.
Hi Rohan! Your order ORD-9922 worth Rs.499.00 is confirmed. Estimated delivery: 2-3 days. Track your order in the app.
Hi Ananya! Your order ORD-9923 worth Rs.8499.00 is confirmed. Estimated delivery: 1-2 days. Track your order in the app.
=== Receipt Header ===
============================================
SWIGGY INSTAMART
============================================
Order ID: ORD-9921
Customer: Priya Sharma
============================================
=== Email Masking ===
priya@gmail.com → pr***@gmail.com
ro@flipkart.in → ro***@flipkart.in
ananyakrishnan@meesho.in → an***************@meesho.in
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 ==
1String s1 = new String("hello");
2String s2 = new String("hello");
3
4if (s1 == s2) { // false — different objects
5 System.out.println("Equal"); // never prints
6}
7
8if (s1.equals(s2)) { // true — same content
9 System.out.println("Equal"); // prints
10}Mistake 2 — Ignoring the Return Value of String Methods
1String username = " rohan_mehta ";
2
3username.trim(); // result thrown away — username unchanged
4username.toUpperCase(); // same mistake
5
6System.out.println(username); // still " rohan_mehta "
7
8// Correct
9username = username.trim().toUpperCase();
10System.out.println(username); // "ROHAN_MEHTA"Mistake 3 — NullPointerException From Calling Methods on a Null String
1String input = null;
2System.out.println(input.length()); // NullPointerException
3
4// Safe pattern — check null first
5if (input != null && !input.isBlank()) {
6 System.out.println(input.length());
7}
8
9// Or — put the known value first in equals()
10boolean isAdmin = "admin".equals(input); // false — no NPE even if input is nullMistake 4 — Building Strings With + Inside a Loop
1// Inefficient — creates a new String object on every iteration
2String result = "";
3for (int i = 1; i <= 10000; i++) {
4 result = result + i + ","; // 10,000 temporary String objects created
5}
6
7// Efficient — one mutable buffer for the whole loop
8StringBuilder sb = new StringBuilder();
9for (int i = 1; i <= 10000; i++) {
10 sb.append(i).append(","); // modifies the same object every time
11}
12String 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
| Topic | Link |
|---|---|
| How StringBuilder and StringBuffer efficiently build mutable strings | Java StringBuilder and StringBuffer → |
| How String methods like format and regex work for advanced text processing | Java String Class → |
| How the String pool relates to JVM memory management | Java Heap and Stack Memory → |
| How immutable classes are designed and why String follows this pattern | Java Immutable Class → |
| How the String class fits into Java's Object hierarchy | Java Object Class → |