10 Fundamental Classes

10.1 Overview of the java.lang Package

The java.lang package is indispensable when programming in Java. It is automatically imported into every source file at compile time. The package contains the Object class that is the superclass of all classes, and the wrapper classes (Boolean, Character, Byte, Short, Integer, Long, Float, Double) used to handle primitive values as objects. It provides classes essential for interacting with the JVM (Runtime), for security (SecurityManager), for loading classes (ClassLoader), for dealing with threads (Thread), and for exceptions (Throwable). The java.lang package also contains classes that provide the standard input, output, and error streams (System), string handling (String, StringBuilder, StringBuffer), and mathematical functions (Math).

Figure 10.1 shows the important classes that are discussed in detail in this chapter.

Figure 10.1 Partial Inheritance Hierarchy in the java.lang Package

Partial Inheritance Hierarchy in the java.lang Package

10.2 The Object Class

All classes extend the Object class, either directly or indirectly. A class declaration, without the extends clause, implicitly extends the Object class (see Section 7.1, p. 284). Thus, the Object class is always at the root of any inheritance hierarchy. The Object class defines the basic functionality that all objects exhibit and all classes inherit. Note that this also applies to arrays, since these are genuine objects in Java.

The Object class provides the following general utility methods (see Example 10.1 for usage of some of these methods):

int hashCode()

When storing objects in hash tables, this method can be used to get a hash value for an object. This value is guaranteed to be consistent during the execution of the program. This method returns the memory address of the object as the default hash value of the object. For a detailed discussion of the hashCode() method, see Section 15.1 on page 748.

boolean equals(Object obj)

Object reference and value equality are discussed together with the == and != operators (see Section 5.11, p. 191). The equals() method in the Object class returns true only if the two references compared denote the same object. The equals() method is usually overridden to provide the semantics of object value equality, as is the case for the wrapper classes and the String class. For a detailed discussion of the equals() method, see Section 15.1 on page 748.

final Class<?> getClass()

Returns the runtime class of the object, which is represented by an object of the class java.lang.Class at runtime.

protected Object clone() throws CloneNotSupportedException

New objects that are exactly the same (i.e., have identical states) as the current object can be created by using the clone() method, i.e., primitive values and reference values are copied. This is called shallow copying. A class can override this method to provide its own notion of cloning. For example, cloning a composite object by recursively cloning the constituent objects is called deep copying.

When overridden, the method in the subclass is usually declared public to allow any client to clone objects of the class. If the overriding clone() method in the subclass relies on the clone() method in the Object class (i.e., a shallow copy), the subclass must implement the Cloneable marker interface to indicate that its objects can be safely cloned. Otherwise, the clone() method in the Object class will throw a checked CloneNotSupportedException.

String toString()

If a subclass does not override this method, it returns a textual representation of the object, which has the following format:

<name of the class>@<hash code value of object>

Since the default hash value of an object is its memory address, this value is printed as a hexadecimal number, e.g., 3e25a5. This method is usually overridden and used for debugging purposes. The method call System.out.println(objRef) will implicitly convert its argument to a textual representation by calling the toString() method on the argument. See also the binary string concatenation operator +, discussed in Section 5.6 on page 180.

protected void finalize() throws Throwable

This method is discussed in connection with garbage collection (see Section 9.4, p. 396). It is called on an object just before it is garbage collected, so that any cleaning up can be done. However, the default finalize() method in the Object class does not do anything useful.

In addition, the Object class provides support for thread communication in synchronized code, through the following methods, which are discussed in Section 13.6 on page 634:

final void wait(long timeout) throws InterruptedException
final void wait(long timeout, int nanos) throws InterruptedException
final void wait() throws InterruptedException
final void notify()
final void notifyAll()

A thread invokes these methods on the object whose lock it holds. A thread waits for notification by another thread.

Example 10.1 Methods in the Object class

class MyClass implements Cloneable {

  public Object clone() {
    Object obj = null;
    try { obj = super.clone();}          // Calls overridden method.
    catch (CloneNotSupportedException e) { System.out.println(e);}
    return obj;
  }
}
//____________________________________________________
public class ObjectMethods {
  public static void main(String[] args) {
    // Two objects of MyClass.
    MyClass obj1 = new MyClass();
    MyClass obj2 = new MyClass();

    // Two strings.
    String str1 = new String("WhoAmI");
    String str2 = new String("WhoAmI");

    // Method hashCode() overridden in String class.
    // Strings that are equal have the same hash code.
    System.out.println("hash code for str1: " + str1.hashCode());
    System.out.println("hash code for str2: " + str2.hashCode() + " ");

    // Hash codes are different for different MyClass objects.
    System.out.println("hash code for MyClass obj1: " + obj1.hashCode());
    System.out.println("hash code for MyClass obj2: " + obj2.hashCode()+" ");

    // Method equals() overridden in the String class.
    System.out.println("str1.equals(str2): " + str1.equals(str2));
    System.out.println("str1 == str2:      " + (str1 == str2) + " ");

    // Method equals() from the Object class called.
    System.out.println("obj1.equals(obj2): " + obj1.equals(obj2));
    System.out.println("obj1 == obj2:      " + (obj1 == obj2) + " ");

    // The runtime object that represents the class of an object.
    Class rtStringClass  = str1.getClass();
    Class rtMyClassClass = obj1.getClass();
    // The name of the class represented by the runtime object.
    System.out.println("Class for str1: " + rtStringClass);
    System.out.println("Class for obj1: " + rtMyClassClass + " ");

    // The toString() method is overridden in the String class.
    String textRepStr = str1.toString();
    String textRepObj = obj1.toString();
    System.out.println("Text representation of str1: " + textRepStr);
    System.out.println("Text representation of obj1: " + textRepObj + " ");

    // Shallow copying of arrays.
    MyClass[] array1 = {new MyClass(), new MyClass(), new MyClass()};
    MyClass[] array2 = array1.clone();
    // Array objects are different, but share the element objects.
    System.out.println("array1 == array2:        " + (array1 == array2));
    for(int i = 0; i < array1.length; i++) {
      System.out.println("array1[" + i + "] == array2[" + i + "] : " +
                         (array1[i] == array2[i]));
    }
    System.out.println();

    // Clone an object of MyClass.
    MyClass obj3 = (MyClass) obj1.clone(); // Cast required.
    System.out.println("hash code for MyClass obj3: " + obj3.hashCode());
    System.out.println("obj1 == obj3: " + (obj1 == obj3));
  }
}

Output from the program:

hash code for str1: -1704812257
hash code for str2: -1704812257

hash code for MyClass obj1: 25669322
hash code for MyClass obj2: 14978587

str1.equals(str2): true
str1 == str2:      false

obj1.equals(obj2): false
obj1 == obj2:      false

Class for str1: class java.lang.String
Class for obj1: class MyClass

Text representation of str1: WhoAmI
Text representation of obj1: MyClass@187aeca

array1 == array2:        false
array1[0] == array2[0] : true
array1[1] == array2[1] : true
array1[2] == array2[2] : true

hash code for MyClass obj3: 19770577
obj1 == obj3: false

Review Questions

Review Questions

10.1 What is the return type of the hashCode() method in the Object class?

Select the one correct answer.

(a) String

(b) int

(c) long

(d) Object

(e) Class

10.2 Which statement is true?

Select the one correct answer.

(a) If the references x and y denote two different objects, the expression x.equals(y) is always false.

(b) If the references x and y denote two different objects, the expression (x.hashCode() == y.hashCode()) is always false.

(c) The hashCode() method in the Object class is declared final.

(d) The equals() method in the Object class is declared final.

(e) All arrays have a method named clone.

10.3 Which exception can the clone() method of the Object class throw?

Select the one correct answer.

(a) CloneNotSupportedException

(b) NotCloneableException

(c) IllegalCloneException

(d) NoClonesAllowedException

10.3 The Wrapper Classes

Wrapper classes were introduced with the discussion of the primitive data types (see Section 2.2, p. 28), and also in connection with boxing and unboxing of primitive values (see Section 5.1, p. 162). Primitive values in Java are not objects. In order to manipulate these values as objects, the java.lang package provides a wrapper class for each of the primitive data types. All wrapper classes are final. The objects of all wrapper classes that can be instantiated are immutable, i.e., the value in the wrapper object cannot be changed.

Although the Void class is considered a wrapper class, it does not wrap any primitive value and is not instantiable (i.e., has no public constructors). It just denotes the Class object representing the keyword void. The Void class will not be discussed further in this section.

In addition to the methods defined for constructing and manipulating objects of primitive values, the wrapper classes also define useful constants, fields, and conversion methods.

Common Wrapper Class Constructors

The Character class has only one public constructor, taking a char value as parameter. The other wrapper classes all have two public one-argument constructors: one takes a primitive value and the other takes a string.

WrapperType(type v)
WrapperType(String str)

The type is a primitive data type. The string argument is converted to a primitive value that corresponds to the WrapperType. Otherwise a NumberFormatException is thrown if the string is not parsable.

Wrapping Primitive Values in Objects

Boxing is a convenient way to wrap a primitive value in an object (see (1a) in Figure 10.2 and Section 5.1, p. 162).

Character charObj1   = ' ';
Boolean   boolObj1   = true;
Integer   intObj1    = 2008;
Double    doubleObj1 = 3.14;

A constructor that takes a primitive value can be used to create wrapper objects (see (1b) in Figure 10.2).

Character charObj1   = new Character(' '),
Boolean   boolObj1   = new Boolean(true);
Integer   intObj1    = new Integer(2008);
Double    doubleObj1 = new Double(3.14);

Figure 10.2 Converting Values Between Primitive, Wrapper, and String Types

image

We can also use the valueOf() method that takes the primitive value to wrap as an argument (see (1c) in Figure 10.2).

Character charObj1   = Character.valueOf(' '),
Boolean   boolObj1   = Boolean.valueOf(true);
Integer   intObj1    = Integer.valueOf(2008);
Double    doubleObj1 = Double.valueOf(3.14);

Converting Strings to Wrapper Objects

A constructor that takes a String object representing the primitive value can also be used to create wrapper objects. The constructors for the numeric wrapper types throw an unchecked NumberFormatException if the String parameter does not parse to a valid number (see (2a) in Figure 10.2).

Boolean boolObj2   = new Boolean("TrUe");       // case ignored: true
Boolean boolObj3   = new Boolean("XX");         // false
Integer intObj2    = new Integer("2008");
Double  doubleObj2 = new Double("3.14");
Long    longObj1   = new Long("3.14");          // NumberFormatException

Common Wrapper Class Utility Methods

Converting Strings to Wrapper Objects

Each wrapper class (except Character) defines the static method valueOf(String str) that returns the wrapper object corresponding to the primitive value represented by the String object passed as argument (see (6b) in Figure 10.2). This method for the numeric wrapper types also throws a NumberFormatException if the String parameter is not a valid number.

static WrapperType valueOf(String str)

Boolean boolObj4   = Boolean.valueOf("false");
Integer intObj3    = Integer.valueOf("1949");
Double  doubleObj3 = Double.valueOf("-3.0");

In addition to the one-argument valueOf() method, the integer wrapper classes define an overloaded static valueOf() method that can take a second argument. This argument specifies the base (or radix) in which to interpret the string representing the signed integer in the first argument:

static WrapperType valueOf(String str, int base)

Byte    byteObj1  = Byte.valueOf("1010", 2);   // Decimal value 10
Short   shortObj2 = Short.valueOf("012", 8);   // Not "12". Decimal value 10.
Integer intObj4   = Integer.valueOf("-a", 16); // Not "-0xa". Decimal value -10.
Long    longObj2  = Long.valueOf("-a", 16);    // Not "-0xa". Decimal value -10L.

Converting Wrapper Objects to Strings

Each wrapper class overrides the toString() method from the Object class. The overriding method returns a String object containing the string representation of the primitive value in the wrapper object (see (3) in Figure 10.2).

String toString()

String charStr   = charObj1.toString();     // " "
String boolStr   = boolObj2.toString();     // "true"
String intStr    = intObj1.toString();      // "2008"
String doubleStr = doubleObj1.toString();   // "3.14"

Converting Primitive Values to Strings

Each wrapper class defines a static method toString(type v) that returns the string corresponding to the primitive value of type, passed as argument (see (6a) in Figure 10.2).

static String toString(type v)

String charStr2   = Character.toString(' '),  // " "
String boolStr2   = Boolean.toString(true);    // "true"
String intStr2    = Integer.toString(2008);    // Base 10. "2008"
String doubleStr2 = Double.toString(3.14);     // "3.14"

For integer primitive types, the base is assumed to be 10. For floating-point numbers, the textual representation (decimal form or scientific notation) depends on the sign and the magnitude (absolute value) of the number. The NaN value, positive infinity, and negative infinity will result in the strings "NaN", "Infinity", and "-Infinity", respectively.

In addition, the wrapper classes Integer and Long define overloaded toString() methods for converting integers to string representation in decimal, binary, octal, and hexadecimal notation (see p. 435).

Converting Wrapper Objects to Primitive Values

Unboxing is a convenient way to unwrap the primitive value in a wrapper object (see (4a) in Figure 10.2 and Section 5.1, p. 162).

char    c = charObj1;           // ' '
boolean b = boolObj2;           // true
int     i = intObj1;            // 2008
double  d = doubleObj1;         // 3.14

Each wrapper class defines a typeValue() method which returns the primitive value in the wrapper object (see (4b) in Figure 10.2).

type type Value()

char    c = charObj1.charValue();           // ' '
boolean b = boolObj2.booleanValue();        // true
int     i = intObj1.intValue();             // 2008
double  d = doubleObj1.doubleValue();       // 3.14

In addition, each numeric wrapper class defines typeValue() methods for converting the primitive value in the wrapper object to a value of any numeric primitive data type. These methods are discussed below.

Wrapper Comparison, Equality, and Hashcode

Each wrapper class also implements the Comparable<Type> interface (see Section 15.1, p. 765), which defines the following method:

int compareTo(Type obj2)

This method returns a value which is less than, equal to, or greater than zero, depending on whether the primitive value in the current wrapper Type object is less than, equal to, or greater than the primitive value in the wrapper Type object denoted by argument obj2.

// Comparisons based on objects created above
Character charObj2   = 'a';
int result1 = charObj1.compareTo(charObj2);      //  < 0
int result2 = intObj1.compareTo(intObj3);        //  > 0
int result3 = doubleObj1.compareTo(doubleObj2);  // == 0
int result4 = doubleObj1.compareTo(intObj1);     // ClassCastException

Each wrapper class overrides the equals() method from the Object class (see Section 15.1, p. 751). The overriding method compares two wrapper objects for object value equality.

boolean equals(Object obj2)

// Comparisons based on objects created above
boolean charTest   = charObj1.equals(charObj2);      // false
boolean boolTest   = boolObj2.equals(Boolean.FALSE); // false
boolean intTest    = intObj1.equals(intObj2);        // true
boolean doubleTest = doubleObj1.equals(doubleObj2);  // true

The following values are interned when they are wrapped during boxing, i.e., only one wrapper object exists in the program for these primitive values when boxing is applied:

boolean values true or false,

• a byte,

• a char in the range u0000 to u007f,

• an int or short value in the range -128 and 127

If references w1 and w2 refer to two wrapper objects that box the same value which is among the ones mentioned above, then w1 == w2 is always true. In other words, for the values listed above, object equality and reference equality give the same result.

// Reference and object equality
Byte bRef1 = (byte)10;
Byte bRef2 = (byte)10;
System.out.println(bRef1 == bRef2);              // true
System.out.println(bRef1.equals(bRef2));         // true

Integer iRef1 = 1000;
Integer iRef2 = 1000;
System.out.println(iRef1 == iRef2);              // false
System.out.println(iRef1.equals(iRef2));         // true

Each wrapper class also overrides the hashCode() method in the Object class (see Section 15.1, p. 760). The overriding method returns a hash value based on the primitive value in the wrapper object.

int hashCode()

int index = charObj1.hashCode();

Numeric Wrapper Classes

The numeric wrapper classes Byte, Short, Integer, Long, Float, and Double are all subclasses of the abstract class Number (see Figure 10.1).

Each numeric wrapper class defines an assortment of constants, including the minimum and maximum value of the corresponding primitive data type:

NumericWrapperType.MIN_VALUE
NumericWrapperType.MAX_VALUE

The following code retrieves the minimum and maximum values of various numeric types:

byte   minByte   = Byte.MIN_VALUE;     // -128
int    maxInt    = Integer.MAX_VALUE;  // 2147483647
double maxDouble = Double.MAX_VALUE;   // 1.7976931348623157e+308

Converting Numeric Wrapper Objects to Numeric Primitive Types

Each numeric wrapper class defines the following set of typeValue() methods for converting the primitive value in the wrapper object to a value of any numeric primitive type:

byte   byteValue()
short  shortValue()
int    intValue()
long   longValue()
float  floatValue()
double doubleValue()

See also (4b) in Figure 10.2.

The following code shows conversion of values in numeric wrapper objects to any numeric primitive type.

Byte    byteObj2   = new Byte((byte) 16);       // Cast mandatory
Integer intObj5    = new Integer(42030);
Double  doubleObj4 = new Double(Math.PI);

short  shortVal  = intObj5.shortValue();        // (1)
long   longVal   = byteObj2.longValue();
int    intVal    = doubleObj4.intValue();       // (2) Truncation
double doubleVal = intObj5.doubleValue();

Notice the potential for loss of information at (1) and (2) above, when the primitive value in a wrapper object is converted to a narrower primitive data type.

Converting Strings to Numeric Values

Each numeric wrapper class defines a static method parseType(String str), which returns the primitive numeric value represented by the String object passed as argument. The Type in the method name parseType stands for the name of a numeric wrapper class, except for the name of the Integer class which is abbreviated to Int. These methods throw a NumberFormatException if the String parameter is not a valid argument (see (5) in Figure 10.2.)

type parseType(String str)

byte   value1 = Byte.parseByte("16");
int    value2 = Integer.parseInt("2010");       // parseInt, not parseInteger.
int    value3 = Integer.parseInt("7UP");        // NumberFormatException
double value4 = Double.parseDouble("3.14");

For the integer wrapper types, the overloaded static method parseType() can additionally take a second argument, which can specify the base in which to interpret the string representing the signed integer in the first argument:

type parseType(String str, int base)

byte  value6 = Byte.parseByte("1010", 2);    // Decimal value 10
short value7 = Short.parseShort("012", 8);   // Not "12". Decimal value 10.
int   value8 = Integer.parseInt("-a", 16);   // Not "-0xa". Decimal value -10.
long  value9 = Long.parseLong("-a", 16);     // Not "-0xa". Decimal value -10L.

Converting Integer Values to Strings in Different Notations

The wrapper classes Integer and Long provide static methods for converting integers to string representation in decimal, binary, octal, and hexadecimal notation. Some of these methods from the Integer class are listed here, but analogous methods are also defined in the Long class. Example 10.2 demonstrates use of these methods.

static String toBinaryString(int i)
static String toHexString(int i)
static String toOctalString(int i)

These three methods return a string representation of the integer argument as an unsigned integer in base 2, 16, and 8, respectively, with no extra leading zeroes.

static String toString(int i, int base)
static String toString(int i)

The first method returns the minus sign '-' as the first character if the integer i is negative. In all cases, it returns the string representation of the magnitude of the integer i in the specified base.

The last method is equivalent to the method toString(int i, int base), where the base has the value 10, that returns the string representation as a signed decimal (see also (6a) in Figure 10.2).

Example 10.2 String Representation of Integers

public class IntegerRepresentation {
  public static void main(String[] args) {
    int positiveInt = +41;    // 051, 0x29
    int negativeInt = -41;    // 037777777727, -051, 0xffffffd7, -0x29

    System.out.println("String representation for decimal value: " + positiveInt);
    integerStringRepresentation(positiveInt);
    System.out.println("String representation for decimal value: " + negativeInt);
    integerStringRepresentation(negativeInt);
  }

  public static void integerStringRepresentation(int i) {
    System.out.println("    Binary: " + Integer.toBinaryString(i));
    System.out.println("    Octal: "  + Integer.toOctalString(i));
    System.out.println("    Hex: "    + Integer.toHexString(i));
    System.out.println("    Decimal: "  + Integer.toString(i));

    System.out.println("    Using toString(int i, int base) method:");
    System.out.println("    Base 2: " + Integer.toString(i, 2));
    System.out.println("    Base 8: " + Integer.toString(i, 8));
    System.out.println("    Base 16: "  + Integer.toString(i, 16));
    System.out.println("    Base 10: "  + Integer.toString(i, 10));
  }
}

Output from the program:

String representation for decimal value: 41
    Binary:101001
    Octal:51
    Hex:29
    Decimal:41
    Using toString(int i, int base) method:
    Base 2:101001
    Base 8:51
    Base 16:29
    Base 10:41
String representation for decimal value: -41
    Binary:11111111111111111111111111010111
    Octal:37777777727
    Hex:ffffffd7
    Decimal:-41
    Using toString(int i, int base) method:
    Base 2:-101001
    Base 8:-51
    Base 16:-29
    Base 10:-41

The Character Class

The Character class defines a myriad of constants, including the following which represent the minimum and the maximum value of the char type (see Section 2.2, p. 29):

Character.MIN_VALUE
Character.MAX_VALUE

The Character class also defines a plethora of static methods for handling various attributes of a character, and case issues relating to characters, as defined by the Unicode standard, version 4.0:

static int     getNumericValue(char ch)
static boolean isLowerCase(char ch)
static boolean isUpperCase(char ch)
static boolean isTitleCase(char ch)
static boolean isDigit(char ch)
static boolean isLetter(char ch)
static boolean isLetterOrDigit(char ch)
static char    toUpperCase(char ch)
static char    toLowerCase(char ch)
static char    toTitleCase(char ch)

The following code converts a lowercase character to an uppercase character:

char ch = 'a';
if (Character.isLowerCase(ch)) ch = Character.toUpperCase(ch);

The Boolean Class

The Boolean class defines the following wrapper objects to represent the primitive values true and false, respectively:

Boolean.TRUE
Boolean.FALSE

Review Questions

Review Questions

10.4 Which of the following are wrapper classes?

Select the three correct answers.

(a) java.lang.Void

(b) java.lang.Int

(c) java.lang.Boolean

(d) java.lang.Long

(e) java.lang.String

10.5 Which of the following classes do not extend the java.lang.Number class?

Select the two correct answers.

(a) java.lang.Float

(b) java.lang.Byte

(c) java.lang.Character

(d) java.lang.Boolean

(e) java.lang.Short

10.6 Which of these classes define immutable objects?

Select the three correct answers.

(a) Character

(b) Byte

(c) Number

(d) Short

(e) Object

10.7 Which of these classes have a one-parameter constructor taking a string?

Select the two correct answers.

(a) Void

(b) Integer

(c) Boolean

(d) Character

(e) Object

10.8 Which of the wrapper classes have a booleanValue() method?

Select the one correct answer.

(a) All wrapper classes.

(b) All wrapper classes except Void.

(c) All wrapper classes that also implement the compareTo() method.

(d) All wrapper classes extending Number.

(e) Only the class Boolean.

10.9 Which statements are true about wrapper classes?

Select the two correct answers.

(a) String is a wrapper class.

(b) Double has a compareTo() method.

(c) Character has a intValue() method.

(d) Byte extends Number.

10.10 What will the program print when compiled and run?

public class RQ200_60 {
  public static void main(String[] args) {
    Integer i = -10;
    Integer j = -10;
    System.out.print(i==j);
    System.out.print(i.equals(j));
    Integer n = 128;
    Integer m = 128;
    System.out.print(n==m);
    System.out.print(n.equals(m));
  }
}

Select the one correct answer.

(a) falsetruefalsetrue

(b) truetruetruetrue

(c) falsetruetruetrue

(d) truetruefalsetrue

(e) None of the above.

10.11 What will the program print when compiled and run?

public class RQ200_70 {
  public static void main(String[] args) {
    Integer i = new Integer(-10);
    Integer j = new Integer(-10);
    Integer k = -10;
    System.out.print(i==j);
    System.out.print(i.equals(j));
    System.out.print(i==k);
    System.out.print(i.equals(k));
  }
}

Select the one correct answer.

(a) falsetruefalsetrue

(b) truetruetruetrue

(c) falsetruetruetrue

(d) truetruefalsetrue

(e) None of the above.

10.4 The String Class

Handling character sequences is supported through three final classes: String, StringBuilder and StringBuffer. The Java 2 platform uses the variable-length UTF-16 encoding to store characters in char arrays and in the string handling classes. The UTF-16 encoding allows characters whose Unicode values are in the range 0000 to 10FFFF. The char type only represents Unicode values in the range 0000 to FFFF, i.e. characters that can be represented in a single 16-bit word. This means that the supplementary characters are represented by multiple char values, i.e. multiple 16-bit words, when these are stored in a string or a char array. The string handling classes provide methods to handle the full range of characters in the UTF-16 encoding, but we will not dwell on the subject in this book.

Immutability

The String class implements immutable character strings, which are read-only once the string has been created and initialized, whereas the StringBuilder class implements dynamic character strings. The StringBuffer class is a thread-safe version of the StringBuilder class.

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

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 strLength = "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. Here we present a few selected constructors:

Note that using a constructor creates a brand new String object, i.e., 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 builders:

byte[] bytes = {97, 98, 98, 97};

String()

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

String(String str)

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

String(char[] value)
String(char[] value, int offset, int count)

These constructors create a new String object whose contents are copied from a char array. The second constructor allows extraction of a certain number of characters (count) from a given offset in the array.

String(StringBuilder builder)
String(StringBuffer buffer)

These constructors allow interoperability with the StringBuilder and the StringBuffer class, respectively.

char[] characters = {'a', 'b', 'b', 'a'};
StringBuilder strBuilder = new StringBuilder("abba");
//...
String byteStr  = new String(bytes);      // Using array of bytes: "abba"
String charStr  = new String(characters); // Using array of chars: "abba"
String buildStr = new String(strBuilder); // Using string builder: "abba"

In Example 10.3, note that the reference str1 does not denote the same String object as the 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

The CharSequence Interface

This interface is implemented by all three classes: String, StringBuilder and StringBuffer. Many methods in these classes accept arguments of this interface type, and specify it as their return type. This facilitates interoperability between these classes. This interface defines the following methods:

char charAt(int index)

A character at a particular index in a sequence 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, an IndexOutOfBoundsException is thrown.

int length()

This method returns the number of char values in this sequence.

CharSequence subSequence(int start, int end)

This method returns a new CharSequence that is a subsequence of this sequence. Characters from the current sequence are read from index start to the index end-1, inclusive.

String toString()

This method returns a string containing the characters in this sequence in the same order as this sequence.

Reading Characters from a String

char charAt(int index)

This method is defined in the CharSequence interface which the String class implements (p. 442).

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 (dst), 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 is defined in the CharSequence interface which the String class implements (p. 442).

boolean isEmpty()

This method returns true if the length of the string is 0, otherwise false.

Example 10.4 uses some of 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.

We leave it as an exercise for the reader to implement a solution for character frequencies using a Map (see Section 15.8, p. 821).

Comparing Strings

Characters are compared based on their Unicode 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. See also The Comparator<E> Interface, p. 771.

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)

The String class implements the Comparable<String> interface. The 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

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 (see Section 12.1, p. 532).

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.toUpperCase();  // 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 5.6 on page 180. 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(" advertise").concat(" here.");  // (3) Chaining.
System.out.println(billboard);       // (4) "Just advertise here."

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 advertise". 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 advertise here.". 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 advertise" is inevitable at (3).

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

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 = "" + 2008;  // String conversion: "2008" <---- 2008

Application of the concatenation operator may result in string conversion being performed on one of the operands, as shown above. The string concatenation operator (+) is discussed in Section 5.7, p. 185. 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)
int indexOf(int ch, int fromIndex)

The first method finds the index of the first occurrence of the argument character in a string. The second method 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; resulting in the value -1 being returned.

int indexOf(String str)
int indexOf(String str, int fromIndex)

The first method finds the start index of the first occurrence of the substring argument in a string. The second method 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 backwards 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 methods can be used to create a string in which all occurrences of a character or a subsequence in a string have been replaced with another character or subsequence:

String replace(char oldChar, char newChar)
String replace(CharSequence target, CharSequence replacement)

The first method returns a new String object that is a result of replacing all occurrences of the oldChar in the current string with the newChar. The current string is returned if no occurrences of the oldChar can be found.

The second method returns a new String object that is a result of replacing all occurrences of the character sequence target in the current string with the character sequence replacement. The current string is returned if no occurrences of the target can be found.

boolean contains(CharSequence cs)

This method returns true if the current string contains the specified character sequence, otherwise false.

Examples of search methods:

String funStr = "Java Jives";
//               0123456789

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

String newStr = funStr.replace('J', 'W'),         // "Wava Wives"
String newBanner = banner.replace("One", "No");   // "No man, No vote"
boolean found1 = banner.contains("One");          // true
boolean found2 = newBanner.contains("One");       // false

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, i.e., 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 = "   Java Nation    ";
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()

This method is defined in the CharSequence interface which the String class implements (p. 442).

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[] charArray)
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"

Formatting Values

The String class provides support for formatted text representation of primitive values and objects through its overloaded format() methods. Formatting of values is covered extensively in Section 12.7, p. 593.

static String format(String format, Object... args)
static String format(Locale l, String format, Object... args)

These methods return a string that is a result of applying the specified format string to the values in the vararg array args.

Pattern Matching

Methods for string pattern matching take an argument that specifies a regular expression (see Section 12.6, p. 554).

Matching

The following method attempts to match the current string against the specified regular expression. The call

str.matches(regexStr);

is equivalent to the call

Pattern.matches(regexStr, str);

See The java.util.regex.Pattern Class, p. 562, for details.

boolean matches(String regexStr)

The method only returns true if the entire string matches the regular expression.

Matching and Replacing

The following methods can be used to replace substrings that match a given regular expression. The call

str.replaceFirst(regexStr, replacement);

is equivalent to the call

Pattern.compile(regexStr).matcher(str).replaceFirst(replacement);

See The java.util.regex.Matcher Class, p. 570, for details.

String replaceFirst(String regexStr, String replacement)
String replaceAll(String regexStr, String replacement)

The first method returns a new String by replacing the first substring of this string that matches the given regular expression with the given replacement.

The second method returns a new String by replacing each substring of this string that matches the given regular expression with the given replacement.

Splitting

The split() method can be called on a string to create an array by splitting the string according to a regular expression pattern (see Section 12.6, p. 554). Given that the reference input is of type String, the call

input.split(regexStr,limit);

is equivalent to the call

Pattern.compile(regexStr).split(input, limit);

Splitting is covered in The java.util.regex.Pattern Class, p. 563.

String[] split(String regexStr)
String[] split(String regexStr, int limit)

The method splits the current string around matches of the specified pattern. The limit determines how many times the pattern will be applied to the string to create the array.

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.

Review Questions

Review Questions

10.12 Which of the following operators cannot have an operand of type String?

Select the two correct answers.

(a) +

(b) -

(c) +=

(d) .

(e) &

10.13 Which expression will extract the substring "kap", given the following declaration:

String str = "kakapo";

Select the one correct answer.

(a) str.substring(2, 2)

(b) str.substring(2, 3)

(c) str.substring(2, 4)

(d) str.substring(2, 5)

(e) str.substring(3, 3)

10.14 What will be the result of attempting to compile and run the following code?

class MyClass {
  public static void main(String[] args) {
    String str1 = "str1";
    String str2 = "str2";
    String str3 = "str3";

    str1.concat(str2);
    System.out.println(str3.concat(str1));
  }
}

Select the one correct answer.

(a) The code will fail to compile because the expression str3.concat(str1) will not result in a valid argument for the println() method.

(b) The program will print str3str1str2, when run.

(c) The program will print str3, when run.

(d) The program will print str3str1, when run.

(e) The program will print str3str2, when run.

10.15 Which statement about the trim() method of the String class is true?

Select the one correct answer.

(a) It returns a string where the leading white space of the original string has been removed.

(b) It returns a string where the trailing white space of the original string has been removed.

(c) It returns a string where both the leading and trailing white space of the original string has been removed.

(d) It returns a string where all the white space of the original string has been removed.

(e) None of the above.

10.16 Which statements are true?

Select the two correct answers.

(a) String objects are immutable.

(b) Subclasses of the String class can be mutable.

(c) All wrapper classes are declared final.

(d) All objects have a public method named clone.

(e) The expression ((new StringBuilder()) instanceof String) is always true.

10.17 Which of these expressions are legal?

Select the four correct answers.

(a) "co".concat("ol")

(b) ("co" + "ol")

(c) ('c' + 'o' + 'o' + 'l')

(d) ("co" + new String('o' + 'l'))

(e) ("co" + new String("co"))

10.18 What will be the result of attempting to compile and run the following program?

public class RefEq {
  public static void main(String[] args) {
    String s = "ab" + "12";
    String t = "ab" + 12;
    String u = new String("ab12");
    System.out.println((s==t) + " " + (s==u));
  }
}

Select the one correct answer.

(a) The program will fail to compile.

(b) The program will print false false, when run.

(c) The program will print false true, when run.

(d) The program will print true false, when run.

(e) The program will print true true, when run.

10.19 Which of these parameter lists can be found in a constructor of the String class?

Select the three correct answers.

(a) ()

(b) (int capacity)

(c) (char[] data)

(d) (String str)

10.20 Which method is not defined in the String class?

Select the one correct answer.

(a) trim()

(b) length()

(c) concat(String)

(d) hashCode()

(e) reverse()

10.21 Which statement about the charAt() method of the String class is true?

Select the one correct answer.

(a) The charAt() method takes a char value as an argument.

(b) The charAt() method returns a Character object.

(c) The expression ("abcdef").charAt(3) is illegal.

(d) The expression "abcdef".charAt(3) evaluates to the character 'd'.

(e) The index of the first character is 1.

10.22 Which expression will evaluate to true?

Select the one correct answer.

(a) "hello: there!".equals("hello there")

(b) "HELLO THERE".equals("hello there")

(c) ("hello".concat("there")).equals("hello there")

(d) "Hello There".compareTo("hello there") == 0

(e) "Hello there".toLowerCase().equals("hello there")

10.23 What will the following program print when run?

public class Search {
  public static void main(String[] args) {
    String s = "Contentment!";
    int middle = s.length()/2;

    String nt = s.substring(middle-1, middle+1);
    System.out.println(s.lastIndexOf(nt, middle));
  }
}

Select the one correct answer.

(a) 2

(b) 4

(c) 5

(d) 7

(e) 9

(f) 11

10.24 What will the following program print when run?

public class Uppity {
  public static void main(String[] args) {
    String str1 = "lower", str2 = "LOWER", str3 = "UPPER";
    str1.toUpperCase();
    str1.replace("LOWER","UPPER");
    System.out.println((str1.equals(str2)) + " " + (str1.equals(str3)));
  }
}

Select the one correct answer.

(a) The program will print false true.

(b) The program will print false false.

(c) The program will print true false.

(d) The program will print true true.

(e) The program will fail to compile.

(f) The program will compile, but throw an exception at runtime.

10.25 What will the following program print when run?

public class FunCharSeq {
  private static void putO(String s1) {
    s1 = s1.trim();
    s1 += "O";
  }

  public static void main(String[] args) {
    String s1 = " W ";
    putO(s1);
    s1.concat("W");
    System.out.println("|" + s1 + "|");
  }
}

Select the one correct answer.

(a) |WOW|

(b) | WW |

(c) | WO |

(d) | W |

(e) The program will fail to compile.

(f) The program will compile, but throw an exception at runtime.

10.5 The StringBuilder and the StringBuffer Classes

Thread-Safety

The classes StringBuilder and StringBuffer implement mutable sequences of characters. Both classes support the same operations. However, the StringBuffer class is the thread-safe analog of the StringBuilder class. Certain operations on a string buffer are synchronized, so that when used by multiple threads, these operations are performed in an orderly way (see Section 13.5, p. 626). Note that a String object is also thread-safe—because it is immutable, a thread cannot change its state. String builders are preferred when heavy modification of character sequences is involved and synchronization of operations is not important.

Although the rest of this section is about string builders, it is equally applicable to string buffers.

Mutability

In contrast to the String class, which implements immutable character sequences, the StringBuilder class implements mutable character sequences. Not only can the character sequences in a string builder be changed, but the capacity of the string builder can also change dynamically. The capacity of a string builder is the maximum number of characters that a string builder can accommodate before its size is automatically augmented.

Although there is a close relationship between objects of the String and StringBuilder classes, these are two independent final classes, both directly extending the Object class. Hence, String references cannot be stored (or cast) to StringBuilder references, and vice versa.

The StringBuilder class provides various facilities for manipulating string builders:

Constructing String Builders

The final class StringBuilder provides four constructors that create and initialize StringBuilder objects and set their initial capacity.

StringBuilder(String str)
StringBuilder(CharSequence charSeq)

The contents of the new StringBuilder object are the same as the contents of the String object or the character sequence passed as argument. The initial capacity of the string builder is set to the length of the argument sequence, plus room for 16 more characters.

StringBuilder(int length)

The new StringBuilder object has no content. The initial capacity of the string builder is set to the value of the argument length, which cannot be less than 0.

StringBuilder()

This constructor also creates a new StringBuilder object with no content. The initial capacity of the string builder is set for 16 characters.

Examples of StringBuilder object creation and initialization:

StringBuilder strBuilder1 = new StringBuilder("Phew!");  // "Phew!", capacity 21
StringBuilder strBuilder2 = new StringBuilder(10);       // "", capacity 10
StringBuilder strBuilder3 = new StringBuilder();         // "", capacity 16

Reading and Changing Characters in String Builders

int length()                                      From the CharSequence interface (p. 442).

Returns the number of characters in the string builder.

char charAt(int index)                               From the CharSequence interface (p. 442).
void setCharAt(int index, char ch)

These methods read and change the character at a specified index in the string builder, respectively. The first character is at index 0 and the last one at index one less than the number of characters in the string builder. A StringIndexOutOfBoundsException is thrown if the index is not valid.

CharSequence subSequence(int start, int end)

This method is implemented as part of the CharSequence interface (p. 442).

The following is an example of reading and changing string builder contents:

StringBuilder strBuilder = new StringBuilder("Javv");      // "Javv", capacity 20
strBuilder.setCharAt(strBuilder.length()-1, strBuilder.charAt(1)); // "Java"

Constructing Strings from String Builders

The StringBuilder class overrides the toString() method from the Object class (see also the CharSequence interface, p. 442). It returns the contents of a string builder in a String object.

String fromBuilder = strBuilder.toString();                                         // "Java"

Differences between the String and StringBuilder Classes

Since the StringBuilder class does not override the equals() method from the Object class, nor does it implement the Comparable interface, the contents of string builders should be converted to String objects for string comparison.

The StringBuilder class also does not override the hashCode() method from the Object class. Again, a string builder can be converted to a String object in order to obtain a hash value.

Appending, Inserting, and Deleting Characters in String Builders

Appending, inserting, and deleting characters automatically results in adjustment of the string builder’s capacity, if necessary. The indices passed as arguments in the methods must be equal to or greater than 0. A StringIndexOutOfBoundsException is thrown if an index is not valid.

Note that the methods in this subsection return the reference value of the modified stringbuilder, making it convenient to chain calls to these methods.

Appending Characters to a String Builder

The overloaded method append() can be used to append characters at the end of a string builder.

StringBuilder append(Object obj)

The obj argument is converted to a string as if by the static method call String.valueOf(obj), and this string is appended to the current string builder.

StringBuilder append(String str)
StringBuilder append(CharSequence charSeq)
StringBuilder append(CharSequence charSeq, int start, int end)
StringBuilder append(char[] charArray
StringBuilder append(char[] charArray, int offset, int length)
StringBuilder append(char c)

These methods allow characters from various sources to be appended to the end of the current string builder.

StringBuilder append(boolean b)
StringBuilder append(int i)
StringBuilder append(long l)
StringBuilder append(float f)
StringBuilder append(double d)

These methods convert the primitive value of the argument to a string by applying the static method String.valueOf() to the argument, before appending the result to the string builder.

Inserting Characters in a String Builder

The overloaded method insert() can be used to insert characters at a given position in a string builder.

StringBuilder insert(int offset, Object obj)
StringBuilder insert(int dstOffset, CharSequence seq)
StringBuilder insert(int dstOffset, CharSequence seq, int start, int end)
StringBuilder insert(int offset, String str)
StringBuilder insert(int offset, char[] charArray)
StringBuilder insert(int offset, char c)
StringBuilder insert(int offset, boolean b)
StringBuilder insert(int offset, int i)
StringBuilder insert(int offset, long l)
StringBuilder insert(int offset, float f)
StringBuilder insert(int offset, double d)

The argument is converted, if necessary, by applying the static method String.valueOf(). The offset argument specifies where the characters are to be inserted and must be greater than or equal to 0.

Deleting Characters in a String Builder

The following methods can be used to delete characters from specific positions in a string builder:

StringBuilder deleteCharAt(int index)
StringBuilder delete(int start, int end)

The first method deletes a character at a specified index in the string builder, contracting the string builder by one character. The second method deletes a substring, which is specified by the start index (inclusive) and the end index (exclusive).

Among other miscellaneous methods included in the class StringBuilder is the following method, which reverses the contents of a string builder:

StringBuilder reverse()

Examples of appending, inserting, and deleting in string builders:

StringBuilder builder = new StringBuilder("banana split");   // "banana split"
builder.delete(4,12);                                        // "bana"
builder.append(42);                                          // "bana42"
builder.insert(4,"na");                                      // "banana42"
builder.reverse();                                           // "24ananab"
builder.deleteCharAt(builder.length()-1);                    // "24anana"
builder.append('s'),                                         // "24ananas"

All the previous methods modify the contents of the string builder and also return a reference value denoting the string builder. This allows chaining of method calls. The method calls invoked on the string builder denoted by the reference builder can be chained as follows, giving the same result:

builder.delete(4,12).append(42).insert(4,"na").reverse().
       deleteCharAt(builder.length()-1).append('s'),         // "24ananas"

The method calls in the chain are evaluated from left to right, so that the previous chain of calls is interpreted as follows:

(((((builder.delete(4,12)).append(42)).insert(4,"na")).reverse()).
     deleteCharAt(builder.length()-1)).append('s'),          // "24ananas"

Each method call returns the reference value of the modified string builder. This value is used to invoke the next method. The string builder remains denoted by the reference builder.

The compiler uses string builders to implement the string concatenation, +. The following example code of string concatenation

String str1 = 4 + "U" + "Only";                                    // (1) "4UOnly"

is equivalent to the following code using one string builder:

String str2 = new StringBuilder().
                  append(4).append("U").append("Only").toString(); // (2)

The code at (2) does not create any temporary String objects when concatenating several strings, since a single StringBuilder object is modified and finally converted to a String object.

Controlling String Builder Capacity

int capacity()

Returns the current capacity of the string builder, i.e., the number of characters the current builder can accommodate without allocating a new, larger array to hold characters.

void ensureCapacity(int minCapacity)

Ensures that there is room for at least a minCapacity number of characters. It expands the string builder, depending on the current capacity of the builder.

void trimToSize()

Attempts to reduce the storage used for the character sequence. It may affect the capacity of the string builder.

void setLength(int newLength)

This method ensures that the actual number of characters, i.e., the length of the string builder, is exactly equal to the value of the newLength argument, which must be greater than or equal to 0. This operation can result in the string being truncated or padded with null characters ('u0000').

This method only affects the capacity of the string builder if the value of the parameter newLength is greater than current capacity.

One use of this method is to clear the string builder:

builder.setLength(0);      // Empty the builder.

Review Questions

Review Questions

10.26 What will be the result of attempting to compile and run the following program?

public class MyClass {
  public static void main(String[] args) {
    String s = "hello";
    StringBuilder sb = new StringBuilder(s);
    sb.reverse();
    if (s == sb) System.out.println("a");
    if (s.equals(sb)) System.out.println("b");
    if (sb.equals(s)) System.out.println("c");
  }
}

Select the one correct answer.

(a) The code will fail to compile because the constructor of the String class is not called properly.

(b) The code will fail to compile because the expression (s == sb) is illegal.

(c) The code will fail to compile because the expression (s.equals(sb)) is illegal.

(d) The program will print c, when run.

(e) The program will throw a ClassCastException, when run.

10.27 What will be the result of attempting to compile and run the following program?

public class MyClass {
  public static void main(String[] args) {
    StringBuilder sb = new StringBuilder("have a nice day");
    sb.setLength(6);
    System.out.println(sb);
  }
}

Select the one correct answer.

(a) The code will fail to compile because there is no method named setLength in the StringBuilder class.

(b) The code will fail to compile because the StringBuilder reference sb is not a legal argument to the println() method.

(c) The program will throw a StringIndexOutOfBoundsException, when run.

(d) The program will print have a nice day, when run.

(e) The program will print have a, when run.

(f) The program will print ce day, when run.

10.28 Which of these parameter lists can be found in a constructor of the StringBuilder class?

Select the three correct answers.

(a) ()

(b) (int capacity)

(c) (char[] data)

(d) (String str)

10.29 Which method is not defined in the StringBuilder class?

Select the one correct answer.

(a) trim()

(b) length()

(c) append(String)

(d) reverse()

(e) setLength(int)

10.30 What will be the result of attempting to compile and run the following program?

public class StringMethods {
  public static void main(String[] args) {
    String str = new String("eeny");
    str.concat(" meeny");
    StringBuilder strBuilder = new StringBuilder(" miny");
    strBuilder.append(" mo");
    System.out.println(str + strBuilder);
  }
}

Select the one correct answer.

(a) The program will fail to compile.

(b) The program will print eeny meeny miny mo, when run.

(c) The program will print meeny miny mo, when run.

(d) The program will print eeny miny mo, when run.

(e) The program will print eeny meeny miny, when run.

10.31 What will the following program print when run?

public class PeskyCharSeq {
  public static void main (String[] args) {
    StringBuilder sb1 = new StringBuilder("WOW");
    StringBuilder sb2 = new StringBuilder(sb1);
    System.out.println((sb1==sb2) + " " + sb1.equals(sb2));
  }
}

Select the one correct answer.

(a) The program will print false true.

(b) The program will print false false.

(c) The program will print true false.

(d) The program will print true true.

(e) The program will fail to compile.

(f) The program will compile, but throws an exception at runtime.

10.32 What will the following program print when run?

public class MoreCharSeq {
  public static void main (String[] args) {
    String s1 = "WOW";
    StringBuilder s2 = new StringBuilder(s1);
    String s3 = new String(s2);
    System.out.println((s1.hashCode() == s2.hashCode()) + " " +
                       (s1.hashCode() == s3.hashCode()));
  }
}

Select the one correct answer.

(a) The program will print false true.

(b) The program will print false false.

(c) The program will print true false.

(d) The program will print true true.

(e) The program will fail to compile.

(f) The program will compile, but throw an exception at runtime.

10.33 What will the following program print when run?

public class CharSeq {
  public static void main (String[] args) {
    String cs1 = "JAVA";
    StringBuilder cs2 = new StringBuilder(cs1);
    System.out.println(cs1.compareTo(cs2) == cs2.compareTo(cs1));
  }
}

Select the one correct answer.

(a) The program will print false.

(b) The program will print true.

(c) The program will fail to compile.

(d) The program will compile, but throw an exception at runtime.

10.34 What will the following program print when run?

public class Appendage {
  private static void putO(StringBuilder s1) {
    s1 = s1.append("O");
  }

  public static void main(String[] args) {
    StringBuilder s1 = new StringBuilder("W");
    putO(s1);
    s1.append("W!");
    System.out.println(s1);
  }
}

Select the one correct answer.

(a) The program will print WW!.

(b) The program will print WOW!.

(c) The program will print W.

(d) The program will fail to compile.

(e) The program will compile, but throw an exception at runtime.

10.35 What will the following program print when run?

public class Chains {
  private static StringBuilder putO(StringBuilder s1) {
    s1.append("O");
    return s1;
  }

  public static void main(String[] args) {
    StringBuilder s1 = new StringBuilder("W");
    boolean status = putO(s1).append("W!").toString().compareTo("WOW!") != 0;
    System.out.println(s1 + " " + status);
  }
}

Select the one correct answer.

(a) The program will print WOW! false.

(b) The program will print WOW! true.

(c) The program will print WW! true.

(d) The program will fail to compile.

(e) The program will compile, but throw an exception at runtime.

Chapter Summary

Chapter Summary

The following information was included in this chapter:

• discussion of the Object class, which is the most fundamental class in Java

• discussion of the wrapper classes, which not only allow primitive values to be treated as objects, but also contain useful methods for converting values

• discussion of the String class, showing how immutable strings are created and used

• discussion of the StringBuilder class, showing how dynamic strings are created and manipulated

• comparison of the String, StringBuilder, and StringBuffer classes

Programming Exercises

Programming Exercises

10.1 Create a class named Pair, which aggregates two arbitrary objects. Implement the equals() and hashCode() methods in such a way that a Pair object is identical to another Pair object if, and only if, the pair of constituent objects are identical. Make the toString() implementation return the textual representation of both the constituent objects in a Pair object. Objects of the Pair class should be immutable.

10.2 A palindrome is a text phrase that is spelled the same backward and forward. The word redivider is a palindrome, since the word would be spelled the same even if the character sequence were reversed. Write a program that takes a string as an argument and reports whether the string is a palindrome.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.89.183