Java · Beginner

Strings in Java

10 min readUpdated September 24, 2026Every example verified

In short: A Java String is an immutable sequence of characters: methods such as toUpperCase() and replace() return a new String and leave the original untouched. Compare text with equals(), not ==, read parts with charAt() and substring(), and build text in a loop with StringBuilder.

Text as an object

A String holds a sequence of characters and is the type of every literal in double quotes. Unlike the primitives, it is an object with methods, and the first thing to know about it is that it is immutable: once created, a String never changes. code.toLowerCase() does not lower-case code; it returns a new String, and code still holds the original. Immutability is why strings can be shared freely without anyone worrying that other code will alter them. It also means that "modifying" a string always means assigning the result: name = name.trim();.

The everyday methods read information out. length() counts characters; charAt(i) returns the character at index i, counted from 0, as a char; substring(from, to) returns the part from index from up to but not including to, so substring(4, 8) is four characters long. indexOf returns the first position of a character or substring, or -1 when it is absent; contains, startsWith and endsWith answer yes-or-no questions. toUpperCase, toLowerCase, trim, strip (Java 11) and replace return transformed copies. split(",") cuts a string on a separator into a String[], which is how a line of comma-separated input becomes fields.

Equality is the classic trap. == on strings compares references: it asks whether two variables point at the same object. Literals written in the source are shared, so "basic" == "basic" happens to be true, but text read from input or built at run time is a separate object with the same characters, and == says false. equals compares the characters and is always what you mean; equalsIgnoreCase ignores case; compareTo orders two strings alphabetically for sorting.

A char is a 16-bit number underneath. 'a' + 1 is the int 98, not "b"; cast back with (char) ('a' + 1) when you want the letter. Character.isDigit, Character.isLetter and Character.toUpperCase classify and convert single characters, and toCharArray() turns a string into a char[] you can loop over.

Joining with + is fine for a handful of pieces, but each + in a loop creates a fresh String and copies everything so far, so building a long result one character at a time is quadratic. StringBuilder is a mutable buffer: append adds to it in place and toString() produces the final String once. Reversing a word, assembling a report line by line and generating CSV all belong in a StringBuilder.

For aligned or rounded output, String.format and System.out.printf fill placeholders: %d for whole numbers, %.2f for a double with two decimals, %s for text, %-10s for text padded to ten characters on the left, %n for a line break. Since Java 15, a text block opened with three double quotes holds multi-line text without escape sequences.

Converting between text and numbers goes through Integer.parseInt and Double.parseDouble one way and String.valueOf or + "" the other. A parse of text that is not a number throws NumberFormatException, which the exceptions lesson shows how to handle.

Syntax

 Java · syntax
String s = "TKT-2048-B";
s.length()               // 10
s.charAt(0)              // 'T'
s.substring(4, 8)        // "2048"  (end index excluded)
s.indexOf("-")           // 3, or -1 if absent
s.contains("2048")       // true
s.toLowerCase()          // new String; s is unchanged
s.trim()                 // copy without leading/trailing whitespace
s.split("-")             // String[] {"TKT", "2048", "B"}
s.equals(other)          // same characters?
s.equalsIgnoreCase(other)

StringBuilder sb = new StringBuilder();
sb.append("a").append(1);
String done = sb.toString();

String line = String.format("%-6s|%5.2f", "rye", 3.5);

Escape a double quote inside a literal as \", a backslash as \\, and write a line break as \n. Every method that changes text returns a new String; assign it or the change is lost.

Inspecting a ticket code

One string, nine questions. Note that the original is unchanged on the second-last line.

 Java
public class Main {
    public static void main(String[] args) {
        String code = "TKT-2048-B";
        System.out.println(code.length());
        System.out.println(code.charAt(0));
        System.out.println(code.substring(4, 8));
        System.out.println(code.indexOf('-'));
        System.out.println(code.toLowerCase());
        System.out.println(code.startsWith("TKT"));
        System.out.println(code.replace('-', '/'));
        System.out.println(code);
        System.out.println(code.contains("2048"));
    }
}

Output

10
T
2048
3
tkt-2048-b
true
TKT/2048/B
TKT-2048-B
true

charAt(0) is the first character and indexOf('-') finds the first dash at index 3. substring(4, 8) takes indexes 4, 5, 6 and 7. Both toLowerCase() and replace() return new strings; printing code afterwards shows the original still has its dashes and capitals.

Cleaning input, equals and split

A passphrase typed with stray spaces. The program is run with the input line " open sesame " (two leading spaces, one trailing).

 Java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String typed = in.nextLine();
        String expected = "open sesame";
        String cleaned = typed.trim();
        System.out.println("[" + typed + "]");
        System.out.println("[" + cleaned + "]");
        System.out.println(cleaned.equals(expected));
        System.out.println(cleaned.equalsIgnoreCase("OPEN SESAME"));
        String[] words = cleaned.split(" ");
        System.out.println(words.length + " words, first: " + words[0]);

        String csv = "rye,spelt,sourdough";
        for (String item : csv.split(",")) {
            System.out.println("- " + item);
        }
    }
}

Input given to the program: open sesame

Output

[  open sesame ]
[open sesame]
true
true
2 words, first: open
- rye
- spelt
- sourdough

The brackets make the whitespace visible: trim() removed it and returned a new String while typed kept it. equals compares characters, so the cleaned input matches the expected phrase even though it is a different object. split returns an array, which the enhanced for loop walks in the CSV part; each field is its own String.

StringBuilder, counting characters and formatting

Reversing a word one character at a time, counting vowels, and producing an aligned table row.

 Java
public class Main {
    public static void main(String[] args) {
        String word = "kayak";
        StringBuilder sb = new StringBuilder();
        for (int i = word.length() - 1; i >= 0; i--) {
            sb.append(word.charAt(i));
        }
        String reversed = sb.toString();
        System.out.println(reversed);
        System.out.println(word.equals(reversed));

        int vowels = 0;
        for (char ch : "sourdough".toCharArray()) {
            if ("aeiou".indexOf(ch) >= 0) {
                vowels++;
            }
        }
        System.out.println("Vowels: " + vowels);

        String line = String.format("%-10s|%6.2f|%3d", "rye", 3.5, 12);
        System.out.println(line);
        System.out.println("ab".repeat(3));
    }
}

Output

kayak
true
Vowels: 4
rye       |  3.50| 12
ababab

The loop walks the indexes from the last down to 0 and appends each character to the builder; toString() produces the String once at the end. Reading "kayak" backwards gives the same word, so equals is true. The vowel count uses indexOf on a small string of vowels as a membership test. In the format string, %-10s left-aligns text in ten columns, %6.2f right-aligns a number in six with two decimals and %3d right-aligns an int in three. repeat (Java 11) concatenates copies.

Methods you will use most

CallReturnsExample
length()number of characters"rye".length() is 3
charAt(i)the char at index i"rye".charAt(1) is 'y'
substring(a, b)characters a to b-1"sourdough".substring(0, 4) is "sour"
indexOf(x)first index of x, or -1"spelt".indexOf("el") is 2
equals(t) / equalsIgnoreCase(t)true if the characters match"Rye".equalsIgnoreCase("rye")
trim() / strip()copy without surrounding whitespace" a ".trim() is "a"
split(sep)String[] of the pieces"a,b".split(",")
toUpperCase() / toLowerCase()converted copy"rye".toUpperCase() is "RYE"
replace(a, b)copy with every a replaced by b"a-b".replace('-', '/')
isEmpty() / isBlank()true if length 0 / only whitespace" ".isBlank() is true

Common mistakes

  • Comparing strings with ==

    Why it goes wrong: typed == "yes" compares references. It can be true for two literals and false for the same text read from input, so the bug appears only with real data.

    Fix: Use equals, or equalsIgnoreCase when case does not matter.

     Java · fix
    if (typed.equals("yes")) {
        // ...
    }
  • Calling a method and discarding the result

    Why it goes wrong: name.toUpperCase(); on its own line computes a new String and throws it away; name is unchanged because strings are immutable.

    Fix: Assign the result back or to a new variable.

     Java · fix
    name = name.toUpperCase();
  • Indexing one past the end

    Why it goes wrong: The last valid index is length() - 1, so s.charAt(s.length()) throws StringIndexOutOfBoundsException: Index 3 out of bounds for length 3. substring(2, 10) on a short string fails the same way.

    Fix: Loop with i < s.length(), and check indexOf for -1 before using it as an index.

  • Doing arithmetic on a char and expecting a letter

    Why it goes wrong: System.out.println('a' + 1) prints 98, because the char is promoted to an int before the addition, and "" + 'a' + 1 prints a1.

    Fix: Cast the result back to char when you want the next letter.

     Java · fix
    char next = (char) ('a' + 1);   // 'b'

Where you use this

Almost every program starts by turning text into structured data. A line of an order file such as rye, 3, 2.75 becomes a product, a quantity and a price by splitting on the comma, trimming each field and parsing the numbers; the reverse trip, formatting values into a padded receipt line, uses String.format. Checking a username for illegal characters, extracting the year from a date string and normalising an email to lower case before comparing are all a few calls to these methods. When the text is large or assembled in a loop, StringBuilder keeps it fast.

 Java · in practice
String[] parts = line.split(",");
String product = parts[0].trim();
int quantity = Integer.parseInt(parts[1].trim());
double price = Double.parseDouble(parts[2].trim());
System.out.println(String.format("%-12s %3d x %6.2f", product, quantity, price));

Key points

  • Strings are immutable: every transforming method returns a new String, so assign the result.
  • Compare text with equals or equalsIgnoreCase; == compares references.
  • Indexes start at 0; charAt(length()) is out of bounds; substring(a, b) excludes b.
  • indexOf returns -1 when nothing is found; test for it before indexing.
  • split turns one line into a String[]; trim removes surrounding whitespace.
  • A char is a number in arithmetic; cast back to char for a letter.
  • Build long or looped text with StringBuilder; format output with String.format or printf.

Try it yourself

The program reads a product name and prints its length. Change it to print the name in upper case, followed by a space and the length in parentheses with the word letters, so that the input spelt prints SPELT (5 letters).

Your program
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String product = in.nextLine();
        System.out.println(product.length());
    }
}
Input the program receives: spelt
Expected output: SPELT (5 letters)

Practise this

Exercises for this lesson are in the Java practice set.

Open the Java playground

Frequently asked questions

Why are strings immutable in Java?

Because an object that cannot change is safe to share. Several variables, threads and collections can hold the same String without any of them being surprised by a change made elsewhere, the JVM can reuse one object for identical literals, and a String's hash code can be computed once and cached, which makes it a fast and reliable map key. The cost is that building text piece by piece should go through StringBuilder.

How do I convert a String to an int in Java?

Use Integer.parseInt(text), which returns an int, or Integer.valueOf(text) for an Integer object. The text must be a whole number with an optional sign and no spaces, so trim input first; anything else throws a NumberFormatException. Double.parseDouble and Long.parseLong do the same for other types, and String.valueOf(n) goes the other way.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with OpenJDK 21 at build time by the publishing checks, and the output shown is what it printed. Running Java inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with OpenJDK 21 locally.