1.15.1 Creating String Objects
Almost every program works with text: names, messages, passwords, file names, answers typed by a user. Since Topic 1.3 you have been writing text as string literals and printing it. In Topics 1.12 to 1.14 you learned that String is a class, so text in a Java program is stored as objects. This chapter is about what you can do with those objects. First, though, how does a String object come into existence?
A String object holds a sequence of characters: letters, digits, spaces, punctuation, one after another in a fixed order. "Number 23" is nine characters long: N, u, m, b, e, r, a space, 2, and 3. The digits in it are characters, not a number you can do arithmetic with.
There are two ways to make one:
- Write a string literal, text inside double quotes. Java creates a
Stringobject holding that text. - Call the
Stringclass’s constructor withnew, the same way you created objects in Topic 1.13. Passing a literal, as innew String("Denver"), creates a newStringobject with the same characters.
Key idea
A String object is a sequence of characters. You can create one with a string literal ("Falcons") or by calling the
String constructor (new String("Denver")).
Worked example: Two ways to create a String
Look at lines 5 and 6: one uses a literal, the other calls the constructor.
public class CreateStrings
{
public static void main(String[] args)
{
String team = "Falcons";
String city = new String("Denver");
String jersey = "Number 23";
System.out.println(team);
System.out.println(city);
System.out.println(jersey);
}
}| Line | What Java does |
|---|---|
| 5 | Creates a String object holding Falcons; team holds a reference to it |
| 6 | new calls the String constructor, which builds an object holding Denver |
| 7 | Creates a String object holding Number 23 (the space and digits are characters) |
| 8–10 | Prints the characters of each object |
Falcons
Denver
Number 23The literal is shorter and is what programmers use almost all the time. Both forms give you a String object you can use in exactly the same ways.
Now notice something that is not in this program: an import statement. When you used Scanner in Topic 1.4, the program needed import java.util.Scanner; at the top, because Scanner lives in the java.util package (a named group of related classes). String lives in a package called java.lang, and Java makes every class in java.lang available to every program automatically. Java does this because classes like String are needed by nearly every program, so requiring an import for them would just be clutter.
Watch out
Forgetting the quotes. Without double quotes, Java does not see text; it sees a name, and it goes looking for a variable called Falcons:
public class MissingQuotes
{
public static void main(String[] args)
{
String team = Falcons;
System.out.println(team);
}
}The compiler reports MissingQuotes.java:5: error: cannot find symbol, because no variable named Falcons exists. Students make this slip because the word looks like text to a person. The fix is String team = "Falcons";. The quotes are what tell Java “these characters are the value.”
Pause and try
Question 1 · Multiple choice
A program contains String title = "Hamlet"; and has no import statements at all. What happens when it is compiled?
- A compile error, because
Stringmust be imported before it can be used - It compiles, because
Stringis injava.lang, which every program can use without an import - It compiles only if the line is changed to
String title = new String("Hamlet"); - It compiles, but the program crashes when it reaches that line
Check your reasoning
B. Classes in the java.lang package, including String, are available by default, so no import is needed.
- A is wrong: that is true for
Scanner(injava.util), not forString. This choice mixes up the two packages. - C is wrong: a string literal creates a
Stringobject just as well as the constructor does. Both are allowed. - D is wrong: nothing about this line is impossible at run time; it just creates a
Stringobject.
Reread 1.15.1 Creating String Objects if you chose differently.
1.15.2 Concatenating Strings
Suppose a program knows a student’s first name and last name in two separate variables, and it needs to print the full name. Or it needs to build a message like Hello, Ada! from pieces. You need a way to glue strings together.
In plain words: put the second string’s characters right after the first string’s characters, with nothing added and nothing removed. Joining strings end to end is called concatenation. In Java, the + operator concatenates when it is used with a String. The result is a new String object; the two original strings are left exactly as they were.
Key idea
+ with strings concatenates: it creates a new String holding the first string’s characters followed by the second’s.
Worked example: Building a full name
Watch the space in line 3: it is its own string literal, " ", holding one space character.
String first = "Ada";
String last = "Lovelace";
String full = first + " " + last;
System.out.println(full);
System.out.println(first);Java evaluates line 3 from left to right, one + at a time:
| Step | Expression | Result (a new String) |
|---|---|---|
| 1 | first + " " |
"Ada " |
| 2 | "Ada " + last |
"Ada Lovelace" |
| 3 | full gets a reference to |
"Ada Lovelace" |
Ada Lovelace
AdaThe second line of output shows that first still holds Ada. Concatenation built a new object; it did not change first.
Now vary the tool. Compound assignment from Topic 1.6 works with strings too: greeting += name; means greeting = greeting + name;. It concatenates, creates a new String, and makes the variable refer to that new object. This is handy for building a message one piece at a time:
String name = "Ada";
String greeting = "Hello";
greeting += ", ";
greeting += name;
greeting += "!";
System.out.println(greeting);| After line | greeting refers to |
|---|---|
| 2 | "Hello" |
| 3 | "Hello, " |
| 4 | "Hello, Ada" |
| 5 | "Hello, Ada!" |
Hello, Ada!
Each += made a brand-new String object and moved greeting to it. You will see why that matters in 1.15.7.
Watch out
Expecting + to add spaces. Concatenation adds exactly what you give it and nothing else:
String first = "Ada";
String last = "Lovelace";
System.out.println(first + last);AdaLovelaceThis compiles and runs, so it is a logic error. Students expect a space because people read two words as separate. Java does not. Put the space in yourself: first + " " + last.
Pause and try
Question 2 · Tracing
What does this code print? Track what playlist and genre refer to after each line.
String playlist = "Rock";
String genre = "Jazz";
playlist += " and ";
playlist += genre;
genre = "Pop";
System.out.println(playlist);
System.out.println(genre);Check your reasoning
Rock and Jazz
Pop| After line | playlist |
genre |
|---|---|---|
| 2 | "Rock" |
"Jazz" |
| 3 | "Rock and " |
"Jazz" |
| 4 | "Rock and Jazz" |
"Jazz" |
| 5 | "Rock and Jazz" |
"Pop" |
Line 4 copies the characters of genre into a new string at that moment. Changing genre on line 5 makes genre refer to a different string, but it cannot reach back into playlist, which already holds Rock and Jazz. Notice the spaces in " and " on line 3; without them the result would be RockandJazz.
Reread 1.15.2 Concatenating Strings if your output differed.
1.15.3 Concatenating Numbers and Booleans
Programs constantly mix text with values: Score: 95, Price: $4.5, Passed: true. The number 95 is an int, not a String. Can + join them?
Yes. When one side of + is a String and the other side is a primitive value (int, double, or boolean), Java first turns the primitive into its text form, then concatenates. The int 95 becomes the characters 9 and 5. Java does this on its own, without you asking, so it is called an implicit conversion.
Worked example: Text plus values
int score = 95;
double price = 4.5;
boolean passed = true;
System.out.println("Score: " + score);
System.out.println("Price: $" + price);
System.out.println("Passed: " + passed);Score: 95
Price: $4.5
Passed: trueIn each line, the left side of + is a String, so Java converts the right side to text (95, 4.5, true) and concatenates. The variables themselves do not change: score is still the int 95.
The tricky part is that + has two jobs. With two numbers it adds; with a String on either side it concatenates. Java decides one + at a time, moving left to right (the same left-to-right rule for equal-precedence operators you learned in Topic 1.3). So the order of the pieces changes the result:
System.out.println("Total: " + 3 + 4);
System.out.println(3 + 4 + " points");
System.out.println("Total: " + (3 + 4));
System.out.println("Half: " + 7 / 2);Total: 34
7 points
Total: 7
Half: 3
| Line | First + |
Second + |
Result |
|---|---|---|---|
| 1 | "Total: " + 3 → "Total: 3" (string) |
"Total: 3" + 4 → concatenate |
"Total: 34" |
| 2 | 3 + 4 → 7 (two ints: add) |
7 + " points" → concatenate |
"7 points" |
| 3 | Parentheses first: 3 + 4 → 7 |
"Total: " + 7 → concatenate |
"Total: 7" |
| 4 | / has higher precedence: 7 / 2 → 3 (int) |
"Half: " + 3 → concatenate |
"Half: 3" |
Once the running result is a String, every later + in that chain concatenates. Before that, + between two numbers still adds. Precedence still applies too: *, /, and % happen before any +.
Key idea
A + with a String on either side concatenates, turning a number or boolean into text. Java works left to right, so
"Total: " + 3 + 4 gives "Total: 34"; use parentheses to add first.
Watch out
"Total: " + 3 + 4 is not Total: 7. Students read the line as “the label, then 3 plus 4,” but Java has no idea which pieces you meant to group. By the time it reaches 4, the left side is already the string "Total: 3", so it concatenates. If you want the sum, write "Total: " + (3 + 4).
Pause and try
Question 3 · Tracing
Write the exact output.
int goals = 2;
int assists = 3;
System.out.println("Points: " + goals + assists);
System.out.println(goals + assists + " points");
System.out.println(1 + 2 + "3" + 4 + 5);Check your reasoning
Points: 23
5 points
3345- Line 3:
"Points: " + goalsis a string, so+ assistsconcatenates:Points: 2, thenPoints: 23. - Line 4:
goals + assistsis two ints, so it adds to5; then5 + " points"concatenates. - Line 5:
1 + 2adds to3.3 + "3"concatenates to"33". From then on the left side is a string, so+ 4gives"334"and+ 5gives"3345".
Reread 1.15.3 Concatenating Numbers and Booleans if your output differed.
1.15.4 Indices and the length Method
To work with part of a string, such as its first letter or the text after an @, you need a way to say where in the string you mean. Java numbers the positions.
Each character in a string has a position number called its index (plural: indices). The first character is at index 0, not 1. Then each character, including spaces and punctuation, gets the next number. Think of the index as “how many characters come before this one”: the first character has zero characters before it.
index: 0 1 2 3 4 5
character: p l a n e t
The length of a string is how many characters it has. You get it by calling the instance method length(), whose header is int length(): it takes no arguments and returns an int. You call it with the dot operator from Topic 1.14, as in word.length(). "planet" has length 6, but its last index is 5, because counting started at 0.
Key idea
length() - 1. The last character is always at index length() - 1.Worked example: Counting characters
String word = "planet";
String cheer = "Go Bears!";
String empty = "";
System.out.println(word.length());
System.out.println(cheer.length());
System.out.println(empty.length());6
9
0| String | Characters counted | length() |
Last index |
|---|---|---|---|
"planet" |
p l a n e t | 6 | 5 |
"Go Bears!" |
G o (space) B e a r s ! | 9 | 8 |
"" |
none | 0 | none |
The space in "Go Bears!" is a character, and so is the !. Every one of them takes an index:
index: 0 1 2 3 4 5 6 7 8
character: G o B e a r s !The last string, "", is the empty string: a String object with no characters at all. Its length is 0 and it has no valid indices.
Watch out
Treating the length as the last index. Because "planet" has 6 characters, students often say the last character is at index 6. It is at index 5. Index 6 would be a seventh character, and there is none. In 1.15.6 you will see that asking for it crashes the program.
Pause and try
Question 4 · Multiple choice
For String motto = "Be kind.";, which statement is true?
motto.length()is 7, and thekis at index 4.motto.length()is 8, and thekis at index 3.motto.length()is 8, and the last index is 8.motto.length()is 6, because spaces and periods are not counted.
Check your reasoning
B. The characters are B, e, space, k, i, n, d, period: 8 in all. Counting from 0, B is 0, e is 1, the space is 2, and k is 3.
- A is wrong: it skips counting the space (giving 7) and counts positions from 1 (giving 4 for
k). - C is wrong: the length is 8, but the last index is always one less, 7.
- D is wrong: every character counts, including spaces and punctuation.
Reread 1.15.4 Indices and the length Method if you chose differently.
1.15.5 Extracting Text with substring
With indices you can describe any part of a string. Now you need a method that hands that part back to you: the first three letters of a word, everything after the @ in an email address, one single letter. A part of a string is called a substring, and the String method that gets one has the same name.
substring comes in two versions. They are on the Java Quick Reference, the sheet of method headers you can look at during the AP exam:
| Header | Returns |
|---|---|
String substring(int from, int to) |
The characters from index from up to, but not including, to |
String substring(int from) |
The characters from index from to the end of the string |
Take the two-parameter version apart:
fromis the index of the first character you want. It is included.tois the index where you stop. The character attois not included; the last character you get is atto - 1.- So the result has
to - fromcharacters.
Why stop just before to? Because then to - from is the length of the result, and substring(0, word.length()) is the whole word without any “minus one.” The one-parameter version is a shortcut: word.substring(from) gives the same result as word.substring(from, word.length()).
Key idea
substring(from, to) returns the characters at indices from through to - 1; it includes from and stops before
to. substring(from) goes from from to the end.
Worked example: Pieces of sunflower
First the index diagram:
index: 0 1 2 3 4 5 6 7 8
character: s u n f l o w e rString word = "sunflower";
System.out.println(word.substring(0, 3));
System.out.println(word.substring(3, 6));
System.out.println(word.substring(3));
System.out.println(word.substring(4, 5));| Call | Indices taken | Count | Result |
|---|---|---|---|
substring(0, 3) |
0, 1, 2 | 3 − 0 = 3 | "sun" |
substring(3, 6) |
3, 4, 5 | 6 − 3 = 3 | "flo" |
substring(3) |
3 through 8 | 9 − 3 = 6 | "flower" |
substring(4, 5) |
4 | 5 − 4 = 1 | "l" |
sun
flo
flower
lThe last call shows a pattern worth remembering: substring(index, index + 1) gives a one-character string, the character at index. This is how you get a single letter out of a string in this course.
Now vary the indices to the edges. What happens when from equals to, or when from is the length?
String word = "sunflower";
System.out.println("[" + word.substring(9) + "]");
System.out.println("[" + word.substring(2, 2) + "]");
System.out.println("[" + word.substring(8) + "]");[]
[]
[r]
The brackets are concatenated around each result so you can see it. substring(9) starts at index 9, which is just past the last character, so there is nothing to take: the result is the empty string. substring(2, 2) asks for “from 2, stopping before 2,” which is also zero characters. And substring(8) starts at the last index, so it returns the last character, "r". Both empty results are legal; the next subsection shows which indices are not.
Watch out
Expecting the to character to be included. A student wants "flow" from "sunflower". The f is at index 3 and the w is at index 6, so they write:
String word = "sunflower";
String part = word.substring(3, 6);
System.out.println(part);floThe w at index 6 is where substring stops, so it is left out. The fix is to use one past the last index you want:
String word = "sunflower";
String part = word.substring(3, 7);
System.out.println(part);flowA quick check: "flow" has 4 characters, and 7 - 3 is 4.
Pause and try
Question 5 · Tracing
Write the exact output.
String sport = "basketball";
System.out.println(sport.substring(6));
System.out.println(sport.substring(0, 6));
System.out.println(sport.substring(2, 3));
System.out.println(sport.substring(5, 7) + sport.substring(0, 1));Check your reasoning
index: 0 1 2 3 4 5 6 7 8 9
character: b a s k e t b a l lball
basket
s
tbbsubstring(6): index 6 to the end,"ball".substring(0, 6): indices 0 to 5,"basket".substring(2, 3): just index 2,"s".substring(5, 7)is indices 5 and 6,"tb".substring(0, 1)is"b". Concatenated:"tbb".
Reread 1.15.5 Extracting Text with substring if your output differed.
Question 6 · Debugging
This code is supposed to print the three-letter abbreviation Sat, but it prints Sa. Find the error and write the corrected line.
String day = "Saturday";
String shortName = day.substring(0, 2);
System.out.println(shortName);Check your reasoning
Error: the to argument is 2, so only indices 0 and 1 are taken. The character at index to is never included.
Corrected line:
String shortName = day.substring(0, 3);Three characters are wanted, and 3 - 0 is 3: indices 0, 1, and 2, which are S, a, t. The original compiled and ran without complaint, so this was a logic error.
Reread 1.15.5 Extracting Text with substring if you missed it.
1.15.6 Out-of-Range Indices
What if the program asks for characters that are not there, such as index 7 of a six-letter word? In Topic 1.1 you saw that some impossible requests are not caught by the compiler but crash the program while it runs. Asking for a missing index is one of them.
The rules, for a string of length n:
- A single character position must be from
0ton - 1. - For
substring(from, to), both values must be from0ton, andfrommust not be bigger thanto. (tomay equalnbecause the character attois never taken.) - For
substring(from),frommust be from0ton.
Break a rule and Java throws a StringIndexOutOfBoundsException, a run-time error whose name says exactly what happened: a string index was out of bounds (outside the allowed range). The compiler cannot catch it, because it checks only that you passed int values, not what those values will be when the line runs.
Key idea
Valid indices run from 0 to length() - 1. Asking substring for an index outside its allowed range throws a
StringIndexOutOfBoundsException while the program runs.
Worked example: One index too far
public class BadSubstring
{
public static void main(String[] args)
{
String word = "planet";
System.out.println("Word: " + word);
System.out.println(word.substring(0, 7));
System.out.println("Done");
}
}| Moment | What happens | Console |
|---|---|---|
| Compile | Every line follows Java’s rules, so it compiles | (nothing) |
| Line 6 | Concatenates and prints | Word: planet |
| Line 7 | "planet" has length 6, so to may be at most 6. 7 is out of range; exception thrown |
exception message |
| Line 8 | Never runs | (nothing) |
Word: planet
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [0, 7) out of bounds for length 6
at BadSubstring.main(BadSubstring.java:7)Read the message in parts: the exception’s name, then the range that was requested ([0, 7) means “from 0, stopping before 7”), the string’s length, and the line number. Older versions of Java word the middle part differently (for example begin 0, end 7, length 6), but the exception name and line number are what matter.
Now vary which rule is broken. Each of these also compiles and then throws the same exception:
Call on "planet" |
Problem |
|---|---|
word.substring(7) |
Start is past the end (largest is 6) |
word.substring(4, 2) |
Start is bigger than stop |
word.substring(-1, 3) |
Negative index |
String word = "planet";
System.out.println(word.substring(7));String word = "planet";
System.out.println(word.substring(4, 2));String word = "planet";
System.out.println(word.substring(-1, 3));Compare those with calls that sit right at the edge and are fine:
String word = "planet";
System.out.println("[" + word.substring(0, 6) + "]");
System.out.println("[" + word.substring(6) + "]");
System.out.println("[" + word.substring(5, 6) + "]");[planet]
[]
[t]
substring(0, 6) uses to = 6, the length, which is allowed. substring(6) starts at the length and returns the empty string. substring(5, 6) is the single-character pattern for the last index.
Watch out
Being too careful with the end. Once students learn that index length() does not exist, some start subtracting 1 everywhere, including in the to argument:
String word = "planet";
System.out.println(word.substring(0, word.length() - 1));
System.out.println(word.substring(0, word.length()));plane
planetThe first line drops the t. Because substring already stops before to, the to argument may be length(), and substring(0, word.length()) is the whole word. The - 1 belongs only where you name a single character’s position, such as the last index, length() - 1.
Pause and try
Question 7 · Multiple choice
For String code = "AP2026";, which call throws a StringIndexOutOfBoundsException?
code.substring(6)code.substring(0, 6)code.substring(3, 3)code.substring(2, 7)
Check your reasoning
D. "AP2026" has length 6, so to can be at most 6. Asking to stop before index 7 would need a character at index 6, which does not exist.
- A is wrong:
frommay equal the length; the result is the empty string"". - B is wrong:
tomay equal the length, because the character attois not taken. The result is the whole string,"AP2026". - C is wrong:
fromequal totois legal and returns"".
Reread 1.15.6 Out-of-Range Indices if you chose differently.
1.15.7 Strings Are Immutable
You have called substring and used += many times now. Here is a question you may not have asked: when you call word.substring(0, 3), does word lose its other letters?
No. A String object can never be changed once it is created. Every method you call on a string leaves that string exactly as it was and returns a new string as its answer. An object that cannot be changed after it is created is immutable. Methods like substring are like asking someone to read you part of a page: you get the words, but the page is not torn.
Why would Java design strings this way? Because several variables can refer to the same String object (Topic 1.13: a reference variable holds the object’s address, and copying the variable copies the address). If one part of a program could change the object, every other variable referring to it would see its text change unexpectedly. Because strings are immutable, sharing them is always safe.
Key idea
Strings are immutable: no method changes a String object. Methods return a new String, and to keep it you must store
it, for example word = word.substring(0, 3);.
Worked example: Two variables, one string
Topic 1.13 showed that assigning one reference variable to another makes both refer to the same object. Watch what += does when two variables share a string.
String original = "cat";
String other = original;
original += "s";
System.out.println(original);
System.out.println(other);| After line | original refers to |
other refers to |
Objects that exist |
|---|---|---|---|
| 1 | object A | (none yet) | A = "cat" |
| 2 | object A | object A | A = "cat" |
| 3 | object B | object A | A = "cat", B = "cats" |
cats
catLine 3 did not add an s to object A. It concatenated "cat" and "s" into a new object B and moved original to it. other still refers to A, which still holds cat, because nothing can change A.
Now vary it: call a method but do nothing with what it returns.
String word = "sunflower";
word.substring(0, 3);
System.out.println(word);sunflower
Line 2 runs. substring builds the new string "sun" and returns it, but nothing stores the result, so it is thrown away. word still refers to the original, unchanged "sunflower".
Watch out
Calling a method and expecting the string to change. Students write word.substring(0, 3); on its own line, the way they might write an instruction to “cut the word down.” It compiles and runs, but it has no visible effect, which makes it a confusing logic error. Store the returned string:
String word = "sunflower";
word = word.substring(0, 3);
System.out.println(word);sunThe original "sunflower" object was still not changed. word was moved to refer to the new object "sun".
Pause and try
Question 8 · Tracing
Write the exact output and explain why the first line is what it is.
String motto = "Practice daily";
String copy = motto;
motto.substring(9);
copy = copy.substring(0, 8);
System.out.println(motto);
System.out.println(copy);Check your reasoning
Practice daily
Practice| After line | motto refers to |
copy refers to |
|---|---|---|
| 1 | "Practice daily" |
|
| 2 | "Practice daily" |
"Practice daily" |
| 3 | "Practice daily" |
"Practice daily" |
| 4 | "Practice daily" |
"Practice" |
Line 3 builds "daily" but throws it away, because the result is not stored. Line 4 stores a new string, "Practice" (indices 0 to 7), in copy. That moves copy to a new object; it does not change the object motto refers to. Strings are immutable, so motto still prints Practice daily.
Reread 1.15.7 Strings Are Immutable if your output differed.
1.15.8 Finding Text with indexOf
substring needs index numbers, but often you do not know them in advance. Where does the username end in an email address? Where is the space in a full name? You need to search for the text and get back where it is.
The method int indexOf(String str) searches the string for str and returns the index where its first occurrence starts. If str is not in the string anywhere, it returns -1. Java uses -1 because it can never be a real index (indices start at 0), so it can only mean “not found.”
Key idea
indexOf(str) returns the index where the first occurrence of str begins, or -1 if str does not appear.
Worked example: Searching a sentence
index: 0 1 2 3 4 5 6 7 8 9 ...
character: t h e c a t s a ...String sentence = "the cat sat on the mat";
System.out.println(sentence.indexOf("the"));
System.out.println(sentence.indexOf("at"));
System.out.println(sentence.indexOf("dog"));
System.out.println(sentence.indexOf("The"));0
5
-1
-1| Call | Reasoning | Result |
|---|---|---|
indexOf("the") |
the appears twice; the first one starts at index 0 |
0 |
indexOf("at") |
First at is inside cat: c is 4, a is 5 |
5 |
indexOf("dog") |
Not in the sentence | -1 |
indexOf("The") |
Uppercase T is a different character from t, so The never appears |
-1 |
For a search string longer than one character, the result is the index of its first character. And the search is case-sensitive: uppercase and lowercase letters do not match each other.
Now combine indexOf with substring. The @ in an email address separates the user name from the domain, but its position depends on the address. Let indexOf find it:
String email = "ada@school.org";
int at = email.indexOf("@");
String user = email.substring(0, at);
String domain = email.substring(at + 1);
System.out.println(at);
System.out.println(user);
System.out.println(domain);3
ada
school.org
at is 3. substring(0, at) takes indices 0 to 2, stopping right before the @. substring(at + 1) starts one past the @ and runs to the end. This code works for any address with an @, however long the user name is.
Watch out
Feeding -1 straight into substring. If the text is not found, indexOf returns -1, and -1 is never a valid index:
public class NoAtSign
{
public static void main(String[] args)
{
String email = "ada.school.org";
int at = email.indexOf("@");
System.out.println(at);
String user = email.substring(0, at);
System.out.println(user);
}
}-1
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [0, -1) out of bounds for length 14
at NoAtSign.main(NoAtSign.java:8)Students forget that “not found” is a possible answer. Line 7 prints -1 normally; line 8 crashes. For now, use indexOf results in substring only when you know the text is present. In Unit 2 you will learn how a program can check the result first.
Pause and try
Question 9 · Tracing
Write the exact output.
String song = "Here Comes the Sun";
int spot = song.indexOf("the");
System.out.println(spot);
System.out.println(song.substring(spot));
System.out.println(song.indexOf("sun"));Check your reasoning
11
the Sun
-1Counting from 0: Here is 0–3, space 4, Comes is 5–9, space 10, so the starts at index 11. substring(11) runs from there to the end: the Sun. The last search is for sun with a lowercase s; the string contains only Sun, and the search is case-sensitive, so the result is -1.
Reread 1.15.8 Finding Text with indexOf if your output differed.
1.15.9 Comparing Strings with equals and compareTo
Programs need to compare text: did the user type the right password? Which of two names comes first alphabetically? Two String methods answer these questions. Both are on the Java Quick Reference.
equals: the same characters?
boolean equals(Object other) returns true if the string has exactly the same sequence of characters as other, and false otherwise. The parameter type is Object, the class every class extends (Topic 1.12), but in this course you will always pass it a String. “Exactly the same” means same length, same characters, same order, same capitalization.
Worked example: Checking a password
String password = "Tiger42";
System.out.println(password.equals("Tiger42"));
System.out.println(password.equals("tiger42"));
System.out.println(password.equals("Tiger42 "));
System.out.println(password.equals(new String("Tiger42")));true
false
false
true| Compared with | Same characters? | Result |
|---|---|---|
"Tiger42" |
Yes | true |
"tiger42" |
No: t vs T |
false |
"Tiger42 " |
No: extra space | false |
new String("Tiger42") |
Yes | true |
The last line compares against a different object created with the constructor. equals does not care that they are separate objects; it compares the characters.
compareTo: which comes first?
int compareTo(String other) compares two strings by dictionary (alphabetical) order and returns an int:
a.compareTo(b) returns |
Meaning |
|---|---|
| a negative number | a comes before b |
| zero | a and b are equal |
| a positive number | a comes after b |
Only the sign matters. The method promises negative, zero, or positive; do not try to memorize particular numbers.
Worked example: Alphabetical order
System.out.println("apple".compareTo("banana"));
System.out.println("apple".compareTo("cherry"));
System.out.println("banana".compareTo("apple"));
System.out.println("pear".compareTo("pear"));
System.out.println("car".compareTo("cart"));-1
-2
1
0
-1| Call | Reasoning | Sign |
|---|---|---|
"apple".compareTo("banana") |
a comes before b | negative |
"apple".compareTo("cherry") |
a comes before c (a different negative number) | negative |
"banana".compareTo("apple") |
Same pair, reversed order of the call | positive |
"pear".compareTo("pear") |
Same characters | zero |
"car".compareTo("cart") |
car matches the start of cart; shorter is first, as in a dictionary |
negative |
Key idea
a.equals(b) is true when the two strings have exactly the same characters. a.compareTo(b) is negative if a comes
first alphabetically, zero if they are equal, and positive if a comes after.
One detail of “alphabetical”: Java puts every uppercase letter before every lowercase letter, so "Zoo" comes before "apple":
System.out.println("Zoo".compareTo("apple"));
System.out.println("zoo".compareTo("apple"));-7
25
With all-lowercase (or all-uppercase) words, Java’s order matches the dictionary.
Watch out
Reading compareTo backwards. a.compareTo(b) describes where a is relative to b. A negative result means a is first. Students sometimes read negative as “a is less important” or flip which string is which. Say it out loud as a sentence: “apple compared to banana is negative, so apple comes before banana.”
Pause and try
Question 10 · Multiple choice
Which call returns a negative number?
"dog".compareTo("cat")"cat".compareTo("cats")"cat".compareTo("cat")"cow".compareTo("cat")
Check your reasoning
B. cat matches the beginning of cats and is shorter, so it comes first. The string the method is called on comes first, so the result is negative.
- A is wrong:
dogcomes aftercat(d is after c), so the result is positive. - C is wrong: identical strings give zero.
- D is wrong: both start with c, but the second letters are o and a, and o comes after a, so
cowis aftercat: positive.
Reread 1.15.9 Comparing Strings with equals and compareTo if you chose differently.
Question 11 · Multiple choice
Given String answer = "Paris";, which line prints true?
System.out.println(answer.equals("paris"));System.out.println(answer.equals("Paris "));System.out.println(answer.equals("Par" + "is"));System.out.println(answer.equals("PARIS"));
Check your reasoning
C. "Par" + "is" is concatenated first, producing a string with the characters Paris, exactly matching answer. equals compares characters, so it returns true.
- A is wrong: lowercase
pis a different character fromP. - B is wrong: the trailing space is a sixth character, so the strings differ.
- D is wrong: capitalization differs in four letters.
Reread 1.15.9 Comparing Strings with equals and compareTo if you chose differently.
1.15.10 Concatenating Other Objects
In 1.15.3 you saw that + turns a number into text. What happens if the thing on the other side of + is an object that is not a String, such as a Scanner? Java still needs text to concatenate, so it needs some way to ask any object for a text version of itself.
Topic 1.12 told you that every class in Java is a subclass of the Object class. Object has a method String toString() that returns a string representing the object. Because every class is a subclass of Object, every object has a toString method. So when you concatenate a String with any object, Java calls that object’s toString method for you and joins the text it returns.
The toString that Object provides is very general. Many classes replace it with their own version that describes their objects better. When a subclass has a public method with the same signature as a public method in its superclass but its own behavior, that is called method overriding. String overrides toString to return the string’s own characters, and Scanner overrides it to return a description of the scanner. (Writing your own toString is not part of this course; you only need to know that Java calls it.)
Key idea
Concatenating a String with any object calls that object’s toString method automatically. Every object has one,
because every class is a subclass of Object.
Worked example: Concatenating a Scanner
The text a Scanner returns from toString is long, so this program prints only parts of it, using substring and indexOf.
import java.util.Scanner;
public class ScannerText
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
String note = "Input: " + input;
String described = input.toString();
System.out.println(note.substring(0, 24));
System.out.println(described.substring(0, 17));
System.out.println(note.indexOf("["));
}
}Input: java.util.Scanner
java.util.Scanner
24| Line | What happens |
|---|---|
| 8 | input is not a String, so Java calls input.toString() and concatenates "Input: " with the text it returns |
| 9 | Calls toString directly; same text |
| 10 | The first 24 characters of note: the 7 characters of "Input: " plus java.util.Scanner, the class’s full name (17 characters) |
| 11 | The first 17 characters of the text from toString |
| 12 | The description continues with a [ right after the class name, at index 24 |
Scanner’s version of toString starts with the class’s full name and then lists settings inside brackets. It does not contain anything a user typed.
Now vary the object. A String made with the constructor is also an object, and its toString returns its own characters:
String city = new String("Denver");
String label = "City: " + city;
System.out.println(label);
System.out.println(city.toString());City: Denver
Denver
That is why concatenating two strings works the way you expect: the text String’s toString returns is just the string itself.
Watch out
Thinking "Input: " + input reads what the user typed. Concatenating a Scanner does not read any input. It only calls toString, which describes the Scanner object. To get what the user typed, you still call a reading method such as input.nextLine(), as in Topic 1.4, and concatenate the String it returns.
Pause and try
Question 12 · Multiple choice
Why can Java concatenate a String with an object of any class?
- Java converts every object to the characters the user typed into it.
- Every class is a subclass of
Object, which has atoStringmethod, so Java can call the object’stoStringto get text. - Only
StringandScannerobjects can be concatenated; other objects cause a compile error. - Java copies the object’s memory address into the string and never calls a method.
Check your reasoning
B. Because every class extends Object, every object has a toString method (perhaps overridden with a class-specific version). Concatenation calls it automatically.
- A is wrong: concatenation does not read input. A
Scanner’s text describes the scanner, not what was typed. - C is wrong: any object can be concatenated, because every object has
toString. - D is wrong: Java does call a method,
toString. Classes likeStringandScanneroverride it to return their own text.
Reread 1.15.10 Concatenating Other Objects if you chose differently.
Putting it together
A school’s sign-up page builds a username from a full name: the first letter of the first name, then the whole last name, then the number of characters in the full name. Here is the code, using indexOf, substring, concatenation, length(), and compareTo:
String fullName = "Grace Hopper";
int space = fullName.indexOf(" ");
String first = fullName.substring(0, space);
String last = fullName.substring(space + 1);
String username = first.substring(0, 1) + last + fullName.length();
System.out.println(username);
System.out.println(first.compareTo(last));
System.out.println(fullName);Index diagram for "Grace Hopper" (length 12):
index: 0 1 2 3 4 5 6 7 8 9 10 11
character: G r a c e H o p p e r
| Line | Code | Value produced | Why |
|---|---|---|---|
| 2 | fullName.indexOf(" ") |
space = 5 |
The space is the first (and only) one |
| 3 | fullName.substring(0, space) |
first = "Grace" |
Indices 0–4, stopping before the space |
| 4 | fullName.substring(space + 1) |
last = "Hopper" |
From index 6 to the end |
| 5 | first.substring(0, 1) |
"G" |
Single-character pattern: index 0 to 0 + 1 |
| 5 | "G" + last + fullName.length() |
username = "GHopper12" |
Left to right: "GHopper", then the int 12 becomes text |
| 7 | first.compareTo(last) |
negative | G comes before H |
| 8 | fullName |
"Grace Hopper" |
Immutable: no call changed it |
GHopper12
-1
Grace Hopper
Every method call returned a new value and left fullName alone, which is why line 8 still prints the full name. If the name had no space, indexOf would return -1 and line 3 would throw a StringIndexOutOfBoundsException, as in 1.15.8.
Common mistakes
The Watch Out boxes explain each mistake. This table is a quick lookup for every String method in this chapter: what it returns and when it fails.
| Method header | Returns | Exception or common slip |
|---|---|---|
int length() |
Number of characters | The last index is length() - 1, not length() |
String substring(int from, int to) |
Characters at from through to - 1 |
Throws if an index is negative or above length(), or if from is greater than to; the character at to is left out |
String substring(int from) |
Characters from from to the end |
Throws if from is below 0 or above length(); substring(length()) is "" |
int indexOf(String str) |
Index of first occurrence, or -1 | Case-sensitive; -1 passed to substring throws |
boolean equals(Object other) |
true if exactly the same characters |
Capitalization and spaces count |
int compareTo(String other) |
Negative, zero, or positive | Negative means the calling string comes first; uppercase letters come before lowercase |
+ and += with a String |
A new String | Left to right: "Total: " + 3 + 4 is "Total: 34"; no spaces added |
And for every method: the original string never changes. Store the result if you want to keep it.
Vocabulary
| Term | Definition | Example |
|---|---|---|
| String | An object that holds a sequence of characters | "Falcons" |
| sequence of characters | Characters (letters, digits, spaces, punctuation) in a fixed order | Number 23 is 9 characters |
| string literal | Text in double quotes; creates a String object | "Denver" |
| constructor | Called with new to create an object |
new String("Denver") |
| package / java.lang | A named group of classes / the package whose classes, including String, need no import | Scanner needs an import; String does not |
| concatenation | Joining strings end to end into a new String with + or += |
"Ada" + " " + "Lovelace" |
| implicit conversion | Java turning a primitive value into text on its own during concatenation | "Score: " + 95 |
| index (plural indices) | The position number of a character, starting at 0 | In "planet", p is at index 0 |
| length | The number of characters in a string, from length() |
"planet".length() is 6 |
| empty string | A String with no characters, length 0 | "" |
| substring | A part of a string; also the method that returns one | "sunflower".substring(0, 3) is "sun" |
| Java Quick Reference | The sheet of method headers available during the AP exam | Lists substring, indexOf, and others |
| StringIndexOutOfBoundsException | The run-time error thrown when a String method is given an index outside its allowed range | "planet".substring(0, 7) |
| immutable | Cannot be changed after it is created; String methods return new strings instead | word.substring(0, 3); leaves word unchanged |
| toString | The method every object has that returns a string representing the object | "Input: " + input calls input.toString() |
| method overriding | A subclass giving its own version of a superclass’s public method, with the same signature | Scanner overrides toString |
Chapter summary
- A String is an immutable object holding a sequence of characters. Create one with a literal or the
Stringconstructor;Stringis injava.lang, so no import is needed. +and+=concatenate into a new String. A primitive on the other side is converted to text; any other object is converted by calling itstoStringmethod. Java works left to right, so the order of the pieces matters.- Characters are numbered by index from 0 to
length() - 1. substring(from, to)returns indicesfromtoto - 1;substring(from)goes to the end;substring(i, i + 1)is the single character ati.indexOf(str)finds wherestrfirst starts, or returns -1.- An index outside the allowed range throws a StringIndexOutOfBoundsException while the program runs.
equalstells whether two strings have exactly the same characters;compareToreturns a negative, zero, or positive number to show alphabetical order.
Review questions
Question 13 · Multiple choice
What does this print?
System.out.println("A" + 2 * 3 + 1);A231A61A7A2*31
Check your reasoning
B. * has higher precedence than +, so 2 * 3 is computed first, giving the int 6. Then the + operators run left to right: "A" + 6 is "A6", and "A6" + 1 is "A61".
- A is wrong: it treats
*as if it ran after concatenation. Precedence from Topic 1.3 still applies inside a concatenation. - C is wrong: it adds 6 + 1 first, but the left
+runs first and already produces a String, so+ 1concatenates. - D is wrong:
*is an operator, not text; only characters inside quotes become part of the string.
Reread 1.15.3 Concatenating Numbers and Booleans if you chose differently.
Question 14 · Tracing
Write the exact output.
String date = "2026-10-07";
int dash = date.indexOf("-");
String year = date.substring(0, dash);
String rest = date.substring(dash + 1);
String month = rest.substring(0, rest.indexOf("-"));
System.out.println(year);
System.out.println(rest);
System.out.println(month);Check your reasoning
index: 0 1 2 3 4 5 6 7 8 9
character: 2 0 2 6 - 1 0 - 0 72026
10-07
10| Line | Value |
|---|---|
| 2 | dash = 4 (the first -) |
| 3 | year = indices 0–3 = "2026" |
| 4 | rest = from index 5 = "10-07" |
| 5 | rest.indexOf("-") is 2 (counting in rest, not date), so month = rest.substring(0, 2) = "10" |
The key step is line 5: indexOf counts positions in the string it is called on. In rest, the dash is at index 2. Also, year holds the characters 2026, not a number.
Reread 1.15.8 Finding Text with indexOf and 1.15.5 Extracting Text with substring if your output differed.
Question 15 · Debugging
This code should print Last letter: t, but it compiles and then throws a StringIndexOutOfBoundsException. Explain why and write the corrected line.
String word = "rocket";
String last = word.substring(word.length(), word.length() + 1);
System.out.println("Last letter: " + last);Check your reasoning
Why: word.length() is 6, so line 2 asks for substring(6, 7), the character at index 6. "rocket" has indices 0 to 5, so to = 7 is beyond the length and Java throws StringIndexOutOfBoundsException. Nothing is printed, because line 3 never runs.
Corrected line:
String last = word.substring(word.length() - 1, word.length());This is substring(5, 6), the single-character pattern for index 5, which is t. (word.substring(word.length() - 1) gives the same result.)
Reread 1.15.6 Out-of-Range Indices if you missed the error.
Question 16 · Multiple choice
What does this print?
String a = "sun";
String b = a;
a = a + "flower";
b.substring(1);
System.out.println(a + " " + b);sunflower sunflowersunflower unsunflower sunsun sun
Check your reasoning
C. After line 2, a and b refer to the same "sun" object. Line 3 creates a new string "sunflower" and moves only a to it; the "sun" object is immutable and unchanged, so b still holds sun. Line 4 builds "un" but does not store it, so b is still sun.
- A is wrong: it assumes that changing
achanges the shared object. Concatenation makes a new object instead. - B is wrong:
b.substring(1)returns"un"but does not changeb, and the result was not stored. - D is wrong: line 3 stores the new string in
a, soadoes change what it refers to.
Reread 1.15.7 Strings Are Immutable if you chose differently.
Question 17 · Tracing
This program compiles. Write everything it prints before it stops, and name the exception it throws and the line that throws it.
public class Login
{
public static void main(String[] args)
{
String entry = "maya.lee@school";
System.out.println("Checking " + entry);
int at = entry.indexOf("@");
System.out.println("Name: " + entry.substring(0, at));
int dot = entry.indexOf(".org");
System.out.println("Site: " + entry.substring(at + 1, dot));
System.out.println("Welcome!");
}
}Check your reasoning
Checking maya.lee@school
Name: maya.leeThen line 10 throws java.lang.StringIndexOutOfBoundsException, and Welcome! is never printed.
| Line | Value |
|---|---|
| 6 | Prints Checking maya.lee@school |
| 7 | at = 8 (maya.lee is 8 characters: indices 0–7) |
| 8 | substring(0, 8) = "maya.lee"; prints Name: maya.lee |
| 9 | .org does not appear, so dot = -1 |
| 10 | substring(9, -1): -1 is a negative index, and from is greater than to. Exception |
Note that the . in maya.lee does not match: the search is for the four characters .org together. The program compiled because the compiler checks types, not the values indexOf will return.
Reread 1.15.8 Finding Text with indexOf and 1.15.6 Out-of-Range Indices if your answer differed.
Question 18 · Tracing
Write the exact output. For the two compareTo lines, it is enough to say whether the value is negative, zero, or positive.
String first = "Mario";
String second = "Maria";
System.out.println(first.compareTo(second));
System.out.println(first.substring(0, 4).equals(second.substring(0, 4)));
System.out.println(second.equals("maria"));
System.out.println(second.compareTo(first));Check your reasoning
| Line | Expression | Result |
|---|---|---|
| 3 | "Mario".compareTo("Maria") |
Positive: the first difference is o vs a, and o comes after a |
| 4 | "Mari".equals("Mari") (both substrings are indices 0–3) |
true |
| 5 | "Maria".equals("maria") |
false: M and m are different characters |
| 6 | "Maria".compareTo("Mario") |
Negative: the same pair in the other order |
The exact output is:
14
true
false
-14The specific numbers (14 and -14) are not something you need to predict; their signs are. Swapping the two strings in compareTo flips the sign.
Reread 1.15.9 Comparing Strings with equals and compareTo if your answer differed.
Question 19 · Tracing
Write the exact output.
String code = "";
code += 4;
code += 2;
code = 1 + code;
System.out.println(code + 1);
System.out.println(code.length() + 1);Check your reasoning
1421
4| After line | code |
Why |
|---|---|---|
| 1 | "" |
The empty string, length 0 |
| 2 | "4" |
"" + 4 concatenates; the int 4 becomes the text 4 |
| 3 | "42" |
"4" + 2 concatenates |
| 4 | "142" |
1 + "42": one side is a String, so the int 1 becomes text and goes in front |
Line 5 prints code + 1, which concatenates: 1421. Line 6 is different: code.length() is the int 3, and 3 + 1 is two ints, so it adds: 4. Starting from the empty string is a common way to make sure += concatenates even when only numbers are added.
Reread 1.15.3 Concatenating Numbers and Booleans and 1.15.4 Indices and the length Method if your output differed.
Question 20 · Short answer
Look at this program.
import java.util.Scanner;
public class ReaderReport
{
public static void main(String[] args)
{
Scanner reader = new Scanner(System.in);
String report = "Reader: " + reader;
System.out.println(report.indexOf("Scanner"));
System.out.println(report.substring(0, 12));
}
}(a) It prints 18 and then Reader: java. Explain where the text java comes from, naming the method Java calls. (b) Explain why the program needs import java.util.Scanner; but no import for String.
Check your reasoning
Model answer:
(a) reader is a Scanner, not a String, so concatenating it with "Reader: " makes Java call reader.toString(). Scanner overrides toString to return a description that starts with its full class name, java.util.Scanner. "Reader: " is 8 characters (indices 0–7), and java.util. is 10 more, so Scanner starts at index 18; the first 12 characters are Reader: java.
(b) Scanner is in the java.util package, which must be imported. String is in java.lang, whose classes are available to every program by default.
A complete answer must:
- say that concatenation calls the object’s
toStringmethod, and that the text describes theScannerrather than anything typed; - say that
Stringis injava.lang, which needs no import, whileScanneris injava.util.
Reread 1.15.10 Concatenating Other Objects and 1.15.1 Creating String Objects if your answer differed.
Reflection
Check each statement. If the answer is “not yet,” reread the linked subsection.
- I can create a String with a literal or the constructor, and explain why String needs no import. Reread
- I can predict the result of joining strings with + and +=. Reread
- I can predict when + adds and when it concatenates, working left to right. Reread
- I can find the index of any character and the length of a string. Reread
- I can predict the result of substring with one or two arguments, including single characters. Reread
- I can tell which substring calls throw a StringIndexOutOfBoundsException. Reread
- I can explain why calling a method on a String never changes it. Reread
- I can predict what indexOf returns, including -1. Reread
- I can predict the result of equals and the sign of compareTo. Reread
- I can explain what happens when a String is concatenated with any object. Reread