10.5 The 'String' Class

Handling character strings is supported through two final classes: String and StringBuffer. The String class implements immutable character strings, which are read-only once the string has been created and initialized, whereas the StringBuffer class implements dynamic character strings.

Character strings implemented using these classes are genuine objects, and the characters in such a string are represented using 16-bit characters (see Section 2.1, p. 23).

This section discusses the class String that provides facilities for creating, initializing, and manipulating character strings. The next section discusses the StringBuffer class.

Creating and Initializing Strings

String Literals Revisited

The easiest way of creating a String object is using a string literal:

String str1 = "You cannot change me!";

A string literal is a reference to a String object. The value in the String object is the character sequence enclosed in the double quotes of the string literal. Since a string literal is a reference, it can be manipulated like any other String reference. The reference value of a string literal can be assigned to another String reference: the reference str1 will denote the String object with the value "You cannot change me!" after the assignment above. A string literal can be used to invoke methods on its String object:

int len = "You cannot change me!".length(); // 21

The compiler optimizes handling of string literals (and compile-time constant expressions that evaluate to strings): only one String object is shared by all string-valued constant expressions with the same character sequence. Such strings are said to be interned, meaning that they share a unique String object if they have the same content. The String class maintains a private pool where such strings are interned.

String str2 = "You cannot change me!";

Both String references str1 and str2 denote the same String object, initialized with the character string: "You cannot change me!". So does the reference str3 in the following code. The compile-time evaluation of the constant expression involving the two string literals, results in a string that is already interned:

String str3 = "You cannot" + " change me!"; // Compile-time constant expression

In the following code, both the references can1 and can2 denote the same String object that contains the string "7Up":

String can1 = 7 + "Up";  // Value of compile-time constant expression: "7Up"
String can2 = "7Up";     // "7Up"

However, in the code below, the reference can4 will denote a new String object that will have the value "7Up" at runtime:

String word = "Up";
String can4 = 7 + word;  // Not a compile-time constant expression.

The sharing of String objects between string-valued constant expressions poses no problem, since the String objects are immutable. Any operation performed on one String reference will never have any effect on the usage of other references denoting the same object. The String class is also declared final, so that no subclass can override this behavior.

String Constructors

The String class has numerous constructors to create and initialize String objects based on various types of arguments. The following shows two of them:

String(String s)

This constructor creates a new String object, whose contents are the same as those of the String object passed as argument.

String()

This constructor creates a new String object, whose content is the empty string, "".


Note that using a constructor creates a brand new String object, that is, using a constructor does not intern the string. A reference to an interned string can be obtained by calling the intern() method in the String class?in practice, there is usually no reason to do so.

In the following code, the String object denoted by str4 is different from the String object passed as argument:

String str4 = new String("You cannot change me!");

Constructing String objects can also be done from arrays of bytes, arrays of characters, or string buffers:

byte[] bytes = {97, 98, 98, 97};
char[] characters = {'a', 'b', 'b', 'a'};
StringBuffer strBuf = new StringBuffer("abba");
//...
String byteStr = new String(bytes);      // Using array of bytes: "abba"
String charStr = new String(character);  // Using array of chars: "abba"
String buffStr = new String(strBuf);     // Using string buffer: "abba"

In Example 10.3, note that the reference str1 does not denote the same String object as references str4 and str5. Using the new operator with a String constructor always creates a new String object. The expression "You cannot" + words is not a constant expression and, therefore, results in a new String object. The local references str2 and str3 in the main() method and the static reference str1 in the Auxiliary class all denote the same interned string. Object value equality is hardly surprising between these references. It might be tempting to use the operator == for object value equality of string literals, but this is not advisable.

Example 10.3 String Construction and Equality
public class StringConstruction {

    static String str1 = "You cannot change me!";            // Interned

    public static void main(String[] args) {
        String emptyStr = new String();                      // ""
        System.out.println("emptyStr: " + emptyStr);

        String str2 = "You cannot change me!";               // Interned
        String str3 = "You cannot" + " change me!";          // Interned
        String str4 = new String("You cannot change me!");   // New String object

        String words = " change me!";
        String str5 = "You cannot" + words;                  // New String object

        System.out.println("str1 == str2:                " +
                           (str1 == str2));                  // (1) true
        System.out.println("str1.equals(str2):           " +
                           str1.equals(str2));               // (2) true

        System.out.println("str1 == str3:                " +
                           (str1 == str3));                  // (3) true
        System.out.println("str1.equals(str3):           " +
                           str1.equals(str3));               // (4) true

        System.out.println("str1 == str4:                " +
                           (str1 == str4));                  // (5) false
        System.out.println("str1.equals(str4):           " +
                           str1.equals(str4));               // (6) true

        System.out.println("str1 == str5:                " +
                           (str1 == str5));                  // (7) false
        System.out.println("str1.equals(str5):           " +
                           str1.equals(str5));               // (8) true
        System.out.println("str1 == Auxiliary.str1:      " +
                           (str1 == Auxiliary.str1));        // (9) true
        System.out.println("str1.equals(Auxiliary.str1): " +
                           str1.equals(Auxiliary.str1));     // (10) true

        System.out.println("\"You cannot change me!\".length(): " +
                           "You cannot change me!".length());// (11) 21
    }
}

class Auxiliary {
    static String str1 = "You cannot change me!";            // Interned
}

Output from the program:

emptyStr:
str1 == str2:                true
str1.equals(str2):           true
str1 == str3:                true
str1.equals(str3):           true
str1 == str4:                false
str1.equals(str4):           true
str1 == str5:                false
str1.equals(str5):           true
str1 == Auxiliary.str1:      true
str1.equals(Auxiliary.str1): true
"You cannot change me!".length(): 21

Reading Characters from a String

char charAt(int index)

A character at a particular index in a string can be read using the charAt() method. The first character is at index 0 and the last one at index one less than the number of characters in the string. If the index value is not valid, a StringIndexOutOfBoundsException is thrown.

void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)

This method copies characters from the current string into the destination character array. Characters from the current string are read from index srcBegin to the index srcEnd-1, inclusive. They are copied into the destination array, starting at index dstBegin and ending at index dstbegin+(srcEnd-srcBegin)-1. The number of characters copied is (srcEnd-srcBegin). An IndexOutOfBoundsException is thrown if the indices do not meet the criteria for the operation.

int length()

This method returns the number of characters in a string.


Example 10.4 uses these methods at (3), (4), (5), and (6). The program prints the frequency of a character in a string and illustrates copying from a string into a character array.

Example 10.4 Reading Characters from a String
public class ReadingCharsFromString {
    public static void main(String[] args) {
        int[] frequencyData = new int [Character.MAX_VALUE];// (1)
        String str = "You cannot change me!";               // (2)

        // Count the frequency of each character in the string.
        for (int i = 0; i < str.length(); i++)              // (3)
            try {
                frequencyData[str.charAt(i)]++;             // (4)
            } catch(StringIndexOutOfBoundsException e) {
                System.out.println("Index error detected: "+ i +" not in range.");
            }
        // Print the character frequency.
        System.out.println("Character frequency for string: \"" + str + "\"");
        for (int i = 0; i < frequencyData.length; i++)
            if (frequencyData[i] != 0)
                System.out.println((char)i + " (code "+ i +"): " +
                                   frequencyData[i]);

        System.out.println("Copying into a char array:");
        char[] destination = new char [str.length()];
        str.getChars( 0,            7, destination, 0);     // (5) "You can"
        str.getChars(10, str.length(), destination, 7);     // (6) " change me!"
        // Print the character array.
        for (int i = 0; i < 7 + (str.length() - 10); i++)
            System.out.print(destination[i]);
        System.out.println();
    }
}

Output from the program:

Character Frequency for string: "You cannot change me!"
  (code 32): 3
! (code 33): 1
Y (code 89): 1
a (code 97): 2
c (code 99): 2
e (code 101): 2
g (code 103): 1
h (code 104): 1
m (code 109): 1
n (code 110): 3
o (code 111): 2
t (code 116): 1
u (code 117): 1
Copying into a char array:
You can change me!

In Example 10.4, the frequencyData array at (1) stores the frequency of each character that can occur in a string. The string in question is declared at (2). Since a char value is promoted to an int value in arithmetic expressions, it can be used as an index in an array. Each element in the frequencyData array functions as a frequency counter for the character corresponding to the index value of the element:

frequencyData[str.charAt(i)]++;                 // (4)

The calls to the getChars() method at (5) and (6) copy particular substrings from the string into designated places in the destination array, before printing the whole character array.

Comparing Strings

Characters are compared based on their integer values.

boolean test = 'a' < 'b';    // true since 0x61 < 0x62

Two strings are compared lexicographically, as in a dictionary or telephone directory, by successively comparing their corresponding characters at each position in the two strings, starting with the characters in the first position. The string "abba" is less than "aha", since the second character 'b' in the string "abba" is less than the second character 'h' in the string "aha". The characters in the first position in each of these strings are equal.

The following public methods can be used for comparing strings:

boolean equals(Object obj)
boolean equalsIgnoreCase(String str2)

The String class overrides the equals() method from the Object class. The String class equals() method implements String object value equality as two String objects having the same sequence of characters. The equalsIgnoreCase() method does the same, but ignores the case of the characters.

int compareTo(String str2)
int compareTo(Object obj)

The first compareTo() method compares the two strings and returns a value based on the outcome of the comparison:

  • the value 0, if this string is equal to the string argument

  • a value less than 0, if this string is lexicographically less than the string argument

  • a value greater than 0, if this string is lexicographically greater than the string argument

The second compareTo() method (required by the Comparable interface) behaves like the first method if the argument obj is actually a String object; otherwise, it throws a ClassCastException.


Here are some examples of string comparisons:

String strA = new String("The Case was thrown out of Court");
String strB = new String("the case was thrown out of court");

boolean b1 = strA.equals(strB);             // false
boolean b2 = strA.equalsIgnoreCase(strB);   // true

String str1 = new String("abba");
String str2 = new String("aha");

int compVal1 = str1.compareTo(str2);         // negative value => str1 < str2

Character Case in a String

String toUpperCase()
String toUpperCase(Locale locale)

String toLowerCase()
String toLowerCase(Locale locale)

Note that the original string is returned if none of the characters need their case changed, but a new String object is returned if any of the characters need their case changed. These methods delegate the character-by-character case conversion to corresponding methods from the Character class.

These methods use the rules of the (default) locale (returned by the method Locale.getDefault()), which embodies the idiosyncrasies of a specific geographical, political, or cultural region regarding number/date/currency formats, character classification, alphabet (including case idiosyncrasies), and other localizations.


Example of case in strings:

String strA = new String("The Case was thrown out of Court");
String strB = new String("the case was thrown out of court");

String strC = strA.toLowerCase();  // Case conversion => New String object:
                                   // "the case was thrown out of court"
String strD = strB.toLowerCase();  // No case conversion => Same String object
String strE = strA.toUppperCase(); // Case conversion => New String object:
                                   // "THE CASE WAS THROWN OUT OF COURT"

boolean test1 = strC == strA; // false
boolean test2 = strD == strB; // true
boolean test3 = strE == strA; // false

Concatenation of Strings

Concatenation of two strings results in a string that consists of the characters of the first string followed by the characters of the second string. The overloaded operator + for string concatenation is discussed in Section 3.6 on page 62. In addition, the following method can be used to concatenate two strings:

String concat(String str)


The concat() method does not modify the String object on which it is invoked, as String objects are immutable. Instead the concat() method returns a reference to a brand new String object:

String billboard = "Just";
billboard.concat(" lost in space.");    // (1) Returned reference value not stored.
System.out.println(billboard);          // (2) "Just"
billboard = billboard.concat(" grooving").concat(" in heap.");  // (3) Chaining.
System.out.println(billboard);          // (4) "Just grooving in heap."

At (1), the reference value of the String object returned by the method concat() is not stored. This String object becomes inaccessible after (1). We see that the reference billboard still denotes the string literal "Just" at (2).

At (3), two method calls to the concat() method are chained. The first call returns a reference value to a new String object whose content is "Just grooving". The second method call is invoked on this String object using the reference value that was returned in the first method call. The second call results in yet another String object whose content is "Just grooving in heap." The reference value of this String object is assigned to the reference billboard. Because String objects are immutable, the creation of the temporary String object with the content "Just grooving" is inevitable at (3).

The compiler uses a string buffer to avoid this overhead of temporary String objects when applying the string concatenation operator (p. 424).

A simple way to convert any primitive value to its string representation is by concatenating it with the empty string (""), using the string concatenation operator (+) (see also (6c) in Figure 10.2):

String strRepresentation = "" + 2003;  // "2003"

Some more examples of string concatenation follow:

String motto = new String("Program once");     // (1)
motto += ", execute everywhere.";              // (2)
motto = motto.concat(" Don't bet on it!");     // (3)

Note that a new String object is assigned to the reference motto each time in the assignment at (1), (2), and (3). The String object with the contents "Program once" becomes inaccessible after the assignment at (2). The String object with the contents "Program once, execute everywhere." becomes inaccessible after (3). The reference motto denotes the String object with the following contents after execution of the assignment at (3):

"Program once, execute everywhere. Don't bet on it!"

Searching for Characters and Substrings

The following overloaded methods can be used to find the index of a character, or the start index of a substring in a string. These methods search forward toward the end of the string. In other words, the index of the first occurrence of the character or substring is found. If the search is unsuccessful, the value ?1 is returned.

int indexOf(int ch)

Finds the index of the first occurrence of the argument character in a string.

int indexOf(int ch, int fromIndex)

Finds the index of the first occurrence of the argument character in a string, starting at the index specified in the second argument. If the index argument is negative, the index is assumed to be 0. If the index argument is greater than the length of the string, it is effectively considered to be equal to the length of the string?returning the value -1.

int indexOf(String str)

Finds the start index of the first occurrence of the substring argument in a string.

int indexOf(String str, int fromIndex)

Finds the start index of the first occurrence of the substring argument in a string, starting at the index specified in the second argument.


The String class also defines a set of methods that search for a character or a substring, but the search is backward toward the start of the string. In other words, the index of the last occurrence of the character or substring is found.

int lastIndexOf(int ch)
int lastIndexOf(int ch, int fromIndex)
int lastIndexOf(String str)
int lastIndexOf(String str, int fromIndex)


The following method can be used to create a string in which all occurrences of a character in a string have been replaced with another character:

String replace(char oldChar, char newChar)


Examples of search methods:

String funStr = "Java Jives";
//               0123456789

String newStr = funStr.replace('J', 'W');     // "Wava Wives"

int jInd1a = funStr.indexOf('J');             // 0
int jInd1b = funStr.indexOf('J', 1);          // 5
int jInd2a = funStr.lastIndexOf('J');         // 5
int jInd2b = funStr.lastIndexOf('J', 4);      // 0

String banner = "One man, One vote";
//               01234567890123456

int subInd1a = banner.indexOf("One");         // 0
int subInd1b = banner.indexOf("One", 3);      // 9
int subInd2a = banner.lastIndexOf("One");     // 9
int subInd2b = banner.lastIndexOf("One", 10); // 9
int subInd2c = banner.lastIndexOf("One", 8);  // 0
int subInd2d = banner.lastIndexOf("One", 2);  // 0

Extracting Substrings

String trim()

This method can be used to create a string where white space (in fact all characters with values less than or equal to the space character '\u0020') from the front (leading) and the end (trailing) of a string has been removed.

String substring(int startIndex)
String substring(int startIndex, int endIndex)

The String class provides these overloaded methods to extract substrings from a string. A new String object containing the substring is created and returned. The first method extracts the string that starts at the given index startIndex and extends to the end of the string. The end of the substring can be specified by using a second argument endIndex that is the index of the first character after the substring, that is, the last character in the substring is at index endIndex-1. If the index value is not valid, a StringIndexOutOfBoundsException is thrown.


Examples of extracting substrings:

String utopia = "\t\n  Java Nation \n\t  ";
utopia = utopia.trim();                          // "Java Nation"
utopia = utopia.substring(5);                    // "Nation"
String radioactive = utopia.substring(3,6);      // "ion"

Converting Primitive Values and Objects to Strings

The String class overrides the toString() method in the Object class and returns the String object itself:

String toString()


The String class also defines a set of static overloaded valueOf() methods to convert objects and primitive values into strings.

static String valueOf(Object obj)
static String valueOf(char[] character)
static String valueOf(boolean b)
static String valueOf(char c)

All these methods return a string representing the given parameter value. A call to the method with the parameter obj is equivalent to obj.toString(). The boolean values true and false are converted into the strings "true" and "false". The char parameter is converted to a string consisting of a single character.

static String valueOf(int i)
static String valueOf(long l)
static String valueOf(float f)
static String valueOf(double d)

The static valueOf() method that accepts a primitive value as argument is equivalent to the static toString() method in the corresponding wrapper class for each of the primitive data types (see also (6a) and (6b) in Figure 10.2 on p. 393).

Note that there are no valueOf() methods that accept a byte or a short.


Examples of string conversions:

String anonStr   = String.valueOf("Make me a string.");     // "Make me a string."
String charStr   = String.valueOf(new char[] {'a', 'h', 'a'});// "aha"
String boolTrue  = String.valueOf(true);                      // "true"
String doubleStr = String.valueOf(Math.PI);                 // "3.141592653589793"

Other miscellaneous methods exist for reading the string characters into an array of characters (toCharArray()), converting the string into an array of bytes (getBytes()), and searching for prefixes (startsWith()) and suffixes (endsWith()) of the string. The method hashCode() can be used to compute a hash value based on the characters in the string.