# Strings

Course: [Java](https://www.devtutorials.ai/learn/java) · Section: Reference Types
Source: https://www.devtutorials.ai/learn/java/reference-types/strings

> Work with Java text: immutability, the String Pool, literals and text blocks, formatting, common String methods, comparing strings, and building text with StringBuilder and StringBuffer.

Think about a printed name badge.

The name is printed on the card. You cannot rub out one letter and write a new one. If the name is wrong, you print a fresh badge and throw the old one away.

A Java `String` works the same way. Once a string is created, its text never changes. Every so-called change actually creates a new string.

In simple words: a `String` is a piece of text, and it cannot be changed after it is created.

## What You Will Learn

In this lesson, you will learn:

1. What a `String` is and why it is immutable.
2. How Java saves memory with the String Pool.
3. How to join text and format it neatly.
4. The most useful `String` methods.
5. How to compare strings correctly.
6. How `StringBuilder` and `StringBuffer` build text efficiently.
7. When to use `String` and when to use `StringBuilder`.

## Start With a Working Example

Press **Run**.

```java runnable title="A String answers questions about its own text"
public class Main {
    public static void main(String[] args) {
        String email = "  Asha.Kumar@Mail.com  ";

        String clean = email.trim().toLowerCase();

        System.out.println("Clean: " + clean);
        System.out.println("Length: " + clean.length());
        System.out.println("Gmail user? " + clean.endsWith("@gmail.com"));
        System.out.println("Name part: " + clean.substring(0, clean.indexOf("@")));
    }
}
```

Expected output:

```text
Clean: asha.kumar@mail.com
Length: 19
Gmail user? false
Name part: asha.kumar
```

`trim()` removes spaces at both ends. `toLowerCase()` makes every letter small. The other methods measure, check, and cut the text.

### Try This

Change the email to `"ravi@gmail.com"`. Predict all four lines, then press Run.

## Introduction to Strings

A **string** is a sequence of characters. It can hold a word, a sentence, a number written as text, or even nothing at all.

```text
String name = "Meena";
String sentence = "Java is fun.";
String empty = "";
```

Strings are everywhere in real programs: names, passwords, messages, file names, and web addresses.

Each character in a string has a position called an **index**. Like arrays, the index starts at `0`.

```text
Index:   0   1   2   3   4
         M   e   e   n   a
```

## String Immutability

**Immutable** means "cannot be changed".

Once a `String` is created, its characters can never change. Methods like `toUpperCase()` do not change the original. They return a **new** string.

```java runnable title="toUpperCase returns a new string and leaves the original alone"
public class Main {
    public static void main(String[] args) {
        String city = "pune";

        city.toUpperCase();
        System.out.println("After calling toUpperCase: " + city);

        city = city.toUpperCase();
        System.out.println("After storing the result: " + city);
    }
}
```

Expected output:

```text
After calling toUpperCase: pune
After storing the result: PUNE
```

The first call made a new string, `"PUNE"`, but nobody stored it. So it was thrown away, and `city` still pointed to `"pune"`.

The second time, the new string was stored back in `city`. The variable now points to the new string. The old `"pune"` string itself never changed.

### Why Strings Are Immutable

- **Safety.** A string passed to a method cannot be changed behind your back.
- **Sharing.** Many variables can safely share one string. This makes the String Pool possible.
- **Security.** File paths and passwords cannot be changed after they are checked.

## String Pool

The **String Pool** is a special area of memory where Java keeps string literals.

When you write a literal like `"Java"`, Java first looks in the pool. If `"Java"` is already there, it reuses that same string. It does not make a second copy.

Think of a shared library book. If ten students want the same book, the library lends the same copy. It does not print ten copies.

This only works because strings are immutable. If one variable could change the shared string, every other variable would change too.

`new String("Java")` skips the pool. It always makes a brand-new object.

```java runnable title="Literals share one pooled string, new does not"
public class Main {
    public static void main(String[] args) {
        String a = "Java";
        String b = "Java";
        String c = new String("Java");

        System.out.println("a == b: " + (a == b));
        System.out.println("a == c: " + (a == c));
        System.out.println("a.equals(c): " + a.equals(c));
    }
}
```

Expected output:

```text
a == b: true
a == c: false
a.equals(c): true
```

`a` and `b` point to the same pooled string, so `==` is true.

`c` is a separate object made with `new`, so `a == c` is false, even though the text is the same.

`equals()` compares the text itself, so it is true.

```text
 String Pool                Rest of memory
┌──────────┐               ┌──────────┐
│  "Java"  │ ◄── a, b      │  "Java"  │ ◄── c
└──────────┘               └──────────┘
```

There is rarely a good reason to write `new String("...")`. Use a literal.

## String Literals

A **string literal** is text written directly in the code, inside double quotes.

Some characters need an **escape sequence**, which is a backslash followed by a letter or symbol:

| Escape | Meaning |
|---|---|
| `\n` | New line |
| `\t` | Tab |
| `\"` | Double quote |
| `\\` | Backslash |

### Text Blocks

For text that spans many lines, Java has **text blocks**. A text block starts and ends with three double quotes, `"""`.

```java runnable title="Escapes and a text block"
public class Main {
    public static void main(String[] args) {
        System.out.println("Name:\tAsha");
        System.out.println("She said \"Hello\"");
        System.out.println("Path: C:\\Users\\Asha");

        String address = """
            12 Lake Road
            Chennai
            600001""";
        System.out.println(address);
    }
}
```

Expected output:

```text
Name:	Asha
She said "Hello"
Path: C:\Users\Asha
12 Lake Road
Chennai
600001
```

The text block keeps its line breaks. Java removes the indentation that all lines share, so the address starts at the left edge.

## String Formatting

### Concatenation

**Concatenation** means joining strings together. You use the `+` operator, or the `concat()` method.

When either side of `+` is a `String`, Java turns the other side into text and joins them.

```java runnable title="+ joins text, and order matters"
public class Main {
    public static void main(String[] args) {
        String first = "Asha";
        String last = "Kumar";

        System.out.println(first + " " + last);
        System.out.println(first.concat(" ").concat(last));
        System.out.println("Score: " + 40 + 2);
        System.out.println("Score: " + (40 + 2));
        System.out.println(40 + 2 + " is the score");
    }
}
```

Expected output:

```text
Asha Kumar
Asha Kumar
Score: 402
Score: 42
42 is the score
```

Java works from left to right.

- In `"Score: " + 40 + 2`, the text comes first, so `40` and `2` are joined as text.
- In `40 + 2 + " is the score"`, the two numbers come first, so they are added to make `42` before the text is joined.

Use brackets whenever you want maths inside a sentence.

`+` can join a string with any value. Numbers and `boolean` values become their text form. Objects are turned into text with their `toString()` method. A `null` value becomes the word `null`.

### Formatting Methods

Joining many pieces with `+` can get messy. **Formatting methods** let you write a template with placeholders, then fill them in.

- `String.format(template, values...)` returns the formatted string.
- `System.out.printf(template, values...)` prints it straight away.
- `"template".formatted(values...)` does the same as `String.format`, in a shorter form.

Each placeholder starts with `%`:

| Placeholder | Used for | Example output |
|---|---|---|
| `%s` | Any value as text | `Asha` |
| `%d` | Whole numbers | `42` |
| `%.2f` | A decimal with 2 places | `3.50` |
| `%n` | A new line | — |
| `%5d` | A whole number, padded to 5 characters | `   42` |

```java readonly title="Templates with placeholders"
public class Main {
    public static void main(String[] args) {
        String item = "Notebook";
        int quantity = 3;
        double price = 45.5;

        String line = String.format("%s x %d = %.2f", item, quantity, quantity * price);
        System.out.println(line);

        System.out.printf("Total due: Rs. %.2f%n", quantity * price);

        String report = "%s scored %d%%".formatted("Ravi", 87);
        System.out.println(report);
    }
}
```

Output:

```text
Notebook x 3 = 136.50
Total due: Rs. 136.50
Ravi scored 87%
```

`%.2f` always shows two decimal places, so `136.5` becomes `136.50`.

`%%` prints a real percent sign, because a single `%` starts a placeholder.

The values fill the placeholders **in order**. The first value goes into the first placeholder.

## String Class

`String` is a **class** in the `java.lang` package. Java imports `java.lang` for you, so you never need an import for `String`.

Because `String` is a class, every string is an **object**. That is why you can call methods on it with a dot, like `name.length()`.

A few facts about the `String` class:

- It is `final`, so no other class can extend it.
- A `String` variable can be `null`, which means it points to no string at all. Calling a method on `null` stops the program with a `NullPointerException`.
- An **empty** string `""` is a real string with zero characters. It is not the same as `null`.

## String Methods

The `String` class has many methods. Here are the ones you will use most.

### Length and Characters

```java runnable title="Measure the text and read single characters"
public class Main {
    public static void main(String[] args) {
        String word = "Programming";

        System.out.println("Length: " + word.length());
        System.out.println("First letter: " + word.charAt(0));
        System.out.println("Last letter: " + word.charAt(word.length() - 1));
        System.out.println("Is empty? " + word.isEmpty());
        System.out.println("Is blank? " + "   ".isBlank());
    }
}
```

Expected output:

```text
Length: 11
First letter: P
Last letter: g
Is empty? false
Is blank? true
```

`charAt(index)` returns one `char`. The last index is `length() - 1`, just like an array.

`isBlank()` is true for a string that is empty or contains only spaces.

### Searching and Cutting

```java runnable title="Find, check and cut parts of a string"
public class Main {
    public static void main(String[] args) {
        String file = "report_2026_final.pdf";

        System.out.println(file.indexOf("_"));
        System.out.println(file.lastIndexOf("_"));
        System.out.println(file.contains("2026"));
        System.out.println(file.startsWith("report"));
        System.out.println(file.endsWith(".pdf"));
        System.out.println(file.substring(7, 11));
        System.out.println(file.substring(file.lastIndexOf(".") + 1));
    }
}
```

Expected output:

```text
6
11
true
true
true
2026
pdf
```

`indexOf` returns the position of the first match, or `-1` if there is none.

`substring(7, 11)` returns the characters from index 7 up to, but not including, index 11. With one argument, `substring` goes to the end of the string.

### Changing Case, Spaces and Content

Remember: each of these returns a **new** string.

```java runnable title="Every method here returns a new string"
public class Main {
    public static void main(String[] args) {
        String raw = "  hello world  ";

        System.out.println("[" + raw.trim() + "]");
        System.out.println(raw.trim().toUpperCase());
        System.out.println(raw.replace("world", "Java").trim());
        System.out.println("ha".repeat(3));

        String csv = "Asha,Ravi,Meena";
        String[] names = csv.split(",");
        System.out.println(names.length + " names, second is " + names[1]);

        System.out.println(String.join(" | ", names));
    }
}
```

Expected output:

```text
[hello world]
HELLO WORLD
hello Java
hahaha
3 names, second is Ravi
Asha | Ravi | Meena
```

`split(",")` breaks the text into an array at every comma. `String.join` does the opposite: it glues an array back together with a separator.

### String Methods at a Glance

| Method | Returns | Example | Result |
|---|---|---|---|
| `length()` | Number of characters | `"Java".length()` | `4` |
| `charAt(i)` | The character at index `i` | `"Java".charAt(1)` | `'a'` |
| `indexOf(s)` | First position of `s`, or `-1` | `"Java".indexOf("v")` | `2` |
| `substring(a, b)` | Characters from `a` up to `b` | `"Java".substring(1, 3)` | `"av"` |
| `contains(s)` | Whether `s` appears | `"Java".contains("av")` | `true` |
| `startsWith(s)` | Whether it begins with `s` | `"Java".startsWith("J")` | `true` |
| `toUpperCase()` | All capitals | `"Java".toUpperCase()` | `"JAVA"` |
| `toLowerCase()` | All small letters | `"Java".toLowerCase()` | `"java"` |
| `trim()` | Without spaces at both ends | `" Java ".trim()` | `"Java"` |
| `replace(a, b)` | Every `a` swapped for `b` | `"Java".replace("a", "o")` | `"Jovo"` |
| `split(s)` | An array, broken at `s` | `"a,b".split(",")` | `["a", "b"]` |
| `isEmpty()` | Whether the length is 0 | `"".isEmpty()` | `true` |
| `repeat(n)` | The text repeated `n` times | `"ab".repeat(2)` | `"abab"` |

## String Comparison

Comparing strings is one of the most common sources of bugs in Java.

### equals()

`equals()` checks whether two strings hold **the same text**. This is almost always what you want.

### equalsIgnoreCase()

`equalsIgnoreCase()` also compares the text, but treats capital and small letters as the same.

### ==

`==` checks whether two variables point to **the same object** in memory. It does not compare the text.

It may seem to work with literals, because of the String Pool. But text that comes from user input, a file, or a method like `substring` is often a different object. Then `==` gives `false`, even when the text matches.

### compareTo()

`compareTo()` compares two strings in **dictionary order**. It returns:

- a **negative** number if the first string comes first;
- `0` if they are equal;
- a **positive** number if the first string comes later.

```java runnable title="Four ways to compare strings"
public class Main {
    public static void main(String[] args) {
        String saved = "admin";
        String typed = "ADMIN".toLowerCase();

        System.out.println("equals: " + saved.equals(typed));
        System.out.println("==: " + (saved == typed));
        System.out.println("equalsIgnoreCase: " + "Chennai".equalsIgnoreCase("CHENNAI"));

        System.out.println("apple vs banana: " + "apple".compareTo("banana"));
        System.out.println("pear vs pear: " + "pear".compareTo("pear"));
        System.out.println("mango vs grape: " + "mango".compareTo("grape"));
    }
}
```

Expected output:

```text
equals: true
==: false
equalsIgnoreCase: true
apple vs banana: -1
pear vs pear: 0
mango vs grape: 6
```

`typed` holds `"admin"`, the same text as `saved`. But it was made at run time by `toLowerCase()`, so it is a separate object. `equals` says true, while `==` says false.

`"apple"` comes before `"banana"`, so `compareTo` gives a negative number. Only the sign matters, not the exact value.

Always compare text with `equals()`. A safe habit is to put the known literal first: `"admin".equals(input)`. That version never throws a `NullPointerException`, even when `input` is `null`.

## StringBuilder

Every time you change a `String`, Java makes a new one. In a loop that adds text hundreds of times, that means hundreds of thrown-away strings.

A **`StringBuilder`** is a **mutable** (changeable) sequence of characters. It changes its own text in place, without making a new object each time.

Think of a whiteboard. You can add words, rub some out, and insert others, all on the same board.

```java runnable title="StringBuilder changes its own text in place"
public class Main {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("Hello");

        sb.append(" World");
        System.out.println(sb);

        sb.insert(0, ">> ");
        System.out.println(sb);

        sb.replace(3, 8, "Hi");
        System.out.println(sb);

        sb.reverse();
        System.out.println(sb);

        System.out.println("Length: " + sb.length());
    }
}
```

Expected output:

```text
Hello World
>> Hello World
>> Hi World
dlroW iH >>
Length: 11
```

Each call changes the same `sb` object. There is no need to write `sb = sb.append(...)`.

| Method | What it does |
|---|---|
| `append(x)` | Adds `x` to the end |
| `insert(i, x)` | Adds `x` at index `i` |
| `delete(a, b)` | Removes characters from `a` up to `b` |
| `replace(a, b, s)` | Replaces characters from `a` up to `b` with `s` |
| `reverse()` | Reverses the text |
| `toString()` | Returns the text as a normal `String` |

### Building Text in a Loop

This is the main reason `StringBuilder` exists.

```java runnable title="Build a long string in a loop"
public class Main {
    public static void main(String[] args) {
        StringBuilder line = new StringBuilder();

        for (int i = 1; i <= 5; i++) {
            line.append(i);
            if (i < 5) {
                line.append(" - ");
            }
        }

        String result = line.toString();
        System.out.println(result);
    }
}
```

Expected output:

```text
1 - 2 - 3 - 4 - 5
```

`toString()` turns the finished builder into a normal `String` when you are done.

## StringBuffer

**`StringBuffer`** has the same methods as `StringBuilder`. You use it the same way.

The difference is safety with **threads**. A thread is one path of work, and a program can run several at the same time.

- `StringBuffer` is **thread-safe**. Only one thread can change it at a time.
- `StringBuilder` is **not** thread-safe, but it is faster.

```java runnable title="StringBuffer works just like StringBuilder"
public class Main {
    public static void main(String[] args) {
        StringBuffer log = new StringBuffer();

        log.append("Start");
        log.append(" -> Load");
        log.append(" -> Save");
        log.append(" -> Done");

        System.out.println(log);
        System.out.println("Steps text length: " + log.length());
    }
}
```

Expected output:

```text
Start -> Load -> Save -> Done
Steps text length: 29
```

For normal programs that use one thread, choose `StringBuilder`. Use `StringBuffer` only when several threads change the same text.

## Strings vs StringBuilder

| | `String` | `StringBuilder` | `StringBuffer` |
|---|---|---|---|
| Can it change? | No (immutable) | Yes (mutable) | Yes (mutable) |
| A change creates | A new object | Nothing new, it edits itself | Nothing new, it edits itself |
| Thread-safe? | Yes, because it cannot change | No | Yes |
| Speed for many changes | Slow | Fast | A little slower than `StringBuilder` |
| Best for | Fixed text, keys, labels | Building text in a loop | Building text shared between threads |

**Easy Way to Remember:** text that stays the same is a `String`. Text you build step by step is a `StringBuilder`.

## Common mistakes

The following mistakes are common when working with strings. Each row shows what you see, why it happens and how to fix it.

| Symptom | Cause | Fix |
|---|---|---|
| A string looks unchanged after a method call | The new string was not stored | Assign the result back: `s = s.trim();` |
| A match fails when the text looks the same | `==` was used on strings | Use `equals()` |
| `StringIndexOutOfBoundsException` | An index of `length()` or more was used | Use indexes from `0` to `length() - 1` |
| `NullPointerException` | A method was called on a `null` string | Check for `null`, or put the literal first |
| `Total: 55` instead of `Total: 10` | Text came before the maths | Put the maths in brackets |
| `char cannot be converted to String` | Single quotes were used | Use double quotes for a `String` |

## What to Remember

- A `String` is immutable. Its methods return new strings, so store the result.
- Literals are shared in the String Pool. `new String(...)` makes a separate object.
- Join text with `+`, and use brackets for maths. Use `String.format` or `printf` for neat output.
- `length()`, `charAt`, `indexOf`, `substring`, `split`, `replace`, and `trim` cover most daily string work.
- Compare text with `equals()`, never with `==`. Use `compareTo()` for dictionary order.
- Use `StringBuilder` to build text step by step. Use `StringBuffer` only when threads share the text.

Next: DateTime
