Java · Beginner
Strings in Java
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
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.
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).
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.
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
| Call | Returns | Example |
|---|---|---|
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, orequalsIgnoreCasewhen case does not matter.Java · fixif (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;nameis unchanged because strings are immutable.Fix: Assign the result back or to a new variable.
Java · fixname = name.toUpperCase();Indexing one past the end
Why it goes wrong: The last valid index is
length() - 1, sos.charAt(s.length())throwsStringIndexOutOfBoundsException: 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 checkindexOffor -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' + 1printsa1.Fix: Cast the result back to char when you want the next letter.
Java · fixchar 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.
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
equalsorequalsIgnoreCase;==compares references. - Indexes start at 0;
charAt(length())is out of bounds;substring(a, b)excludes b. indexOfreturns -1 when nothing is found; test for it before indexing.splitturns one line into aString[];trimremoves surrounding whitespace.- A
charis a number in arithmetic; cast back tocharfor a letter. - Build long or looped text with
StringBuilder; format output withString.formatorprintf.
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).
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());
}
}
speltSPELT (5 letters)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.toUpperCase() + " (" + product.length() + " letters)");
}
}
Practise this
Exercises for this lesson are in the Java practice set.
Related lessons
- Variables and Data Types in JavaJava's primitive types and String, declaring and initialising variables, var, casting and integer overflow: why the type you pick decides what a value can hold.9 min
- Arrays in JavaHow Java arrays store a fixed number of values of one type: creating and indexing them, length, the for-each loop, java.util.Arrays helpers and 2D arrays.9 min
- Loops in Java: for, while and do-whileHow Java repeats work with for, while and do-while loops, when each fits, what break and continue do, and how to avoid off-by-one and infinite loops.9 min
- Methods in JavaHow to define and call Java methods: parameters and arguments, return types and void, overloading, variable scope, and why arguments are passed by value.9 min
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.
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.