Strings are objects
The String class is part of the java.lang package, so it is available by default. A String object represents a sequence of characters. You can create one in two ways:
- with a string literal:
String s = "hello"; - by calling the
Stringclass constructor:String t = new String("hello");
Both give you a reference to a String object (Topic 1.12). Literals are far more common, so for the rest of the guide you will see few new String(...) calls.
Strings cannot be changed
A String object is immutable: once it has been created, its attributes cannot be changed. Methods called on a String object do not change the content of the String object. They produce a new value. This single fact explains most beginner errors with strings.
public class Immutable {
public static void main(String[] args) {
String word = "cat";
word.substring(1);
System.out.println(word);
word = word.substring(1);
System.out.println(word);
}
}
cat
at
The first call to substring computed "at" and threw it away, because nothing stored it. word still refers to the original "cat". The second call stored the returned reference back in word, so now word refers to a different String, "at". The original "cat" object was not changed; the variable simply refers elsewhere.
Joining strings with + and +=
Two String objects can be concatenated (joined) with the + or += operator, resulting in a new String object. A primitive value can be concatenated with a String, and the primitive value is implicitly converted to a String. A String can also be concatenated with any object, which implicitly calls that object’s toString method (a method that every class has, because every class is a subclass of Object).
Operators of equal precedence go from left to right, and this matters a great deal when + is sometimes addition and sometimes joining:
public class Concat {
public static void main(String[] args) {
System.out.println("a" + 1 + 2);
System.out.println(1 + 2 + "a");
System.out.println("a" + (1 + 2));
int n = 5;
String msg = "n = " + n;
msg += "!";
System.out.println(msg);
System.out.println("x" + 2.5 + true);
}
}
a12
3a
a3
n = 5!
x2.5true
Read each line left to right, one + at a time:
"a" + 1 + 2:"a" + 1is theString"a1", and"a1" + 2is"a12".1 + 2 + "a":1 + 2is theint3, and3 + "a"is"3a"."a" + (1 + 2): the bracket is worked out first, so it is"a" + 3, which is"a3".
The statement msg += "!"; is the compound assignment of Topic 1.6: it creates a new String and stores the reference in msg.
Indices and the String methods
Each character has a position called an index, and the first character is at index 0. A String object has index values from 0 to one less than the length of the string. Attempting to access an index outside this range results in a StringIndexOutOfBoundsException.
The Java Quick Reference lists these String methods. All are instance methods, called on a String object (Topic 1.14).
| Method | Returns | What it does |
|---|---|---|
int length() |
int |
the number of characters |
String substring(int from, int to) |
String |
the characters beginning at index from and ending at index to - 1 |
String substring(int from) |
String |
the same as substring(from, length()): everything from from to the end |
int indexOf(String str) |
int |
the index of the first occurrence of str; -1 if not found |
boolean equals(Object other) |
boolean |
true if this string has the same sequence of characters as other |
int compareTo(String other) |
int |
below 0 if this string is before other alphabetically, 0 if equal, above 0 if after |
public class Methods {
public static void main(String[] args) {
String a = "microscope";
System.out.println(a.length());
System.out.println(a.substring(2, 6));
System.out.println(a.substring(6));
System.out.println(a.indexOf("sco"));
System.out.println(a.indexOf("z"));
System.out.println(a.substring(0, 1));
System.out.println(a.substring(a.length() - 1));
System.out.println("[" + a.substring(10) + "]" + "[" + a.substring(3, 3) + "]");
}
}
10
cros
cope
5
-1
m
e
[][]
Study each line against Figure 1.
length()is 10, so the last index is 9.substring(2, 6)takes indices 2, 3, 4 and 5:cros. Thefromindex is included and thetoindex is excluded.substring(6)takes everything from index 6 to the end:cope.indexOf("sco")finds the first place the three lettersscobegin, at index 5. Not found gives-1.- To get a single character (a one-letter
String), usesubstring(index, index + 1).substring(0, 1)is the first letter andsubstring(a.length() - 1)is the last. - Two edge cases that are legal:
substring(10)where 10 is the length gives the empty string"", andsubstring(3, 3)also gives"".
Out-of-range indices
Using substring with an index outside 0 to length() for from or to, or with from greater than to, throws a StringIndexOutOfBoundsException. For "abc" (length 3): substring(2, 10) fails because 10 is beyond the end; substring(3, 2) fails because from is greater than to; substring(4) fails because 4 is past the end. By contrast substring(3) is legal and gives "".
Comparing strings
public class Compare {
public static void main(String[] args) {
String p = "hi";
String q = new String("hi");
System.out.println(p.equals(q));
System.out.println("hello".equals("Hello"));
System.out.println("apple".compareTo("banana"));
System.out.println("banana".compareTo("apple"));
System.out.println("cat".compareTo("cat"));
}
}
true
false
-1
1
0
equalsasks whether two strings have exactly the same characters."hello"and"Hello"differ in the first letter’s case, so the answer isfalse.compareToorders strings alphabetically. The documentation guarantees only the sign: a value less than 0 if the object is less than the other, zero if equal and greater than 0 if greater. The actual numbers printed above (-1 and 1) are not something to rely on. ("cat".compareTo("catalog")is also negative, because a shorter string that matches the start of a longer one comes first.)- The
==operator on two object references asks something different: whether both variables refer to the same object. Herepandqare different objects with the same text, sop == qisfalseeven thoughp.equals(q)istrue. You will study==in Unit 2; for now, compare the contents of strings withequals.
A routine for string questions
String questions reward a steady routine more than clever tricks. Whatever you are asked, the same five steps keep you out of trouble.
- Write the string with its indices. Put each character in a box with its index underneath, as in Figure 1. Do not count in your head. A single missed space is the commonest source of wrong answers.
- Mark the start and end. For
substring(from, to)circle the character atfromand the character just beforeto. Those, and everything between them, are the answer. Forsubstring(from)the end is the last character. - Find positions with
indexOfrather than by counting, whenever the question allows it. A program that usesindexOf(" ")works for any name; a program that hard-codes 5 works for one. - Remember immutability. If an expression’s result is not stored or printed, it is lost. If a variable is reassigned, draw the new string in the table and keep the old one only if another variable still refers to it.
- Check the legal range before you finish:
fromandtomust satisfy0 <= from <= to <= length(). If they do not, the answer is aStringIndexOutOfBoundsException, and nothing after that line runs.
Two habits help with concatenation. When a line mixes + for numbers and + for text, rewrite it with brackets showing the order Java will really use: (("a" + 1) + 2). And when you need a single character, write it as substring(i, i + 1), never as an index on its own: the Java Quick Reference gives no way to read one character directly, and the one-letter String is exactly what the course expects.
Finally, remember what strings are not. They are not arrays of boxes you can edit; you cannot put a new letter at index 3. To “change” a letter you build a new string from the parts before and after it: s.substring(0, 3) + "x" + s.substring(4). That sentence contains most of what Topic 1.15 teaches.
Worked example 1: Reformat a name
Task. The variable full holds "Grace Hopper". Write code that displays Hopper, G. Show the index arithmetic.
Step 1, find the space. full.indexOf(" ") is 5, because G0 r1 a2 c3 e4 then the space at index 5.
Step 2, take the parts. The first name is full.substring(0, 5), which is "Grace". The last name starts just after the space, at index 6, so it is full.substring(5 + 1), which is "Hopper".
Step 3, build the result. The first initial is first.substring(0, 1), which is "G". Join the pieces: last + ", " + initial + ".".
public class Reformat {
public static void main(String[] args) {
String full = "Grace Hopper";
int sp = full.indexOf(" ");
String first = full.substring(0, sp);
String last = full.substring(sp + 1);
System.out.println(sp + " " + first + " " + last);
System.out.println(last + ", " + first.substring(0, 1) + ".");
}
}
5 Grace Hopper
Hopper, G.
Step 4, check. Every index is inside 0 to 12 (the length of the string is 12): substring(0, 5) and substring(6) are legal. The program works for any two-word name, because it finds the space rather than assuming where it is.
Worked example 2: Trace strings that never change
Task. Trace this code and give the output.
String s = "abcdef";
String t = s.substring(2, 4);
s = t + s.substring(4);
String u = s.substring(1, 3) + s.indexOf("f");
System.out.println(s + " " + t + " " + u + " " + u.length());
Step 1. s refers to "abcdef" (a0 b1 c2 d3 e4 f5). t = s.substring(2, 4) takes indices 2 and 3: "cd".
Step 2. s.substring(4) is "ef". t + "ef" is "cdef", and the assignment makes s refer to this new string. The old "abcdef" is not changed; s simply refers to something else.
Step 3. Now s is "cdef" (c0 d1 e2 f3). s.substring(1, 3) is "de", and s.indexOf("f") is 3. Joining a String and an int gives "de3".
Step 4. u.length() is 3.
| After statement | s |
t |
u |
|---|---|---|---|
String s = "abcdef"; |
"abcdef" |
- | - |
String t = s.substring(2, 4); |
"abcdef" |
"cd" |
- |
s = t + s.substring(4); |
"cdef" |
"cd" |
- |
String u = s.substring(1, 3) + s.indexOf("f"); |
"cdef" |
"cd" |
"de3" |
The output is cdef cd de3 3.
Common misconceptions
- “
word.substring(1);changesword.” Strings are immutable; the result must be stored. - “
substring(from, to)includes the character atto.” It stops atto - 1. - “The first character is at index 1.” It is at index 0; the last is at
length() - 1. - “The expression
"a" + 1 + 2givesa3.” It givesa12: the+operators go left to right. - “
indexOfreturns 0 when the text is not found.” It returns-1; 0 means “found at the start”. - “
==compares the text of two strings.” Useequals. - “
compareToreturns exactly -1, 0 or 1.” Only the sign is guaranteed. - “
substring(length())is an error.” It is legal and gives the empty string. - “Calling a method on a string variable that holds
nullis fine.” It throws aNullPointerException.
Where this leads
This is the last topic of Unit 1. Practise with the Topic 1.15 practice questions, recap with the revision notes and tick off the checklist. Then take the Unit 1 diagnostic and the Unit 1 mixed review. Unit 2 builds on strings with selection and iteration, so that you can process every character of a string, not just those you pick out by index.
Quick questions
Why does word.substring(1); not change word?
A String is immutable: once created, its characters cannot be changed. substring returns a new String and leaves the original untouched. To keep the result you store it: word = word.substring(1);.
What is the difference between substring(a, b) and substring(a)?
substring(a, b) returns the characters from index a up to, but not including, index b. substring(a) returns everything from index a to the end, which is the same as substring(a, length()).
Why use equals instead of == to compare strings?
equals compares the sequences of characters. The == operator asks whether two variables refer to the same object, so two different String objects with identical text can give false. Compare the content of strings with equals.
What does "a" + 1 + 2 print?
The letter a followed by 1 and then 2, run together as text. The + operators are evaluated left to right. The first one joins a String and an int, producing the text a1; the second joins that text with 2. By contrast 1 + 2 + "a" gives 3a, because 1 + 2 is added first.