Appendix D Annotated Answers to Review Questions

1 Basics of Java Programming

1.1 (d)

A method is an operation defining the behavior for a particular abstraction. Java implements abstractions using classes that have properties and behavior. Behavior is defined by the operations of the abstraction.

1.2 (b)

An object is an instance of a class. Objects are created from classes that implement abstractions. The objects that are created are concrete realizations of those abstractions. An object is neither a reference nor a variable.

1.3 (b)

(2) is the first line of a constructor declaration. A constructor in Java is declared like a method, except that the name is identical to the class name, and it does not specify a return value. (1) is a header of a class declaration, and (3), (4), and (5) are instance method declarations.

1.4 (b) and (f)

Two objects and three reference variables are created by the code. Objects are normally created by using the new operator. The declaration of a reference variable creates a variable regardless of whether a reference value is assigned to it or not.

1.5 (d)

An instance member is a field or an instance method. These members belong to an instance of the class rather than the class as a whole. Members which are not explicitly declared static in a class declaration are instance members.

1.6 (c)

An object communicates with another object by calling an instance method of the other object.

1.7 (d) and (f)

Given the declaration class B extends A {...}, we can conclude that class B extends class A, class A is the superclass of class B, class B is a subclass of class A, and class B inherits from class A, which means that objects of class B will inherit the field value1 from class A.

1.8 (d)

The compiler supplied with the JDK is named javac. The names of the source files to be compiled are listed on the command line after the command javac.

1.9 (a)

Java programs are executed by the Java Virtual Machine (JVM). In the JDK, the command java is used to start the execution by the JVM. The java command requires the name of a class that has a valid main() method. The JVM starts the program execution by calling the main() method of the given class. The exact name of the class should be specified, and not the name of the class file, i.e., the .class extension in the class file name should not be specified.

2 Language Fundamentals

2.1 (c)

52pickup is not a legal identifier. The first character of an identifier cannot be a digit.

2.2 (e)

In Java, the identifiers delete, thrown, exit, unsigned, and next are not keywords. Java has a goto keyword, but it is reserved and not currently used.

2.3 (b)

It is a completely valid comment. Comments do not nest. Everything from the start sequence of a multiple-line comment (/*) to the first occurrence of the end sequence of a multiple-line comment (*/) is ignored by the compiler.

2.4 (a) and (d)

String is a class, and "hello" and "t" denote String objects. Java has the following primitive data types: boolean, byte, short, char, int, long, float, and double.

2.5 (a), (c), and (e)

(a) is a boolean data type, while (c) and (e) are floating-point data types.

2.6 (c)

The bit representation of int is 32-bits wide and can hold values in the range –231 through 231–1.

2.7 (a), (c), and (d)

The uxxxx notation can be used anywhere in the source to represent Unicode characters.

2.8 (c)

Local variable a is declared but not initialized. The first line of code declares the local variables a and b. The second line of code initializes the local variable b. Local variable a remains uninitialized.

2.9 (c)

The local variable of type float will remain uninitialized. Fields and static variables are initialized with a default value. Local variables remain uninitialized unless explicitly initialized. The type of the variable does not affect whether a variable is initialized or not.

2.10 (e)

The program will compile. The compiler can figure out that the local variable price will be initialized, since the value of the condition in the if statement is true. The two instance variables and the two static variables are all initialized to the respective default value of their type.

3 Declarations

3.1 (b)

Only (b) is a valid method declaration. Methods must specify a return type or must be declared void. This makes (d) and (e) invalid. Methods must specify a list of zero or more comma-separated parameters enclosed by parentheses, ( ). The keyword void cannot be used to specify an empty parameter list. This makes (a) and (c) invalid.

3.2 (a), (b), and (e)

Non-static methods have an implicit this object reference. The this reference cannot be changed, as in (c). The this reference can be used in a non-static context to refer to both instance and static members. However, it cannot be used to refer to local variables, as in (d).

3.3 (a) and (d)

The first and the third pairs of methods will compile. The second pair of methods will not compile, since their method signatures do not differ. The compiler has no way of differentiating between the two methods. Note that the return type and the names of the parameters are not a part of the method signatures. Both methods in the first pair are named fly and, therefore, overload this method name. The methods in the last pair do not overload the method name glide, since only one method has that name. The method named Glide is distinct from the method named glide, as identifiers are case-sensitive in Java.

3.4 (a)

A constructor cannot specify any return type, not even void. A constructor cannot be final, static, or abstract.

3.5 (b) and (e)

A constructor can be declared private, but this means that this constructor can only be used within the class. Constructors need not initialize all the fields when a class is instanstiated. A field will be assigned a default value if not explicitly initialized. A constructor is non-static and, as such, it can directly access both the static and non-static members of the class.

3.6 (c)

A compilation error will occur at (3), since the class does not have a constructor accepting a single argument of type int. The declaration at (1) declares a method, not a constructor, since it is declared as void. The method happens to have the same name as the class, but that is irrelevant. The class has an implicit default constructor, since the class contains no constructor declarations. This constructor is invoked to create a MyClass object at (2).

3.7 (a), (b), and (d)

We cannot instantiate an enum type using the new operator. An enum type is implicitly final. Enum types inherit members from the Object class, as any other reference type.

3.8 (d)

An enum type can be run as a standalone application. The constants need not be qualified when referenced inside the enum type declaration. The constants are static members. The toString() method always returns the name of the constant, unless it is overridden.

3.9 (c)

An enum type can be run as a standalone application. (1), (2), and (3) define constant-specific class bodies that override the toString() method. For constants that do not override the toString() method, the name of the constant is returned.

3.10 (d)

An enum type cannot be declared as abstract. (b) is not correct, because without the enum type name, it would be a call to an instance method in a static context. Any abstract method must be implemented by each enum constant.

3.11 (c)

All enum types override the equals() method from the Object class. The equals() method of an enum type compares its constants for equality according to reference equality (same as with the == operator). This equals() method is final.

3.12 (a) and (d)

Declarations in (a) and (d) are overridden in each constant-specific class body. Declarations in (b) and (c) are not overridden by the declarations in the constant-specific class bodies, because of the incompatible return type.

3.13 (c), (e), (f), and (g)

Note how the nested enum type constants are accessed. Enum constants of an enum type can be compared, and an enum constant is an instance of its enum type.

3.14 (d)

Enum constants can be used as case labels and are not qualified with the enum type name in the case label declaration. The switch expression is compatible with the case labels, as the reference this will refer to objects of the enum type Scale5, which is the type of the case labels. The call to the method getGrade() returns a char value, which in this case is 'C'.

3.15 (d), (f), and (g)

A nested enum type must be declared inside a static member type, like (2), (3) and (5). Note that a nested enum type is implicitly static, and the keyword static is not mandatory in this case. An enum type cannot be local, as static member types cannot be declared locally.

3.16 (a), (b), and (d)

The static method values() returns an array with the enum constants for the specified type. The final method name() always returns the name of the enum constant. There is no names() method for enums in the Java standard library. The loop in (d) only converts the array of enums to a list, and iterates over this list. The argument Direction.class is not an array and, therefore, an illegal argument to the asList() method.

3.17 (b)

A constructor in the enum type is called for each enum constant created, when the enum type is loaded.

3.18 (d)

In Java, arrays are objects. Each array object has a final field named length that stores the size of the array.

3.19 (a)

Java allows arrays of length zero. Such an array is passed as argument to the main() method when a Java program is run without any program arguments.

3.20 (c)

The [] notation can be placed both after the type name and after the variable name in an array declaration. Multidimensional arrays are created by constructing arrays that can contain references to other arrays. The expression new int[4][] will create an array of length 4, which can contain references to arrays of int values. The expression new int[4][4] will create the same two-dimensional array, but will in addition create four more one-dimensional arrays, each of length 4 and of the type int[]. References to each of these arrays are stored in the two-dimensional array. The expression int[][4] will not work, because the arrays for the dimensions must be created from left to right.

3.21 (b) and (e)

The size of the array cannot be specified, as in (b) and (e). The size of the array is given implicitly by the initialization code. The size of the array is never specified in the declaration of an array reference. The size of an array is always associated with the array instance (on the right-hand side), not the array reference (on the left-hand side).

3.22 (e)

The array declaration is valid, and will declare and initialize an array of length 20 containing int values. All the values of the array are initialized to their default value of 0. The for(;;) loop will print all the values in the array, that is, it will print 0 twenty times.

3.23 (d)

The program will print "0 false 0 null" when run. All the instance variables, including the array element, will be initialized to their default values. When concatenated with a string, the values are converted to their string representation. Notice that the null pointer is converted to the string “null", rather than throwing a NullPointerException.

3.24 (b)

Evaluation of the actual parameter i++ yields 0, and increments i to 1 in the process. The value 0 is copied into the formal parameter i of the method addTwo() during method invocation. However, the formal parameter is local to the method, and changing its value does not affect the value in the actual parameter. The value of the variable i in the main() method remains 1.

3.25 (d)

The variables a and b are local variables that contain primitive values. When these variables are passed as arguments to another method, the method receives copies of the primitive values in the variables. The actual variables are unaffected by operations performed on the copies of the primitive values within the called method. The variable bArr contains a reference value that denotes an array object containing primitive values. When the variable is passed as a parameter to another method, the method receives a copy of the reference value. Using this reference value, the method can manipulate the object that the reference value denotes. This allows the elements in the array object referenced by bArr to be accessed and modified in the method inc2().

3.26 (a) and (f)

A value can only be assigned once to a final variable. A final formal parameter is assigned the value of the actual parameter at method invocation. Within the method body, it is illegal to reassign or modify the value stored in a final parameter. This causes a++ and c = d to fail. Whether the actual parameter is final does not constrain the client that invoked the method, since the actual parameter values are assigned to the formal parameters.

3.27 (a), (d), and (f)

The ellipses (...) must be specified before the parameter name. Only one varargs parameter is permitted, and it must be the last parameter in the formal parameter list.

3.28 (c)

In (a) and (b), the arguments are elements in the array that is passed to the method. In (c), the int array is encapsulated as an element in the array that is passed to the method. Note that int[] is not a subtype of Object[]. In (d), (e), and (f), the argument is a subtype of Object[], and the argument itself is passed without being encapsulated in a new array.

3.29 (c)

The method call in (4) calls the method in (2). The method call in (5) calls the method in (1). The method call in (6) calls the method in (3), as does the call in (7). Note the type of the varargs parameter in (3): an array of arrays of int.

3.30 (d), (f), and (g)

The main() method must be declared public, static, and void and takes a single array of String objects as argument. The order of the static and public keywords is irrelevant. Also, declaring the method final is irrelevant in this respect.

3.31 (a), (b), and (c)

Neither main, String, nor args are reserved keywords, but they are legal identifiers. In the declaration public static void main(String[] args), the identifier main denotes the method that is the entry point of a program. In all other contexts, the identifier main has no predefined meaning.

3.32 (d)

The length of the array passed to the main() method is equal to the number of program arguments specified in the command line. Unlike some other programming languages, the element at index 0 does not contain the name of the program. The first argument given is retrieved using args[0], and the last argument given is retrieved using args[args.length-1].

3.33 (e)

The program will print "no arguments" and "four arguments" when called with zero and three program arguments, respectively. When the program is called with no program arguments, the args array will be of length zero. The program will in this case print "no arguments". When the program is called with three arguments, the args array will have length 3. Using the index 3 on the numbers array will retrieve the string "four", because the start index is 0.

4 Access Control

4.1 (c)

The code will fail to compile, since the package declaration cannot occur after an import statement. The package and import statements, if present, must always precede any type declarations. If a file contains both import statements and a package statement, the package statement must occur before the import statements.

4.2 (c) and (e)

The name of the class must be fully qualified. A parameter list after the method name is not permitted. (c) illustrates single static import and (e) illustrates static import on demand.

4.3 (a) and (b)

(a) imports all types from the package java.util, including the type java.util.Locale. (b) explicitly imports the type java.util.Locale, which is what is needed.

(c) is syntactically incorrect, as java.util.Locale.UK is not a type. (d) imports types from the package java.util.Locale, but not the type java.util.Locale.

In (e), the static import is specified from a package (java.util), and not from a type, as required. In (f), the static import is incorrectly specified for a type (java.util.Locale) and not for a static member.

Both (g) and (h) with static import do not work, because we are referring to the constant UK using the simple name of the class (Locale) in the main() method.

4.4 (f)

The enum type Signal is not visible outside the package p1. If it were, (b), (c) and (d) would work. No static import is really necessary, since the constants of the enum type Signal are not used in the package p2.

4.5 (a), (b), and (c)

(d) does not statically import p3.Util.print. Note that p3.Util.Format can be imported as a type and as a static member from p3.Util, as in (a) and (b), respectively.

4.6 (b) and (e)

Static import from a class does not automatically import static members of any nested types declared in that class. The order of the import statements is arbitrary as long as it is delcared after any package statement and before any type declaration. Name conflicts must be disambiguated explicitly.

4.7 (b), (d), and (f)

In (a), the file A.class will be placed in the same directory as the file A.java. There is no -D option for the javac command, as in (c). The compiler maps the package structure to the file system, creating the necessary (sub)directories.

4.8 (b) and (d)

In (a) and (c), class A cannot be found. In (e) and (f), class B cannot be found—there is no package under the current directory /top/wrk/pkg to search for class B. Note that specifying pkg in the classpath in (d) is superfluous. The parent directory of the package must be specified, i.e., the location of the package.

4.9 (d) and (f)

The parent directory (or location) of the package must be specified. Only (d) and (f) do that. (d) specifies the current directory as well, but the search is from left to right in the specified paths, resulting in the top.sub.A class being found.

4.10 (a) and (c)

There is no -d option for the java command, as in (b). There should be no white space between the -D option and the name-value pair, as in (d), and no white space around the = sign either. The value can be quoted, especially if it contains white space.

4.11 (e)

4.12 (c) and (d)

A class or interface name can be referred to by using either its fully qualified name or its simple name. Using the fully qualified name will always work, but in order to use the simple name it has to be imported. By importing net.basemaster.* all the type names from the package net.basemaster will be imported and can now be referred to using simple names. Importing net.* will not import the subpackage basemaster.

4.13 (c)

Any normal class can be declared abstract. A class cannot be instantiated if the class is declared abstract. The declaration of an abstract method cannot provide an implementation. The declaration of a non-abstract method must provide an implementation. If any method in a class is declared abstract, then the class must be declared abstract, so (a) is invalid. The declaration in (b) is not valid, since it omits the keyword abstract in the method declaration. The declaration in (d) is not valid, since it omits the keyword class.

4.14 (e)

A class can be extended unless it is declared final. For classes, final means it cannot be extended, while for methods, final means it cannot be overridden in a subclass. A nested static class, (d), can be extended. A private member class, (f), can also be extended. The keyword native can only be used for methods, not for classes and fields.

4.15 (b) and (d)

Outside the package, the member j is accessible to any class, whereas the member k is only accessible to subclasses of MyClass.

The field i has package accessibility, and is only accessible by classes inside the package. The field j has public accessibility, and is accessible from anywhere. The field k has protected accessibility, and is accessible from any class inside the package and from subclasses anywhere. The field l has private accessibility, and is only accessible within its own class.

4.16 (c)

The default accessibility for members is more restrictive than protected accessibility, but less restrictive than private. Members with default accessibility are only accessible within the class itself and from classes in the same package. Protected members are, in addition, accessible from subclasses anywhere. Members with private accessibility are only accessible within the class itself.

4.17 (b)

A private member is only accessible within the class of the member. If no accessibility modifier has been specified for a member, the member has default accessibility, also known as package accessibility. The keyword default is not an accessibility modifier, and its only use is as a label in a switch statement. A member with package accessibility is only accessible from classes in the same package. Subclasses in other packages cannot access a member with default accessibility.

4.18 (a), (c), (d), (e), and (h)

The lines (1), (3), (4), (5), and (8) will compile. Keep in mind that a protected member of a superclass is only accessible in a subclass that is in another package, if the member is inherited by an object of the subclass (or by an object of a subclass of this subclass). This rules out (2), (6), and (7). The class D does not have any inheritance relationship with any of the other classes, and it does not inherit the field pf. This rules out the lines from (9) to (12).

4.19 (b) and (e)

You cannot specify accessibility of local variables. They are accessible only within the block in which they are declared.

Objects themselves do not have any accessibility, only references to objects do. If no accessibility modifier (public, protected, or private) is given in the member declaration of a class, the member is only accessible by classes in the same package. A subclass does not have access to members with default accessibility declared in a superclass, unless both classes are in the same package. Local variables cannot be declared static or have an accessibility modifier.

4.20 (c)

The line void k() { i++; } can be re-inserted without introducing errors. Re-inserting line (1) will cause the compilation to fail, since MyOtherClass will try to override a final method. Re-inserting line (2) will fail, since MyOtherClass will no longer have a default constructor. The main() method needs to call the default constructor. Reinserting line (3) will work without any problems, but re-inserting line (4) will fail, since the method will try to access a private member of the superclass.

4.21 (e)

An object reference is needed to access non-static members. Static methods do not have the implicit object reference this, and must always supply an explicit object reference when referring to non-static members. The static method main() legally refers to the non-static method func(), using the reference variable ref. Static members are accessible both from static and non-static methods, using their simple names. No NullPointerException is thrown, as ref refers to an instance of MyClass.

4.22 (c)

Local variables can have the same name as member variables. The local variables will simply shadow the member variables with the same names. Declaration (4) defines a static method that tries to access a variable named a, which is not locally declared. Since the method is static, this access will only be valid if variable a is declared static within the class. Therefore, declarations (1) and (4) cannot occur in the same class declaration, while declarations (2) and (4) can.

4.23 (b)

The keyword this can only be used in instance (non-static) methods. Only one occurrence of each static variable of a class is created, when the class is loaded by the JVM. This occurrence is shared among all the objects of the class (and for that matter, by other clients). Local variables are only accessible within the block scope, regardless of whether the block scope is defined within a static context.

4.24 (c)

A class can be declared abstract even if it does not delcare any abstract methods. The variable k cannot be declared synchronized. Only methods and blocks can be synchronized.

4.25 (c)

The declaration in (c) is not legal, as variables cannot be declared abstract. The keywords static and final are valid modifiers for both field and method declarations. The modifiers abstract and native are valid for methods, but not for fields.

4.26 (a) and (c)

Abstract classes can declare both final methods and non-abstract methods. Non-abstract classes cannot, however, contain abstract methods. Nor can abstract classes be final. Only methods can be declared native.

4.27 (a)

The keyword transient signifies that the fields should not be stored when objects are serialized. Constructors cannot be declared abstract. When an array object is created, as in (c), the elements in the array object are assigned the default value corresponding to the type of the elements. Whether the reference variable denoting the array object is a local or a member variable is irrelevant. Abstract methods from a superclass need not be implemented by a subclass, but the subclass must then be declared abstract.

5 Operators and Expressions

5.1 (a)

A value of type char can be assigned to a variable of type int. An widening conversion will convert the value to an int.

5.2 (d)

An assignment statement is an expression statement. The value of the expression statement is the value of the expression on the right-hand side. Since the assignment operator is right associative, the statement a = b = c = 20 is evaluated as follows: (a = (b = (c = 20))). This results in the value 20 being assigned to c, then the same value being assigned to b and finally to a. The program will compile, and print 20, when run.

5.3 (c)

Strings are objects. The variables a, b, and c are references that can denote such objects. Assigning to a reference only changes the reference value. It does not create a copy of the source object or change the object denoted by the old reference value in the target reference. In other words, assignment to references only affects which object the target reference denotes. The reference value of the "cat" object is first assigned to a, then to b, and later to c. The program prints the string denoted by c, i.e., "cat".

5.4 (a), (d), and (e)

A binary expression with any floating-point operand will be evaluated using floating-point arithmetic. Expressions such as 2/3, where both operands are integers, will use integer arithmetic and evaluate to an integer value. In (e), the result of (0x10 * 1L) is promoted to a floating-point value.

5.5 (b)

The / operator has higher precedence than the + operator. This means that the expression is evaluated as ((1/2) + (3/2) + 0.1). The associativity of the binary operators is from left to right, giving (((1/2) + (3/2)) + 0.1). Integer division results in ((0 + 1) + 0.1) which evaluates to 1.1.

5.6 (d)

0x10 is a hexadecimal literal equivalent to the decimal value 16. 10 is a decimal literal. 010 is an octal literal equivalent to the decimal value 8. The println() method will print the sum of these values, which is 34, in decimal form.

5.7 (b), (c), and (f)

The unary + and - operators with right-to-left associativity are used in the valid expressions (b), (c), and (f). Expression (a) tries to use a nonexistent unary - operator with left-to-right associativity, expression (d) tries to use a decrement operator (--) on an expression that does not resolve to a variable, and expression (e) tries to use a nonexistent unary * operator.

5.8 (b)

The expression evaluates to –6. The whole expression is evaluated as (((-(-1)) - ((3 * 10) / 5)) - 1) according to the precedence and associativity rules.

5.9 (a), (b), (d), and (e)

In (a), the conditions for implicit narrowing conversion are fulfilled: the source is a constant expression of type int, the destination type is of type short, the value of the source (12) is in the range of the destination type. The assignments in (b), (d), and (e) are valid, since the source type is narrower than the target type and an implicit widening conversion will be applied. The expression (c) is not valid. Values of type boolean cannot be converted to other types.

5.10 (a), (c), and (d)

The left associativity of the + operator makes the evaluation of (1 + 2 + "3") proceed as follows: (1 + 2) + "3"3 + "3""33". Evaluation of the expression ("1" + 2 + 3), however, will proceed as follows: ("1" + 2) + 3"12" + 3"123". (4 + 1.0f) evaluates as 4.0f + 1.0f5.0f and (10/9) performs integer division, resulting in the value 1. The operand 'a' in the expression ('a' + 1) will be promoted to int, and the resulting value will be of type int.

5.11 (d)

The expression ++k + k++ + + k is evaluated as ((++k) + (k++)) + (+k)((2) + (2) + (3)), resulting in the value 7.

5.12 (d)

The types char and int are both integral. A char value can be assigned to an int variable since the int type is wider than the char type and an implicit widening conversion will be done. An int type cannot be assigned to a char variable because the char type is narrower than the int type. The compiler will report an error about a possible loss of precision in (4).

5.13 (c)

Variables of the type byte can store values in the range –128 to 127. The expression on the right-hand side of the first assignment is the int literal 128. Had this literal been in the range of the byte type, an implicit narrowing conversion would have been applied to convert it to a byte value during assignment. Since 128 is outside the range of the type byte, the program will not compile.

5.14 (a)

First, the expression ++i is evaluated, resulting in the value 2. Now the variable i also has the value 2. The target of the assignment is now determined to be the element array[2]. Evaluation of the right-hand expression, --i, results in the value 1. The variable i now has the value 1. The value of the right-hand expression 1 is then assigned to the array element array[2], resulting in the array contents to become {4, 8, 1}. The program sums these values and prints 13.

5.15 (a) and (c)

The expression (4 <= 4) is true. The null literal can be compared, so (null != null) yields false.

5.16 (c) and (e)

The remainder operator is not limited to integral values, but can also be applied to floating-point operands. Short-circuit evaluation occurs with the conditional operators (&&, ||). The operators *, /, and % have the same level of precedence. The type short has the range -32768 to +32767, inclusive. (+15) is a legal expression using the unary + operator.

5.17 (a), (c), and (e)

The != and ^ operators, when used on boolean operands, will return true if and only if one operand is true, and false otherwise. This means that d and e in the program will always be assigned the same value, given any combination of truth values in a and b. The program will, therefore, print true four times.

5.18 (b)

The element referenced by a[i] is determined based on the current value of i, which is zero, i.e., the element a[0]. The expression i = 9 will evaluate to the value 9, which will be assigned to the variable i. The value 9 is also assigned to the array element a[0]. After the execution of the statement, the variable i will contain the value 9, and the array a will contain the values 9 and 6. The program will print 9 9 6, when run.

5.19 (c) and (d)

Unlike the & and | operators, the && and || operators short-circuit the evaluation of their operands if the result of the operation can be determined from the value of the first operand. The second operand of the || operator in the program is never evaluated because of short-circuiting. All the operands of the other operators are evaluated. Variable i ends up with the value 3, which is the first digit printed, and j ends up with the value 1, which is the second digit printed.

6 Control Flow

6.1 (d)

The program will display the letter b when run. The second if statement is evaluated since the boolean expression of the first if statement is true. The else clause belongs to the second if statement. Since the boolean expression of the second if statement is false, the if block is skipped and the else clause is executed.

6.2 (a), (b), and (e)

The conditional expression of an if statement can have any subexpressions, including method calls, as long as the whole expression evaluates to a value of type boolean. The expression (a = b) does not compare the variables a and b, but assigns the value of b to the variable a. The result of the expression is the value being assigned. Since a and b are boolean variables, the value returned by the expression is also boolean. This allows the expression to be used as the condition for an if statement. An if statement must always have an if block, but the else clause is optional. The expression if (false) ; else ; is legal. In this case, both the if block and the else block are simply the empty statement.

6.3 (f)

There is nothing wrong with the code. The case and default labels do not have to be specified in any specific order. The use of the break statement is not mandatory, and without it the control flow will simply fall through the labels of the switch statement.

6.4 (a) and (f)

The type of the switch expression must be either an enum type or one of the following: byte, char, short, int or the corresponding wrapper type for these primitive types. This excludes (b) and (e). The type of the case labels must be assignable to the type of the switch expression. This excludes (c) and (d). The case label value must be a constant expression, which is not the case in (g) where the case label value is of type Byte.

6.5 (c)

The case label value 2 * iLoc is a constant expression whose value is 6, the same as the switch expression. Fall through results in the printout shown in (c).

6.6 (b)

The switch expression, when unboxed, has the value 5. The statement associated with the default label is excecuted, and the fall through is stopped by the break statement.

6.7 (a)

The value of the case label iFour is not a constant expression and, therefore, the code will not compile.

6.8 (d)

Enum constants can be used as case labels and are not qualified with the enum type name in the case label declaration. The switch expression is compatible with the case labels, as the reference this will refer to objects of the enum type Scale5, which is the type of the case labels. The call to the method getGrade() returns a char value, which in this case is 'C'.

6.9 (e)

The loop body is executed twice and the program will print 3. The first time the loop is executed, the variable i changes from 1 to 2 and the variable b changes from false to true. Then the loop condition is evaluated. Since b is true, the loop body is executed again. This time the variable i changes from 2 to 3 and the variable b changes from true to false. The loop condition is now evaluated again. Since b is now false, the loop terminates and the current value of i is printed.

6.10 (b) and (e)

Both the first and the second number printed will be 10. Both the loop body and the increment expression will be executed exactly 10 times. Each execution of the loop body will be directly followed by an execution of the increment expression. Afterwards, the condition j<10 is evaluated to see whether the loop body should be executed again.

6.11 (c)

Only (c) contains a valid for loop. The initializer in a for statement can contain either declarations or a list of expression statements, but not both as attempted in (a). The loop condition must be of type boolean. (b) tries to use an assignment of an int value (notice the use of = rather than ==) as a loop condition and is, therefore, not valid. The loop condition in the for loop (d) tries to use the uninitialized variable i, and the for loop in (e) is syntactically invalid, as there is only one semicolon.

6.12 (f)

The code will compile without error, but will never terminate when run. All the sections in the for header are optional and can be omitted (but not the semicolons). An omitted loop condition is interpreted as being true. Thus, a for loop with an omitted loop condition will never terminate, unless a break statement is encountered in the loop body. The program will enter an infinite loop at (4).

6.13 (b), (d), and (e)

The loop condition in a while statement is not optional. It is not possible to break out of the if statement in (c). Notice that if the if statement had been placed within a labeled block, a switch statement, or a loop, the usage of break would be valid.

6.14 (a) and (d)

i=1, j=0” and “i=2, j=1” are part of the output. The variable i iterates through the values 0, 1, and 2 in the outer loop, while j toggles between the values 0 and 1 in the inner loop. If the values of i and j are equal, the printing of the values is skipped and the execution continues with the next iteration of the outer loop. The following can be deduced when the program is run: variables i and j are both 0 and the execution continues with the next iteration of the outer loop. “i=1, j=0” is printed and the next iteration of the inner loop starts. Variables i and j are both 1 and the execution continues with the next iteration of the outer loop. “i=2, j=0” is printed and the next iteration of the inner loop starts. “i=2, j=1” is printed, j is incremented, j < 2 fails, and the inner loop ends. Variable i is incremented, i < 3 fails, and the outer loop ends.

6.15 (b)

The code will fail to compile, since the conditional expression of the if statement is not of type boolean. The conditional expression of an if statement must be of type boolean. The variable i is of type int. There is no conversion between boolean and other primitive types.

6.16 (d)

Implementation (4) will correctly return the largest value. The if statement does not return any value and, therefore, cannot be used as in implementations (1) and (2). Implementation (3) is invalid since neither the switch expression nor the case label values can be of type boolean.

6.17 (c)

As it stands, the program will compile correctly and will print “3, 2” when run. If the break statement is replaced with a continue statement, the loop will perform all four iterations and will print “4, 3”. If the break statement is replaced with a return statement, the whole method will end when i equals 2, before anything is printed. If the break statement is simply removed, leaving the empty statement (;), the loop will complete all four iterations and will print “4, 4”.

6.18 (a) and (c)

The block construct {} is a compound statement. The compound statement can contain zero or more arbitrary statements. Thus, {{}} is a legal compound statement, containing one statement that is also a compound statement, containing no statement. The block { continue; } by itself is not valid, since the continue statement cannot be used outside the context of a loop. (c) is a valid example of breaking out of a labeled block. (d) is not valid for the same reasons (b) was not valid. The statement at (e) is not true, since the break statement can also be used to break out of labeled blocks, as illustrated by (c).

6.19 (c) and (d)

Only the element type in (c), i.e., Integer, can be automatically unboxed to an int as required. The element type in (d) is int.

6.20 (d) and (e)

In the header of a for(:) loop, we can only declare a local variable that is compatible with the element type of an Iterable or an array. (The Iterable interface is discussed in Chapter 15). This rules out (a) and (b). The Iterable or array can be specified by an expression that evaluates to a reference type of an Iterable or an array. It is only evaluated once. Expressions that are permissible are found in (a), (d), and (e), but only (d) and (e) specify a legal for(:) header.

6.21 (d)

The type of nums is int[][]. The outer loop iterates over the rows, so the type of the loop variable in the outer loop must be int[], and the loop expression is nums. The inner loop iterates over each row, i.e. int[]. The loop variable in the inner loop must be int, and the loop expression in the inner loop is a row given by the loop variable of the outer loop. Only in the loop headers in (d) are both element types compatible.

6.22 (d)

There are no problems with automatic unboxing/boxing of the Character variable cRef in the various contexts where it is used in the program.

6.23 (d)

The program will only print 1, 4, and 5, in that order. The expression 5/k will throw an ArithmeticException, since k equals 0. Control is transferred to the first catch block, since it is the first block that can handle arithmetic exceptions. This exception handler simply prints 1. The exception has now been caught and normal execution can resume. Before leaving the try statement, the finally block is executed. This block prints 4. The last statement of the main() method prints 5.

6.24 (b) and (e)

If run with no arguments, the program will print "The end". If run with one argument, the program will print the given argument followed by "The end". The finally block will always be executed, no matter how control leaves the try block.

6.25 (d)

The program will compile without error, but will throw a NullPointerException when run. The throw statement can only throw Throwable objects. A NullPointerException will be thrown if the expression of the throw statement results in a null reference.

6.26 (c) and (d)

Normal execution will only resume if the exception is caught by the method. The uncaught exception will propagate up the runtime stack until some method handles it. An overriding method need only declare that it can throw a subset of the checked exceptions the overridden method can throw. The main() method can declare that it throws checked exceptions just like any other method. The finally block will always be executed, no matter how control leaves the try block.

6.27 (b)

The program will print 1 and 4, in that order. An InterruptedException is handled in the first catch block. Inside this block a new RuntimeException is thrown. This exception was not thrown inside the try block and will not be handled by the catch blocks, but will be sent to the caller of the main() method. Before this happens, the finally block is executed. The code to print 5 is never reached, since the Runtime-Exception remains uncaught after the execution of the finally block.

6.28 (a)

The program will print 2 and throw an InterruptedException. An InterruptedException is thrown in the try block. There is no catch block to handle the exception, so it will be sent to the caller of the main() method, i.e., to the default exception handler. Before this happens, the finally block is executed. The code to print 3 is never reached.

6.29 (b)

The only thing that is wrong with the code is the ordering of the catch and finally blocks. If present, the finally block must always appear last in a try-catch-finally construct.

6.30 (a)

Overriding methods can specify all, none, or a subset of the checked exceptions the overridden method declares in its throws clause. The InterruptedException is the only checked exception specified in the throws clause of the overridden method. The overriding method f() need not specify the InterruptedException from the throws clause of the overridden method, because the exception is not thrown here.

6.31 (c)

The overriding f() method in MyClass is not permitted to throw the checked InterruptedException, since the f() method in class A does not throw this exception. To avoid compilation errors, either the overriding f() method must not throw an InterruptedException or the overridden f() method must declare that it can throw an InterruptedException.

6.32 (c) and (d)

Statements (c) and (d) will throw an AssertionError because the first expression is false. Statement (c) will report true as the error message, while (d) will report false as the error message.

6.33 (b) and (d)

-ea (enable assertions) is a valid runtime option, not -ae. -source 1.4 is a compile time option. -dsa (disable system assertions) is a valid runtime option, not -dea.

6.34 (e)

The class of exceptions thrown by assertion statements is always AssertionError.

6.35 (c)

Assertions can be enabled or disabled at runtime, but the assert statements are always compiled into bytecode.

6.36 (a) and (b)

Statement (a) will cause the assert statement to throw an AssertionError since (-50 > -50) evaluates to false. Statement (b) will cause the expression 100/value to throw an ArithmeticException since an integer division by zero is attempted.

6.37 (a) and (d)

Option (a) enables assertions for all non-system classes, while (d) enables assertions for all classes in the package org and its subpackages. Options (b), (c), and (f) try to enable assertions in specifically named classes: Bottle, org.example, and org.example.ttp. Option (e) is not a valid runtime option.

6.38 (c)

The assert statement correctly asserts that 150 is greater than 100 and less than 200.

6.39 (b) and (d)

The AssertionError class, like all other error classes, is not a checked exception, and need not be declared in a throws clause. After an AssertionError is thrown, it is propagated exactly the same way as other exceptions, and can be caught by a try-catch construct. It inherits the toString() method from the Throwable class.

6.40 (d)

The Object class is the direct superclass of the Throwable class. The Throwable class is the direct superclass of the Error and Exception classes. The Error class is the direct superclass of the AssertionError class. The Exception class is the direct superclass of RuntimeException class.

6.41 (c) and (d)

The command line enables assertions for all non-system classes, except for those in the com package or one of its subpackages. (b) and (e) are incorrect because assertions are not enabled for the system classes.

7 Object-Oriented Programming

7.1 (a) and (b)

The extends clause is used to specify that a class extends another class. A subclass can be declared abstract regardless of whether the superclass was declared abstract. Private, overridden, and hidden members from the superclass are not inherited by the subclass. A class cannot be declared both abstract and final, since an abstract class needs to be extended to be useful, and a final class cannot be extended. The accessibility of the class is not limited by the accessibility of its members. A class with all the members declared private can still be declared public.

7.2 (b) and (e)

The Object class has a public method named equals, but it does not have any method named length. Since all classes are subclasses of the Object class, they all inherit the equals() method. Thus, all Java objects have a public method named equals. In Java, a class can only extend a single superclass, but there is no limit on how many classes can extend a superclass.

7.3 (b) and (c)

A subclass need not redefine all the methods defined in the superclass. It is possible for a subclass to define a method with the same name and parameters as a method defined by the superclass, but then the new method must satisfy the criteria for method overriding. A subclass can define a field that can hide a field defined in a superclass. Two classes cannot be the superclass of one another.

7.4 (a), (b), and (d)

Bar is a subclass of Foo that overrides the method g(). The statement a.j = 5 is not legal, since the member j in the class Bar cannot be accessed through a Foo reference. The statement b.i = 3 is not legal either, since the private member i cannot be accessed from outside of the class Foo.

7.5 (a)

A method can be overridden by defining a method with the same signature (i.e., name and parameter list) and where the return types are the same or covariant. Only instance methods that are accessible by their simple name can be overridden. A private method, therefore, cannot be overridden in subclasses, but the subclasses are allowed to define a new method with exactly the same signature. A final method cannot be overridden. An overriding method cannot exhibit behavior that contradicts the declaration of the original method. An overriding method, therefore, cannot declare that it throws checked exceptions that cannot be thrown by the original method in the superclass.

7.6 (g)

It is not possible to invoke the doIt() method in A from an instance method in class C. The method in C needs to call a method in a superclass two levels up in the inheritance hierarchy. The super.super.doIt() strategy will not work, since super is a keyword and cannot be used as an ordinary reference, nor accessed like a field. If the member to be accessed had been a field, the solution would be to cast the this reference to the class of the field and use the resulting reference to access the field. Field access is determined by the declared type of the reference, whereas the instance method to execute is determined by the actual type of the object denoted by the reference.

7.7 (e)

The code will compile without errors. None of the calls to a max() method are ambiguous. When the program is run, the main() method will call the max() method in C with the parameters 13 and 29. This method will call the max() method in B with the parameters 23 and 39. The max() method in B will in turn call the max() method in A with the parameters 39 and 23. The max() method in A will return 39 to the max() method in B. The max() method in B will return 29 to the max() method in C. The max() method in C will return 29 to the main() method.

7.8 (c)

The simplest way to print the message in the class Message would be to use msg.text. The main() method creates an instance of MyClass, which results in the creation of a Message instance. The field msg denotes this Message object in MySuperclass and is inherited by the MyClass object. Thus, the message in the Message object can be accessed directly by msg.text in the print() method of MyClass.

7.9 (b) and (g)

(a) and (1) do not have covariant return types.

(b) overrides (2).

The instance method in (c) cannot override the static method at (4).

The static method in (d) and the static method at (4) do not have compatible return types.

The static method in (e) cannot override the instance method at (3).

The instance method in (f) and the instance method at (5) do not have compatible return types.

The instance method in (g) overrides the instance method at (6), and they have covariant return types.

7.10 (g)

In the class Car, the static method getModelName() hides the static method of the same name in the superclass Vehicle. In the class Car, the instance method getRegNo() overrides the instance method of the same name in the superclass Vehicle. The declared type of the reference determines the method to execute when a static method is called, but the actual type of the object at runtime determines the method to execute when an overridden method is called.

7.11 (d)

Note that the method equals() in the class Item overloads the method with the same name in the Object class. Calls to overloaded methods are resolved at compiletime. Using the reference itemA of the type Item results in the equals() method of the Item to be executed, and using the reference itemC of the type Object results in the equals() method of the Object class to be executed. This is a canonical example where using the @Override annotation in front of the equals() method would be very useful.

7.12 (e)

The class MySuper does not have a default constructor. This means that constructors in subclasses must explicitly call the superclass constructor and provide the required parameters. The supplied constructor accomplishes this by calling super(num) in its first statement. Additional constructors can accomplish this either by calling the superclass constructor directly using the super() call, or by calling another constructor in the same class using the this() call which, in turn, calls the superclass constructor. (a) and (b) are not valid, since they do not call the superclass constructor explicitly. (d) fails, since the super() call must always be the first statement in the constructor body. (f) fails, since the super() and this() calls cannot be combined.

7.13 (b)

In a subclass without any declared constructors, the implicit default constructor will call super(). The use of the super() and this() statements are not mandatory as long as the superclass has a default constructor. If neither super() nor this() is declared as the first statement in the body of a constructor, then the default super() will implicitly be the first statement. A constructor body cannot have both a super() and a this() statement. Calling super() will not always work, since a superclass might not have a default constructor.

7.14 (d)

The program will print 12 followed by Test. When the main() method is executed, it will create a new instance of B by passing "Test" as argument. This results in a call to the constructor of B that has one String parameter. The constructor does not explicitly call any superclass constructor but, instead, the default constructor of the superclass A is called implicitly. The default constructor of A calls the constructor in A that has two String parameters, passing it the argument list ("1", "2"). This constructor calls the constructor with one String parameter, passing the argument "12". This constructor prints the argument. Now the execution of all the constructors in A is completed, and execution continues in the constructor of B. This constructor now prints the original argument "Test" and returns to the main() method.

7.15 (b) and (c)

Interface declarations do not provide any method implementations and only permit multiple interface inheritance. An interface can extend any number of interfaces and can be extended by any number of interfaces. Fields in interfaces are always static, and can be declared static explicitly. Abstract method declarations in interfaces are always non-static, and cannot be declared static.

7.16 (a), (c), and (d)

Fields in interfaces declare named constants, and are always public, static, and final. None of these modifiers are mandatory in a constant declaration. All named constants must be explicitly initialized in the declaration.

7.17 (a) and (d)

The keyword implements is used when a class implements an interface. The keyword extends is used when an interface inherits from another interface or a class inherits from another class.

7.18 (e)

The code will compile without errors. The class MyClass declares that it implements the interfaces Interface1 and Interface2. Since the class is declared abstract, it does not need to implement all abstract method declarations defined in these interfaces. Any non-abstract subclasses of MyClass must provide the missing method implementations. The two interfaces share a common abstract method declaration void g(). MyClass provides an implementation for this abstract method declaration that satisfies both Interface1 and Interface2. Both interfaces provide declarations of constants named VAL_B. This can lead to an ambiguity when referring to VAL_B by its simple name from MyClass. The ambiguity can be resolved by using fully qualified names: Interface1.VAL_B and Interface2.VAL_B. However, there are no problems with the code as it stands.

7.19 (a) and (c)

Declaration (b) fails, since it contains an illegal forward reference to its own named constant. The field type is missing in declaration (d). Declaration (e) tries illegally to use the protected modifier, even though named constants always have public accessibility. Such constants are implicitly public, static, and final.

7.20 (c)

The program will throw a java.lang.ClassCastException in the assignment at (3), when run. The statement at (1) will compile, since the assignment is done from a subclass reference to a superclass reference. The cast at (2) assures the compiler that arrA will refer to an object that can be referenced by arrB. This will work when run, since arrA will refer to an object of type B[]. The cast at (3) will also assure the compiler that arrA will refer to an object that can be referenced by arrB. This will not work when run, since arrA will refer to an object of type A[].

7.21 (d)

(4) will cause a compile-time error, since it attempts to assign a reference value of a supertype object to a reference of a subtype. The type of the source reference value is MyClass and the type of the destination reference is MySubclass. (1) and (2) will compile, since the reference is assigned a reference value of the same type. (3) will also compile, since the reference is assigned a reference value of a subtype.

7.22 (e)

Only the assignment I1 b = obj3 is valid. The assignment is allowed, since C3 extends C1, which implements I1. The assignment obj2 = obj1 is not legal, since C1 is not a subclass of C2. The assignments obj3 = obj1 and obj3 = obj2 are not legal, since neither C1 nor C2 is a subclass of C3. The assignment I1 a = obj2 is not legal, since C2 does not implement I1. Assignment I2 c = obj1 is not legal, since C1 does not implement I2.

7.23 (b)

The statement would be legal at compile time, since the reference x might actually refer to an object of the type Sub. The cast tells the compiler to go ahead and allow the assignment. At runtime, the reference x may turn out to denote an object of the type Super instead. If this happens, the assignment will be aborted and a ClassCastException will be thrown.

7.24 (c)

Only A a = d is legal. The reference value in d can be assigned to a, since D implements A. The statements c = d and d = c are illegal, since there is no subtypesupertype relationship between C and D. Even though a cast is provided, the statement d = (D) c is illegal. The object referred to by c cannot possibly be of type D, since D is not a subclass of C. The statement c = b is illegal, since assigning a reference value of a reference of type B to a reference of type C requires a cast.

7.25 (a), (b), and (c)

The program will print A, B, and C when run. The object denoted by the reference a is of type C. The object is also an instance of A and B, since C is a subclass of B and B is a subclass of A. The object is not an instance of D.

7.26 (b)

The expression (o instanceof B) will return true if the object referred to by o is of type B or a subtype of B. The expression (!(o instanceof C)) will return true unless the object referred to by o is of type C or a subtype of C. Thus, the expression (o instanceof B) && (!(o instanceof C)) will only return true if the object is of type B or a subtype of B that is not C or a subtype of C. Given objects of the classes A, B, and C, this expression will only return true for objects of class B.

7.27 (a)

The program will print all the letters I, J, C, and D, when run. The object referred to by the reference x is of class D. Class D extends class C and class C implements interface I. This makes I, J, and C supertypes of class D. The reference value of an object of class D can be assigned to any reference of its supertypes and is, therefore, an instanceof these types.

7.35 (e)

The program will print 2 when System.out.println(ref2.f()) is executed. The object referenced by ref2 is of the class C, but the reference is of type B. Since B contains a method f(), the method call will be allowed at compile time. During execution it is determined that the object is of the class C, and dynamic method lookup will cause the overridden method in C to be executed.

7.36 (c)

The program will print 1 when run. The f() methods in A and B are private and are not accessible by the subclasses. Because of this, the subclasses cannot overload or override these methods, but simply define new methods with the same signature. The object being called is of the class C. The reference used to access the object is of the type B. Since B contains a method g(), the method call will be allowed at compile time. During execution it is determined that the object is of the class C, and dynamic method lookup will cause the overridden method g() in B to be executed. This method calls a method named f. It can be determined during compilation that this can only refer to the f() method in B, since the method is private and cannot be overridden. This method returns the value 1, which is printed.

7.37 (c) and (d)

The code as it stands will compile. The use of inheritance in this code does not define a Planet has-a Star relationship. The code will fail if the name of the field starName is changed in the Star class, since the subclass Planet tries to access it using the name starName. An instance of Planet is not an instance of HeavenlyBody. Neither Planet nor Star implements HeavenlyBody.

7.38 (b)

The code will compile. The code will not fail to compile if the name of the field starName is changed in the Star class, since the Planet class does not try to access the field by name, but instead uses the public method describe() in the Star class for that purpose. An instance of Planet is not an instance of HeavenlyBody, since it neither implements HeavenlyBody nor extends a class that implements HeavenlyBody.

7.39 (f)

(a) to (e) are all true, but (f) is not.

8 Nested Type Declarations

8.1 (e)

The code will compile, and print 123, when run. An instance of the Outer class will be created and the field secret will be initialized to 123. A call to the createInner() method will return the reference value of the newly created Inner instance. This object is an instance of a non-static member class and is associated with the outer instance. This means that an object of a non-static member class has access to the members within the outer instance. Since the Inner class is nested in the class containing the field secret, this field is accessible to the Inner instance, even though the field secret is declared private.

8.2 (b) and (e)

A static member class is in many respects like a top-level class and can contain non-static fields. Instances of non-static member classes are created in the context of an outer instance. The outer instance is inherently associated with the inner instance. Several non-static member class instances can be created and associated with the same outer instance. Static member classes do not have any inherent outer instance. A static member interface, just like top-level interfaces, cannot contain non-static fields. Nested interfaces are always static.

8.3 (e)

The program will fail to compile, since the expression ((State) this).val in the method restore() of the class Memento is invalid. The correct way to access the field val in the class State, which is hidden by the field val in the class Memento, is to use the expression State.this.val. Other than that, there are no problems with the code.

8.4 (d)

The program will compile without error, and will print 1, 3, 4, in that order, when run. The expression B.this.val will access the value 1 stored in the field val of the (outer) B instance associated with the (inner) C object referenced by the reference obj. The expression C.this.val will access the value 3 stored in the field val of the C object referenced by the reference obj. The expression super.val will access the field val from A, the superclass of C.

8.5 (c) and (d)

The class Inner is a non-static member class of the Outer class and its full name is Outer.Inner. The Inner class does not inherit from the Outer class. The method named doIt is, therefore, neither overridden nor overloaded. Within the scope of the Inner class, the doIt() method of the Outer class is hidden by the doIt() method of the Inner class.

8.6 (f)

The nested class Inner is a non-static member class, and can only be instantiated in the context of an outer instance of the class Outer. Each Outer object has its own counter for the number of Inner objects associated with it. The instance method multiply() creates three objects of the class Inner: two in the context of the current Outer instance, and one in the context of a new Outer object.

The counter value printed by the first print statement is returned by the second Inner object which is associated with the current Outer object. And since the current Outer object has two Inner objects associated with it at this point, the value 2 of its counter is printed.

The counter value printed by the second print statement is returned by the third Inner object which is associated with the new Outer object created in the multiply() method. And since the second Outer object has only one Inner object associated with it, the value 1 of its counter is printed.

8.7 (e)

Non-static member classes, unlike top-level classes, can have any accessibility modifier. Static member classes can only be declared in top-level or nested static member classes and interfaces. Only static member classes can be declared static. Declaring a class static only means that instances of the class are created without having an outer instance. This has no bearing on whether the members of the class can be static or not.

8.8 (d) and (e)

The methods labeled (1) and (3) will not compile, since the non-final parameter i is not accessible from within the inner class. The syntax of the anonymous class in the method labeled (2) is not correct, as the parameter list is missing.

8.9 (a) and (d)

No other static members, except final static fields, can be declared within a non-static member class. Members in outer instances are directly accessible using simple names (provided they are not hidden). Fields in nested static member classes need not be final. Anonymous classes cannot have constructors, since they have no names. Nested classes define distinct types from the enclosing class, and the instanceof operator does not take the type of the outer instance into consideration.

8.10 (d)

The iterator implemented will traverse the elements of the array in the reverse order, and so will the for(:) loop. The Iterable and the Iterator interfaces are implemented correctly.

8.11 (b)

Classes can be declared as members of top-level classes. Such a class is a static member class if it is declared static, otherwise, it is a non-static member class. Top-level classes, local classes, and anonymous classes cannot be declared static.

8.12 (d)

Note that the nested classes are locally declared in a static context.

(a) and (b) refer to the field str1 in Inner. (c) refers to the field str1 in Access. (e) requires the Helper class to be in the Inner class in order to compile, but this will not print the right answer. (f), (g), and (h) will not compile, as the Helper local class cannot be accessed using the enclosing class name.

8.13 (e)

No statement is executed in the main() method that will print anything. The print statement in the constructor of the Inner class will only be executed if an object of the Inner class is created in the main() method.

8.14 (b), (c), and (e)

(b) and (c) because i2 and i3 are instance fields that cannot be accessed from a static context, in this case from a local class in a static method. (f) is not allowed because the local variable i5 is not declared final in the enclosing method.

9 Object Lifetime

9.1 (e)

An object is only eligible for garbage collection if all remaining references to the object are from other objects that are also eligible for garbage collection. Therefore, if an object obj2 is eligible for garbage collection and object obj1 contains a reference to it, then object obj1 must also be eligible for garbage collection. Java does not have a keyword delete. An object will not necessarily be garbage collected immediately after it becomes unreachable. However, the object will be eligible for garbage collection. Circular references do not prevent objects from being garbage collected, only reachable references do. An object is not eligible for garbage collection as long as the object can be accessed by any live thread. An object that has been eligible for garbage collection can be made non-eligible. This occurs if the finalize() method of the object creates a reachable reference to the object.

9.2 (b)

Before (1), the String object initially referenced by arg1 is denoted by both msg and arg1. After (1), the String object is only denoted by msg. At (2), reference msg is assigned a new reference value. This reference value denotes a new String object created by concatenating contents of several other String objects. After (2), there are no references to the String object initially referenced by arg1. The String object is now eligible for garbage collection.

9.7 (b)

The Object class defines a protected finalize() method. All classes inherit from Object, thus, all objects have a finalize() method. Classes can override the finalize() method and, as with all overriding, the new method must not reduce the accessibility. The finalize() method of an eligible object is called by the garbage collector to allow the object to do any cleaning up before the object is destroyed. When the garbage collector calls the finalize() method, it will ignore any exceptions thrown by the finalize() method. If the finalize() method is called explicitly, normal exception handling occurs when an exception is thrown during the execution of the finalize() method, i.e., exceptions are not simply ignored. Calling the finalize() method does not in itself destroy the object. Chaining of the finalize() method is not enforced by the compiler, and it is not mandatory to call the overridden finalize() method.

9.8 (d)

The finalize() method is like any other method, it can be called explicitly if it is accessible. However, the intended purpose of the method is to be called by the garbage collector in order to clean up before an object is destroyed. Overloading the finalize() method name is allowed, but only the method with the original signature will be called by the garbage collector. The finalize() method in Object is protected. This means that overriding methods must be declared either protected or public. The finalize() method in Object can throw any Throwable object. An overridden definition of this method can throw any type of Throwable. However, overriding methods can limit the range of throwables to unchecked exceptions. Further overridden definitions of this method in subclasses will then not be able to throw checked exceptions.

9.9 (b)

The finalize() method will never be called more than once on an object, even if the finalize() method resurrects the object. An object can be eligible for garbage collection even if there are references denoting the object, as long as the objects owning these references are also eligible for garbage collection. There is no guarantee that the garbage collector will destroy an eligible object before the program terminates. The order in which the objects are destroyed is not guaranteed. The finalize() method can make an object that has been eligible for garbage collection accessible again by a live thread.

9.10 (d) and (g)

(a), (b), (c), (j), (k), (l): reduce the visibility of the inherited method. (e), (f), (h), (i): the call to the finalize() method of the superclass can throw a Throwable, which is not handled by the method. The Throwable superclass is not assignable to the Exception subclass.

9.11 (e)

It is not guaranteed if and when the garbage collection will be run, nor in which order the objects will be finalized. However, it is guaranteed that the finalization of an object will only be run once, hence (e) cannot possible be a result from running the program.

9.12 (c) and (e)

It is not guaranteed if and when the garbage collection will be run, nor in which order the objects will be finalized. So the program may not print anything. If the garbage collection is run, the MyString object created in the program may get finalized before the program terminates. In that case, the finalize() method will print A, as the string in the field str is not changed by the concat() method.

9.13 (c), (e), and (f)

The static initializer blocks (a) and (b) are not legal since the fields alive and STEP are non-static and final, respectively. (d) is not a syntactically legal static initializer block.

9.14 (c)

The program will compile, and print 50, 70, 0, 20, 0, when run. All fields are given default values unless they are explicitly initialized. Field i is assigned the value 50 in the static initializer block that is executed when the class is initialized. This assignment will override the explicit initialization of field i in its declaration statement. When the main() method is executed, the static field i is 50 and the static field n is 0. When an instance of the class is created using the new operator, the value of static field n (i.e., 0) is passed to the constructor. Before the body of the constructor is executed, the instance initializer block is executed, which assigns the values 70 and 20 to the fields j and n, respectively. When the body of the constructor is executed, the fields i, j, k, and n, and the parameter m, have the values 50, 70, 0, 20, and 0, respectively.

9.15 (f)

This class has a blank final boolean variable active. This variable must be initialized when an instance is constructed, or else the code will not compile. The keyword static is used to signify that a block is a static initializer block. No keyword is used to signify that a block is an instance initializer block. (a) and (b) are not instance initializers blocks, and (c), (d), and (e) fail to initialize the blank final variable active.

9.16 (c)

The program will compile, and print 2, 3, and 1, when run. When the object is created and initialized, the instance initializer block is executed first, printing 2. Then the instance initializer expression is executed, printing 3. Finally, the constructor body is executed, printing 1. The forward reference in the instance initializer block is legal, as the use of the field m is on the left-hand side of the assignment.

9.17 (e)

The program will compile, and print 1, 3, and 2, when run. First, the static initializers are executed when the class is initialized, printing 1 and 3. When the object is created and initialized, the instance initializer block is executed, printing 2.

9.18 (c) and (e)

Line A will cause illegal redefinition of the field width. Line B uses an illegal forward reference to the fields width and height. The assignment in line C is legal. Line D is not a legal initializer, since it is neither a declaration nor a block. Line E declares a local variable inside an initializer block, which is legal.

10 Fundamental Classes

10.1 (b)

The method hashCode() in the Object class returns a hash code value of type int.

10.2 (e)

All arrays are genuine objects and inherit all the methods defined in the Object class, including the clone() method. Neither the hashCode() method nor the equals() method is declared final in the Object() class, and it cannot be guaranteed that implementations of these methods will differentiate between all objects.

10.3 (a)

The clone() method of the Object class will throw a CloneNotSupportedException if the class of the object does not implement the Cloneable interface.

10.4 (a), (c), and (d)

The class java.lang.Void is considered a wrapper class, although it does not wrap any value. There is no class named java.lang.Int, but there is a wrapper class named java.lang.Integer. A class named java.lang.String also exists, but it is not a wrapper class since all strings in Java are objects.

10.5 (c) and (d)

The classes Character and Boolean are non-numeric wrapper classes and they do not extend the Number class. The classes Byte, Short, Integer, Long, Float, and Double are numeric wrapper classes that extend the Number class.

10.6 (a), (b), and (d)

All instances of concrete wrapper classes are immutable.

10.7 (b) and (c)

All instances of wrapper classes except Void and Character have a constructor that accepts a string parameter. The class Object has only a default constructor.

10.8 (e)

While all numeric wrapper classes have the methods byteValue(), doubleValue(), floatValue(), intValue(), longValue(), and shortValue(), only the Boolean class has the booleanValue() method. Likewise, only the Character class has the charValue() method.

10.9 (b) and (d)

String is not a wrapper class. All wrapper classes except Boolean and Void have a compareTo() method. Only the numeric wrapper classes have an intValue() method. The Byte class, like all other numeric wrapper classes, extends the Number class.

10.10 (d)

Wrapper objects are immutable, but 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 and false

• All byte values (-128, 127)

short values between -128 and 127

int values between -128 and 127

char in the range u0000 to u007f

10.11 (a)

Using the new operator creates a new object. Boxing also creates a new object if one is not already interned from before.

10.12 (b) and (e)

The operators - and & cannot be used in conjunction with a String object. The operators + and += perform concatenation on strings, and the dot operator accesses members of the String object.

10.13 (d)

The expression str.substring(2,5) will extract the substring "kap". The method extracts the characters from index 2 to index 4, inclusive.

10.14 (d)

The program will print str3str1 when run. The concat() method will create and return a new String object, which is the concatenation of the current String object and the String object given as an argument. The expression statement str1.concat(str2) creates a new String object, but its reference value is not stored.

10.15 (c)

The trim() method of the String class returns a string where both the leading and the trailing white space of the original string have been removed.

10.16 (a) and (c)

The String class and all wrapper classes are declared final and, therefore, cannot be extended. The clone() method is declared protected in the Object class. String objects are immutable and, therefore, cannot be modified. The classes String and StringBuilder are unrelated.

10.17 (a), (b), (c), and (e)

The expressions ('c' + 'o' + 'o' + 'l') and ('o' + 'l') are of type int due to numeric promotion. Expression (d) is illegal, since the String class has no constructor taking a single int parameter. Expression (a) is legal, since string literals denote String objects and can be used just like any other object.

10.18 (d)

The constant expressions "ab" + "12" and "ab" + 12 will, at compile time, be evaluated to the string-valued constant "ab12". Both variables s and t are assigned a reference to the same interned String object containing "ab12". The variable u is assigned a new String object, created by using the new operator.

10.19 (a), (c), and (d)

The String class does not have a constructor that takes a single int as a parameter.

10.20 (e)

The String class has no reverse() method.

10.21 (d)

The expression "abcdef".charAt(3) evaluates to the character 'd'. The charAt() method takes an int value as an argument and returns a char value. The expression ("abcdef").charAt(3) is legal. It also evaluates to the character 'd'. The index of the first character in a string is 0.

10.22 (e)

The expression "Hello there".toLowerCase().equals("hello there") will evaluate to true. The equals() method in the String class will only return true if the two strings have the same sequence of characters.

10.23 (c)

The variable middle is assigned the value 6. The variable nt is assigned the string "nt". The substring "nt" occurs three times in the string "Contentment!", starting at indices 2, 5, and 9. The call s.lastIndexOf(nt, middle) returns the start index of the last occurrence of "nt", searching backwards from position 6.

10.24 (b)

The reference value in the reference str1 never changes and it refers to the string literal "lower" all the time. The calls to toUpperCase() and replace() return a new String object whose reference value is ignored.

10.25 (d)

The call to the putO() method does not change the String object referred to by the s1 reference in the main() method. The reference value returned by the call to the concat() method is ignored.

10.26 (b)

The code will fail to compile since the expression (s == sb) is illegal. It compares references of two classes that are not related.

10.27 (e)

The program will compile without errors and will print have a when run. The contents of the string buffer are truncated down to 6 characters.

10.28 (a), (b), and (d)

The StringBuilder class does not have a constructor that takes an array of char as a parameter.

10.29 (a)

The StringBuilder class does not define a trim() method.

10.30 (d)

The program will construct an immutable String object containing "eeny" and a StringBuilder object containing " miny". The concat() method returns a reference value to a new immutable String object containing "eeny meeny", but the reference value is not stored. The append() method appends the string " mo" to the string buffer.

10.31 (b)

The references sb1 and sb2 are not aliases. The StringBuilder class does not override the equals() method, hence the answer is (b).

10.32 (a)

The StringBuilder class does not override the hashCode() method, but the String class does. The references s1 and s2 refer to a String object and a StringBuilder object, respectively. The hash values of these objects are computed by the hashCode() method in the String and the Object class, respectively—giving different results. The references s1 and s3 refer to two different String objects that are equal, hence they have the same hash value.

10.33 (c)

The classes String and StringBuilder are unrelated, therefore the first call to the compareTo() method will not compile. The class StringBuilder does not implement the Comparable interface, therefore the second call to the compareTo() method will not compile.

10.34 (b)

The call to the putO() method changes the StringBuilder object referred to by the s1 reference in the main() method. So does the call to the append() method.

10.35 (a)

The type of the return value from the chain of calls is StringBuilder, StringBuilder, String, and int, respectively. The string builder contents are changed to "WOW!" by the two first calls in the chain. The toString() call extracts the character sequence, which is compared with the string literal "WOW". The compareTo() method returns the value 0. The boolean expression evaluates to false.

11 Files and Streams

11.1 (a), (b), (c), and (e)

A string cannot act as the source of an input stream and as the destination of an output stream provided in the java.io package.

11.2 (b) and (e)

The separator constant is of type String and contains the sequence of characters used as path separators on a given platform. The most common platforms only use a single character as a path separator, but there is no such restriction.

11.3 (b)

The method getName() can be used on a File object to return the name of the entry excluding the specification of the directory in which the entry resides.

11.4 (e)

The length() method can be used on a File object to return the number of bytes in the file. Note that bytes are not the same as characters, and the size of characters in a file depends on the encoding scheme used.

11.5 (b) and (d)

Compiling and running the program results in the following output:

./documents/../book/../chapter1
/wrk/./documents/../book/../chapter1
/wrk/chapter1
chapter1
./documents/../book/..


11.6 (c)

The toString() method in the File class produces the same output as the getPath() method. Compiling and running the program results in the following output:

ListingFiles.class
.ListingFiles.class
.ListingFiles.class


11.7 (b)

The boolean value false is returned when the method canWrite() is called on a File object representing a file that is not writable on the file system.

11.8 (a)

The first parameter of the renameTo() method is of type File. The current file should be renamed to the file name represented by the File object. Note that the File object given does not need to represent an actual entry in the file system. It only represents a valid pathname.

11.9 (d)

The write() method writes bytes only. When given an int it only writes the 8 least significant bits.

11.10 (d)

The read() method will return -1 when the end of the stream has been reached. Normally an unsigned 8-bit int value is returned (range from 0 to 255). I/O errors result in an IOEXception being thrown.

11.11 (a) and (c)

Classes that implement the DataOutput interface, i.e., DataOutputStream and ObjectOutputStream, provide methods for writing binary representations of primitive values. The output stream classes FileOutputStream, PrintStream, and BufferedOutputStream do not provide such methods.

11.13 (a), (c), and (d)

The Writer class has no write() method with a parameter of type char. It has methods with parameters of types String, int, and char[]. The OutputStream class has a write() method with a parameter of type int, but Writer is not a subclass of OutputStream.

11.14 (d)

The default encoding for OutputStreamWriter is the default encoding of the host platform.

11.15 (a)

The byte type does not have its own print() method in the PrintWriter class. There is no natural text representation of a byte.

11.16 (c)

The standard error stream is accessed via the static variable named err in the System class.

11.17 (d)

The print() methods do not throw an IOException.

11.18 (d)

The read() method of an InputStreamReader returns -1 when the end of the stream is reached.

11.19 (a), (d), and (e)

(b) There is no stream called InputStreamWriter.

(c) The class FileOutputStream is not a Writer.

11.20 (b)

The readLine() method of a BufferedReader returns null when the end of the stream is reached.

11.21 (c)

Both the readLine() and the readPassword() method of the Console class return all characters typed on the line. The arguments of the two methods concern the prompt written to the console.

11.22 (a)

There are no methods defined in the Serializable interface. The interface is a marker interface that is used to signify that the class supports serialization.

11.23 (c)

A ObjectOutputStream can write both objects and Java primitive types, as it implements the ObjectInput and the DataInput interfaces. The serialization mechanism will follow object references and can write whole hierarchies of objects.

11.24 (d)

During deserialization, the default constructor of the superclass Person is called, because this superclass is not Serializable.

11.25 (e)

During deserialization, the default constructor of the superclass Person is called, because the superclass is not Serializable. The default constructor initializes the field name to the string "NoName".

11.26 (c)

During deserialization, the default constructor of the superclass Person is not called, because the superclass is Serializable.

11.27 (d)

The class Student is Serializable, because its superclass Person is Serializable. During deserialization, the default constructor of the superclass Person is not called because the superclass is Serializable. But the field name is transient, and therefore not serialized.

11.28 (d)

During serialization of a Student object, the string "NewName" is also serialized. During deserialization, this string is read and assigned to the transient field name which had not been serialized.

11.29 (e)

Note that only GraduateStudent is Serializable. The field name in the Person class is transient. During serialization of a GraduateStudent object, the fields year and studNum are included as part of the serialization process, but not the field name. During deserialization, the private method readObject() in the GraduateStudent class is called. This method first deserializes the GraduateStudent object, but then initializes the fields with new values.

11.30 (d)

The field name in the Person class is transient, and the field numOfStudents in the Student class is static. During serialization of a Student object, neither of these fields are serialized. After deserialization, the value of the field name is null, but the value of the static field numOfStudents has been incremented because a second Student object has been created.

12 Localization, Pattern Matching, and Formatting

12.1 (c)

It is the information in the current locale which is formatted according to the locale that is passed as argument. Note that the get methods are not called on the same locale in printLocaleInfo().

12.2 (b) and (d)

The Date class does not have any locale-sensitive methods. A negative argument in the non-default constructor indicates a negative offset from the epoch, i.e., a time before the epoch.

12.3 (d) and (f)

(d) The roll() method does not recompute and normalize the larger fields.

(f) The first month of the year is 0, and not 1.

12.4 (e)

(a), (b) DateFormat is an abstract class and, therefore, cannot be instantiated.

(c), (d) The method getDateTimeInstance() requires either no formatting styles or two formatting styles with an optional locale.

(d) The arguments are not applicable to the format() method.

12.5 (g)

(a) The input string "Mar 7, 2008" is not compatible with the formatting style DateFormat.SHORT of the formatter, resulting in an exception.

Leading whitespace and trailing characters are ignored, and values are parsed and normalized in all other cases. The alternatives from (a) to (f) give the following output, respectively:

Fri Mar 07 00:00:00 EST 2008
Fri Mar 07 00:00:00 EST 2008
Sun Apr 06 00:00:00 EDT 2008
Wed Jan 07 00:00:00 EST 2009
Fri Mar 07 00:00:00 EST 2008
Fri Mar 07 00:00:00 EST 2008


12.6 (f)

(f) The method parseNumber() does not catch the ParseException that can be thrown by the parse() method. The parse() method returns a Number, which is not assignment compatible to a reference of type Double. If these errors are corrected, the alternatives from (a) to (e) give the following outputs, respectively:

1234.567
0.567
1234
1234.567
1


12.7 (b), (c), and (d)

The expression x+ means one or more occurrences of x in the target string. The operator is greedy and, therefore, will match as many contagious occurrences of x as possible in the target. The regular expression a+ will match 3 substrings (aa, a, aa) of the target string. The regular expressions aa+ and (aa)+ will match the same 2 substrings of this target string, i.e., (aa, aa). However, these regular expressions would give different results if the target was "aaa". The first one will match the whole target, while the second one will only match the first two as.

12.8 (a), (c), and (d)

The expression x? means 0 or 1 occurrence of x in the target string. The regular expression a? will match 5 non-empty substrings corresponding to the 5 as in the target string. The regular expressions aa? and (aa)? will not match the same substrings of this target string. The regular expression aa? will match the 3 non-empty substrings (aa, a, aa) of the target string, whereas the regular expression (aa)? will match the 2 non-empty substrings (aa, aa) of the target string.

12.9 (e)

The expression x* means 0 or more occurrences of x in the target string. The regular expression a* will match the 3 non-empty substrings aa, a, aa in the target string. The regular expression aa* will also match the same 3 non-empty substrings aa, a, aa in the target string. The regular expression (aa)* will only match the 2 non-empty substrings aa and aa in the target string.

12.10 (e)

The pattern d matches a digit, not all integers. The pattern d will match 0, 5, 7, and 4 in the target string. The pattern w matches a word character (i.e., letters, digits and _), not a word. The pattern w will match 0, 5, 7, U, P, _, 4, m, and e in the target string. The pattern s matches a white space character (i.e., space (' '), tabulator ( ), and newline ( )), not a string. The pattern s will match each of the two spaces in the target string. The pattern . will match any character in the target string. The operators [] specifies a set of characters to match, not strings to match. The regular expression [meUP] will match the characters U, P, m, and e in the target string.

12.11 (c) and (e)

To escape any metacharacter in regexes, we need to specify two backslashes (\) in Java strings. Note that a backslash () is used to escape metacharacters in both Java strings and in regular expressions.

In (1), the compiler reports that d is an invalid escape sequence in a Java string.

(2) will throw a java.util.regex.PatternSyntaxException: dangling metacharacter +, which is the first character in the regex, is missing an operand.

In (3), the compiler reports that + is an invalid escape sequence. (4) will match a positive integer with an optional + sign, printing true.

The method Pattern.matches() returns true if the regex matches the entire input.

12.12 (d), (e), (f), and (g)

(a), (b) A java.util.regex.PatternSyntaxException is thrown because the first + in the regex is a dangling metacharacter.

(c) The regex does not match the target "2007", since a sign is required.

The regex (-|\+)? and [-+]? are equivalent: 0 or 1 occurrence of either - or +. Note that in the [] regex notation, the + sign is an ordinary character as in (e) and (f), and escaping it as in (g) is not necessary. The metacharacter d is equivalent to [0-9], i.e., any digit.

12.13 (d)

The Pattern compiles a regular expression and the matcher applies it to the target. Whether the next match exists is given by the method find(). The matched input is returned by the method group().

12.14 (c)

The no-argument reset() method can be used to reset the matcher to the beginning of the current target. That is why the matching against the first target is repeated in the output. The same matcher can be applied to different targets by passing the new target to the reset() method.

12.15 (d)

Each sequence of white space is replaced by a single space (' '), including the newline ( ) character.

12.16 (b)

The application of a* and a+ is greedy in (1) and (2). A greedy quantifier reads the whole target string and backtracks to match as much of the input as possible.

The application of a*? and a+? is reluctant in (3) and (4). A reluctant quantifier only reads enough of the input to match the regular expression. Note that the substring "Xz" is not matched by X.+?z, as it requires non-empty input between X and z.

12.17 (b)

The regular expression [a-zA-Z0-9_]+ will match any permutation of letter, digit, or the '_' character. But only the first character of the match is converted to uppercase. A match that starts with any other character than a lowercase letter is not affected. The appendReplacement() method implements what is called a non-terminal append-and-replace step. The appendTail() method implements what is called a terminal append-and-replace step. The net result is that the buffer contains the modified input after the matcher has exhausted the input.

12.18 (d)

Note that the dot (.) does not need escaping, as it loses its metacharacter meaning in a [] pair. On the other hand the - character is a metacharacter for specifying an interval in a [] pair and needs escaping in order to match it in the input.

12.19 (b), (c), and (d)

Constructors of the Scanner class accept neither a StringBuffer nor a StringBuilder. A Readable is accepted, and all Readers implement the Readable interface. When the scanner cannot match the next token, it actually throws a InputMismatchException, which is a subtype of NoSuchElementException.

12.20 (e), (f), and (h)

The print statement in (e) will print 99.0, as the token is converted to a double value. The method call lexer.nextBoolean() in (f) will throw an InputMismatchException, as the first token cannot be converted to a boolean value. The method call lexer.nextLine() in (h) will read whatever is remaining in the input, in this case, 99 2007 true 786.

Note that the method call lexer.next() in (g) reads the first token as 99, using default delimiters.

12.21 (c) and (e)

In (c), the dot (.) is a metacharacter, thus matching any character. Splitting the input on the string pattern in (c) returns the empty string. Splitting the input on the string pattern in (e) returns the following tokens: [A, 2, BCDE, , 6, , F, 8]. Note the empty strings returned which cause input mismatch.

In (a), it is not necessary to escape the dot (.) inside a [] pair. The string pattern "[0.]+" is more general than the string pattern "0+\.*0*". The regexes (a|b|c) and [abc] are equivalent.

Note that each iteration of the while loop advances the scanner with two tokens in the input, corresponding to the calls of the next methods.

12.22 (d)

Note that the dot (.) needs escaping in the string pattern which recognizes a non-empty sequence of word characters that end in a dot (.).

12.23 (f)

The regexes will produce the following output, respectively, showing what is matched and not matched by the regex:

 1234,  567.,X12.34,  X.56,   78.,    X.,
 1234, X567.,X12.34,   .56,  X78.,    X.,
X1234, X567., 12.34,  X.56,  X78.,    X.,
 1234,  567., 12.34,   .56,   78.,     .,
 1234,  567., 12.34,  X.56,   78.,    X.,
 1234,  567., 12.34,   .56,   78.,    X.,


Note that the regexes in (d) will match a dot (.) as well.

12.24 (a)

The delimiters of a scanner can be changed during tokenizing. When setting new delimiters one should take into consideration the delimiter that terminated the previous token, as this must be skipped one way or another in order to find the next token.

12.25 (f)

The string pattern of the hasNext() method will only match the tokens "Trick" and "treat", but "Trick!" will be printed on recognizing these tokens. The call to the next() method in the loop will skip the current token in the input. Without this call, the program will go into an infinite loop.

12.26 (c), (e), and (h)

The alternatives print the following lines, respectively, when we exclude (c), (e), and (h):

|2007| -25.0|mp3 4 u | true| after8|
|2007.0|-25.0|
|2007|25|0|3|4|8|
|mp|u|true|after|
|2007.0|25.0|0.0|mp3|4.0|u|true|after8|
|4|


(c) does not skip the input that did not match a double value, in order for the scanner to find the next token, and the program, therefore, goes into an infinite loop.

(e) checks for a boolean value, but tries to read a double instead, resulting in a java.util.InputMismatchException being thrown.

(h) does not skip the input that did not match an int value, in order for the scanner to find the next token, and the program therefore goes into an infinite loop.

12.27 (a), (c), (d), and (f)

The classes java.util.StringBuilder and java.util.Scanner do not provide the format() method. The Console class only provides the first form.

12.28 (c) and (d)

Only the classes java.util.PrintStream and java.util.PrintWriter provide the printf() method. The Console class only provides the first form.

12.29 (c) and (d)

The conversion 'd' cannot be applied to char or Character values. The flag combination '(+-' is also valid.

12.30 (e)

In (a) the resulting string is created by String.format() and printed by System.out. A PrintStream (such as System.out) can format and print to its output stream. A Formatter can be connected to an Appendable (which is implemented by the StringBuilder) as the destination for the formatted output. The StringBuilder is then printed to the terminal window. A Formatter can also be connected to a PrintStream (in this case System.out) which is the destination of the formatted output.

12.31 (e)

All the three classes PrintStream, PrintWriter, and Formatter have the format() method that writes to the underlying output stream, which is created or provided depending on the constructor argument.

12.32 (a) and (b)

First, note that the Object[] array oneDimArray is passed as varargs Object... to the format() method.

(a) In the format %4$-1c, -1 is superfluous if we only want to print 1 character. 4$ here indicates the fourth value in the oneDimArray that represents a row in the twoDimArray. %5s implicitly refers to the first value in the oneDimArray, which is printed with a width of 5 characters right-justified.

In %5$1.1b, %5$ indicates the fifth value (boolean) in the oneDimArray. The precision .1 indicates that only the first character should be printed. The width of 1 is superfluous in 1.1.

%(+8.2f corresponds to the second value (float) in the oneDimArray, indicating that negative values should be enclosed in () and positive values should have the + sign. The width is 8 and the precision is 2 decimals.

%6d corresponds to the third value (int) in the oneDimArray, indicating that the width is 6 characters.

(b) The superfluous -1 in (a) is missing in (b) for formatting a character. The int value is formatted with %6s, giving the same result as %6d in (a).

(c) In the format specification %2$(+-8.2f, the - sign indicates that the value should be printed left-justified, not right-justified as in the output. The format %3$6s in (c) only indicates the third argument (int value) explicitly.

(d) Arguments are made explicit. The format %3$,6d specifies the locale-specific grouping separator (,), which is used for the value 5151, formatting it as 5,151. This is not so in the output.

12.33 (c)

The program will use the following formats %(,+-1.1, %(,+-2.2, and %(,+-3.3 to print the values in the array dArray. The flag '(' results in negative values being enclosed in (). The flag ',' specifies that the locale-specific grouping character should be used. The flag '+' specifies that positive values should be prefixed with the + sign. The flag '-' specifies that values should be printed left-justified. The width is less than the required width, and therefore overridden. The precision can result in rounding, as is the case for the first two values.

12.34 (d)

The respective exceptions thrown by the statements above are:

java.util.MissingFormatWidthException (requires positive width)

java.util.UnknownFormatConversionException (because of width 0)

java.util.IllegalFormatFlagsException (because of the flag combination '( +-')

java.util.MissingFormatArgumentException (the second format has no matching argument)

java.util.UnknownFormatConversionException ('!' is an unknown flag)

12.35 (b) and (d)

(b) will throw a java.util.IllegalFormatPrecisionException because of the precision specification. (d) will throw a java.util.FormatFlagsConversionMismatchException because of the + flag.

The other statements will produce the following output, respectively, excluding (b) and (d): |L        |, |!|, |h|, |! |, |null|, | V|.

12.36 (h)

The statements will produce the following output, respectively:

| (123) | |+123 | | (00123) | | (123) | |-123 | |-123 | | 123 |


12.37 (i)

(a) will throw a java.util.MissingFormatWidthException. The width is not specified.

(b) will throw a java.util.IllegalFormatPrecisionException. The precision cannot be 0.

The remaining statements will throw a java.util.IllegalFormatConversionException. The conversion d cannot be used with the specified argument.

12.38 (a), (d), and (e)

All the loops format the value true.

(a), (d), and (e) use the format strings %2.1b, %4.2b, %6.3b, and %8.4b, resulting in the output:

| t| tr| tru| true|


(b) uses the format strings %.0b, %2.1b, %4.2b, %6.3b, and %8.4b, resulting in the output:

|| t| tr| tru| true|


(c) uses the format strings %2.0b, %4.1b, %6.2b, and %8.3b, resulting in the output:

| | t| tr| tru|


12.39 (b)

(a) will print: |1000| 10|0100|

(b) will print: |1000|+10 | 100|

(c) will print: | +10| 100|1000|

(d) will throw a java.util.IllegalFormatConversionException, as intArray is passed as new Object[] { intArray } to the printf() method. An array is certainly not an int. Remember that int[] is not a subtype of Object[], but Integer[] is.

12.40 (f)

Rows in the output are printed according to the following formats:

%5.0f, %5.1f, %5.2f
%6.0f, %6.1f, %6.2f
%7.0f, %7.1f, %7.2f
%8.0f, %8.1f, %8.2f
%9.0f, %9.1f, %9.2f


These formats result in the output shown in (a) to (e), respectively.

13 Threads

13.1 (c)

Create a new Thread object and call the method start(). The call to the start() method will return immediately and the thread will start executing the run() method asynchronously.

13.2 (c)

When extending the Thread class, the run() method should be overridden to provide the code executed by the thread. This is analogous to implementing the run() method of the Runnable interface.

13.3 (b) and (e)

The Thread class implements the Runnable interface and is not abstract. A program terminates when the last user thread finishes. The Runnable interface has a single method named run. Calling the run() method on a Runnable object does not necessarily create a new thread; the run() method is executed by a thread. Instances of the Thread class must be created to spawn new threads.

13.4 (e)

The program will compile without errors and will simply terminate without any output when run. Two thread objects will be created, but they will never be started. The start() method must be called on the thread objects to make the threads execute the run() method asynchronously.

13.5 (b)

(1) results in the run() method of the Extender class being called, which overrides the method from the Thread class, as does (2). (3) results in the run() method of the Implementer class being called.

Invoking the start() method on a subclass of the Thread class always results in the overridden run() method being called, regardless of whether a Runnable is passed in a constructor of the subclass.

13.6 (d)

Note that calling the run() method on a Thread object does not start a thread. However, the run() method of the Thread class will invoke the run() method of the Runnable object that is passed as argument in the constructor call. In other words, the run() method of the R1 class is executed in the R2 thread, i.e., the thread that called the run() method of the Thread class.

13.7 (c)

Note that the complete signature of the run() method does not specify a throws clause, meaning it does not throw any checked exceptions. However, it can always be implemented with a throws clause containing unchecked exceptions, as is the case in the code above.

13.8 (d)

The call to the run() method just executes the method in the main thread. Once a thread has terminated, it cannot be started by calling the start() method as shown above. A new thread must be created and started.

13.9 (a) and (e)

Because the exact behavior of the scheduler is undefined, the text A, B, and End can be printed in any order. The thread printing B is a daemon thread, which means that the program may terminate before the thread manages to print the letter. Thread A is not a daemon thread, so the letter A will always be printed

13.10 (b) and (d)

We cannot synchronize on a primitive value. Synchronizing on a local object is useless, as each thread will create its own local object and it will not be a shared resource.

13.11 (b), (c), and (d)

The lock is also released when an uncaught exception occurs in the block.

13.12 (a)

No two threads can concurrently execute synchronized methods on the same object. This does not prevent one thread from executing a non-synchronized method while another thread executes a synchronized method on the same object. The synchronization mechanism in Java acts like recursive semaphores, which means that during the time a thread owns the lock, it may enter and re-enter any region of code associated with the lock, so there is nothing wrong with recursive synchronized calls. Synchronized methods can call other synchronized and non-synchronized methods directly.

13.13 (b)

One cannot be certain whether any of the letters i, j, and k will be printed during execution. For each invocation of the doIt() method, each variable pair is incremented and their values are always equal when the method returns. The only way a letter could be printed would be if the method check() was executed between the time the first and the second variable were incremented. Since the check() method does not depend on owning any lock, it can be executed at any time, and the method doIt() cannot protect the atomic nature of its operations by acquiring locks.

13.14 (c) and (d)

First note that a call to sleep() does not release the lock on the Smiley.class object once a thread has acquired this lock. Even if a thread sleeps, it does not release any locks it might possess.

(a) does not work, as run() is not called directly by the client code.

(b) does not work, as the infinite while loop becomes the critical region and the lock will never be released. Once a thread has the lock, other threads cannot participate in printing smileys.

(c) works, as the lock will be released between each iteration, giving other threads the chance to acquire the lock and print smileys.

(d) works for the same reason as (c), since the three print statements will be executed as one atomic operation.

(e) may not work, as the three print statements may not be executed as one atomic operation, since the lock will be released after each print statement.

Synchronizing on this does not help, as the printout from each of the three print statements executed by each thread can be interspersed.

13.15 (d)

A thread dies when the execution of the run() method ends. The call to the start() method is asynchronous, i.e., it returns immediately, and it moves the thread to the Ready-to-run state. Calling the sleep() or wait() methods will block the thread.

13.16 (b) and (d)

The inner createThread() call is evaluated first, and will print 23 as the first number. The last number the main thread prints is 14. After the main thread ends, the thread created by the inner createdThread() completes its join() call and prints 22. After this thread ends, the thread created by the outer createThread() call completes its join() call and prints the number 12 before the program terminates. Note that in the inner call to the createThread() method, the thread t2 can start to execute before this call finishes, resulting in 21 being printed before 24.

13.17 (b), (c), and (f)

The wait() and notify() methods of the Object class can only be called on an object whose lock the thread holds, otherwise a java.lang.IllegalMonitorStateException is thrown. The static method yield() of the class Thread does not throw any exceptions. Both the sleep() and join() methods of the Thread class throw an InterruptedException.

13.18 (d) and (f)

(a) and (b) result in a java.lang.IllegalMonitorStateException, as the t1 thread does not hold a lock on the current object, i.e., on the thread itself.

(c) The t1 thread will wait forever, as it never gets any notification, and the main thread also waits forever for t1 to complete.

(e) The yield() method does not throw the checked InterruptedException, and the compiler reports an error as the code in the catch block is unreachable.

13.19 (e)

The exact behavior of the scheduler is not defined. There is no guarantee that a call to the yield() method will grant other threads use of the CPU.

13.20 (b)

The final method notify() is defined in the Object class.

13.21 (a)

The priority of a thread is set by calling the setPriority() method in the Thread class. No Thread constructor accepts a priority level as an argument.

13.22 (a) and (c)

A thread can hold multiple locks; e.g., by nesting synchronized blocks. Invoking the wait() method on an object whose lock is held by the current thread will relinquish the lock for the duration of the call. The notify() method does not relinquish any locks.

13.23 (c)

An IllegalMonitorStateException will be thrown if the wait() method is called when the current thread does not hold the lock of the object.

13.24 (a), (c), (d), and (e)

The thread terminates once the run() method completes execution.

13.25 (d)

Since the two methods are mutually exclusive, only one operation at a time can take place on the tank that is a shared resource between the two threads.

The method emptying() waits to empty the tank if it is already empty (i.e., isEmpty is true). When the tank becomes full (i.e., isEmpty becomes false), it empties the tank and sets the condition that the tank is empty (i.e., isEmpty is true).

The method filling() waits to fill the tank if it is already full (i.e., isEmpty is false). When the tank becomes empty (i.e., isEmpty becomes true), it fills the tank and sets the condition that the tank is full (i.e., isEmpty is false).

Since the tank is empty to start with (i.e., isEmpty is true), it will be filled first. Once started, the program will continue to print the string "filling" followed by the string "emptying".

Note that the while loop in the pause() method must always check against the field isEmpty.

13.26 (e)

The runner thread can only proceed if intArray[0] is not 0. If this element is not 0, it has been initialized to 10 by the main thread. If this element is 0, the runner thread is put into the wait set of the intArray object, and must wait for a notification. The main thread only notifies after initializing both elements of the array to 10. Calling the notify() method on an object with no threads in its wait set does not pose any problems. A thread can only call notify() on an object whose lock it holds. Therefore, the last synchronized block in the main() method is necessary.

14 Generics

14.1 (b)

The type of lst is List of Integer and the type of numList is List of Number. The compiler issues an error because List<Integer> is not a subtype of List<Number>.

14.2 (f)

Assigning bList, a reference of a non-generic list, to oList, a reference of a generic list, in (2) results in an unchecked conversion warning.

14.3 (c)

We can only get an Object from a List<? super Integer>. The list could contain Comparable objects, and Number does not implement Comparable.

14.4 (b)

The compiler issues an unchecked conversion warning in (1), as we are assigning a raw list to a generic list.

14.5 (b), (f), and (g)

We cannot refer to the type parameters of a generic class in a static context, e.g., in static initializer blocks, static field declarations, and as types of local variables in static methods. Also we cannot create an array of a type parameter, as in (2).

14.6 (e)

Any generic list can be assigned to a raw list reference. A raw list and an unbounded wildcard list are assignment compatible.

14.7 (b), (c), (e), and (f)

In (b), (c), (e), and (f), the parameterized type in the object creation expression is a subtype of the type of the reference. This is not the case in (a): just because HashMap<Integer, String> is a subtype of Map<Integer, String>, it does not follow that HashMap<Integer, HashMap<Integer, String>> is a subtype of Map<Integer, Map<Integer, String>>—there is no subtype covariance relationship between concrete parameterized types. In (d) and (g), wild cards cannot be used to instantiate the class.

14.8 (b)

ArrayList<Fruit> is not a subtype of List<? extends Apple>, and ArrayList<Apple> is not a subtype of List<? super Fruit>. Any generic list can be assigned to a raw list reference. A raw list and an unbounded wildcard list are assignment compatible.

14.9 (a), (b), (c), and (e)

(a), (b) and (c) The compiler will report unchecked call warnings.

(d) Incompatible types, assigning type Object to type String.

(e) From any list, we are guaranteed to get an Object.

14.10 (b)

(a) Primitive types are not permitted as type parameters.

(c) String is not a generic class.

(d) The interface Map cannot be instantiated.

(e) The method call accounts.getNum(name) returns an Object, which cannot be converted to a Long.

14.11 (d)

The compiler issues unchecked warnings for calls to the add() method. The TreeSet class orders elements according to their natural ordering. A ClassCastException is thrown at runtime, as Strings are not comparable to Integers.

14.12 (a) and (b)

The type of reference g is of raw type Garage. We can put any object in such a garage, but only get Objects out. The type of value returned by the get() method in statements (6) - (8) is Object and, therefore, not compatible for assignment to Vehicle, Car, and Sedan.

14.13 (b) and (c)

The type of reference g is of Garage<Car>. We can put a Car (or its subtype) in such a garage and get a Car (or its supertype) out. The type of value returned by the get() method in statement (8) is Car and, therefore, not compatible for assignment to Sedan.

14.14 (c)

The type of reference g is of Garage<?>. We cannot put any object in such a garage, and can only get an Object out. The type of value returned by the get() method in statements (6) - (8) is (capture-of ?), i.e., some unknown type and, therefore, not compatible for assignment to Vehicle, Car, and Sedan, as the value might turn out to be of a totally unrelated type. For more details on wildcard capture, see Section 14.9, p. 705.

14.15 (c)

The type of reference g is of type Garage<? extends Car>. We cannot put any car in such a garage, and can only get a Car (or its supertype) out. The type of value returned by the get() method in statement (8) is (capture-of ? extends Car), i.e., some unknown subtype of Car and, therefore, not compatible for assignment to Sedan, as the value might turn out be of a type totally unrelated to Sedan. For more details on wildcard capture, see Section 14.9, p. 705.

14.16 (b) and (c)

The type of reference g is of type Garage<? super Car>. We can put a Car (or its subtype) in such a garage, but can only get Objects out. The type of value returned by the get() method in statements (6)-(8) is (capture-of ? super Car), i.e., some supertype of Car and, therefore, not compatible for assignment to Vehicle, Car, and Sedan, as it might be a value of a supertype of Vehicle, Car, or Sedan, respectively. For more details on wildcard capture, see Section 14.9, p. 705.

14.17 (b), (d), and (e)

(a) R cannot be resolved.

(b) Ok.

(c) The ClassA is parameterized with only one type parameter.

(d) Ok.

(e) Ok. The declaration says that the parameterized type ClassA<Number> is the supertype of ClassF<U> for any concrete type U that satisfies the constraint U extends Comparable<U> & Serializable. In the code below, String satisfies the constraint. However the target in the first declaration is of the supertype ClassA<Number>, but this is not the case in the second declaration.

ClassA<Number> ref1 = new ClassF<String>(); // OK
ClassA<Integer> ref2 = new ClassF<String>();// Not OK


(f) The keyword implements cannot be used to specify bounds.

(g) A wildcard cannot be used to specify the type parameter for the superclass.

(h) String is Comparable<String>, therefore, Comparable cannot be implemented more than once with different arguments, i.e., as a bound for U.

(i) From the declaration, superclass ClassA<Integer> implements Comparable<Integer>. Subclass ClassJ thus implements Comparable<Integer>. However, Comparable cannot be implemented more than once with different arguments, i.e., as Comparable<Number> in the declaration.

14.18 (a) and (e)

In (b) and (c), we cannot add any object to the list as the list is of an unknown type. From a list of an unknown type, we are only guaranteed to get an Object. In (d) capture-of ? cannot be converted to String.

14.19 (b)

It is the fully qualified name of the class after erasure that is printed at runtime. Note that it is the type of the object, not the reference, that is printed. The erasure of all the lists in the program is ArrayList.

14.20 (b) , (e), and (f)

(a) and (d) Incompatible types for assignment: cannot convert from List<Integer> to ArrayList<Integer>.

(c) Incompatible types for assignment: cannot convert from List<Integer> to List<Number>.

14.21 (e)

(a) Incompatible types for assignment in the main() method: cannot convert from Collection<capture-of ? extends CharSequence> to Collection<String>.

(b) Incompatible return value in the delete4LetterWords() method: cannot convert from Collection<E> to List<E>.

(c) In the for(:) loop, the component type of words (capture-of ? CharSequence) cannot be converted to E.

(d) In the for(:) loop, the component type of words (capture-of ? CharSequence) cannot be converted to E. Incompatible return value in the delete4LetterWords() method: cannot convert from Collection<E> to List<E>.

(e) OK.

(f) Keyword super cannot be used in a constraint. It can only be used with a wildcard (?).

14.22 (c)

(a) Cannot instantiate the interface List.

(b) and (d) The method call ds.add(row) expects a list with element type ArrayList<Integer>, but row has the type List<Integer>.

(e) Incompatible types for assignment: cannot convert from ArrayList<ArrayList<Integer> to List<List<Integer>.

(f) The interface List requires a single type parameter, and it cannot be instantiated.

(g), (h) Both the interface List and the class ArrayList require a single type parameter.

14.23 (b) and (f)

After erasure, the method at (1) has the signature overloadMe(List, List). Since all methods are declared void, they must differ in their parameter list after erasure in order to be overloaded with the method at (1). All methods have different parameter lists from that of the method at (1), except for the declarations (b) and (f). In other words, all methods have signatures that are not override-equivalent to the signature of the method at (1), except for (b) and (f).

If one considers the declarations (a) and (e) on their own, these two methods have the same signature overloadMe(Collection, List) after erasure and, therefore, would not be overloaded, i.e., they would be override-equivalent.

14.24 (c) and (d)

(a) The method appendAndWrite(Collection<T>, T) cannot be applied to the inferred argument type (List<capture-of#n ?>, String). We cannot add to a collection of type Collection<?>.

(b) The method appendAndWrite(Collection<T>, T) cannot be applied to the inferred argument type (List<capture-of#m ? extends Object>, String). We cannot add to a collection of type Collection<? extends Object>.

(c) The method appendAndWrite(Collection<T>, T) can be applied to the inferred argument type (List<capture-of#k ? super Object>, String). T is Object. We can add any object to a collection of type Collection<? super Object>.

(d) The method appendAndWrite(Collection<T>, T) can be applied to the inferred argument type (List<Object>, String). T is Object. We can add any object to a collection of type Collection<Object>.

14.25 (b)

Passing a raw list to either a list of Integers or to a list of type parameter T is not type-safe.

14.26 (d), (e), and (f)

(a) The arguments in the call are (List<Number>, List<Integer>). No type inferred from the arguments satisfies the formal parameters (List<? extends T>, List<? super T>).

(b) The arguments in the call are (List<Number>, List<Integer>). The actual type parameter is Number. The arguments do not satisfy the formal parameters (List<? extends Number>, List<? super Number>). List<Number> is a subtype of List<? extends Number>, but List<Integer> is not a subtype of List<? super Number>.

(c) The arguments in the call are (List<Number>, List<Integer>). The actual type parameter is Integer. The arguments do not satisfy the formal parameters (List<? extends Integer>, List<? super Integer>). List<Number> is not a subtype of List<? extends Integer>, although List<Integer> is a subtype of List<? super Integer>.

(d) The arguments in the call are (List<Integer>, List<Number>). The inferred type is Integer. The arguments satisfy the formal parameters (List<? extends Integer>, List<? super Integer>).

(e) The arguments in the call are (List<Integer>, List<Number>). The actual type parameter is Number. The arguments satisfy the formal parameters (List<? extends Number>, List<? super Number>).

(f) Same reasoning as in (d), but the actual type parameter is explicitly specified in the method call.

14.27 (e)

The methods in (1), (2), and (3) are not generic, but the methods in (4) and (5) are. A generic method need not be declared in a generic class. Regardless of what type an object has, it is still an Object.

14.28 (a) and (b)

(a) The class uses the correct syntax to declare the constructors.

(b) The constructors in (2) and (3) have the same erasure and, therefore, only one of them can be declared, i.e., we have a name clash. The compiler reports an error.

(c) A generic class can declare generic constructors, as in (3) and (4).

(d) A type parameter declared by the class can be ignored in the class body.

14.29 (h)

(a) Invokes the default constructor at (1).

(b) Invokes the constructor at (2) with T as String and V as String.

(c) Invokes the constructor at (2) with T as Integer and V as String.

(d) Invokes the constructor at (2) with T as Integer and V as Integer.

(e) Invokes the constructor at (2) with T as String and V as String, same as (b).

(f) Invokes the constructor at (3) with T as Integer and V as String. The constructor requires parameters (Integer, String), which is compatible with the arguments (Integer, String) in the constructor call.

(g) Invokes the constructor at (3) with T as Integer and V as String, same as (f).

(h) T is Integer and V is String. The constructor requires parameters (Integer, String), which is not compatible with the arguments (String, Integer) in the constructor call.

14.30 (a), (e), and (i)

The erasure of the signature of (2) is different from the erasure of the signature of (1), i.e., overloaded, since signatures are not override-equivalent. Therefore, of the three alternatives (a), (b), and (c), only (a) is correct.

The signature of (3) is the same as the erasure of the signature of (1), i.e., overridden. Therefore, of the three alternatives (d), (e), and (f), only (e) is correct.

The erasure of the signature of (5) is the same as the signature of (4), and not the other way around, i.e., name clash. Therefore, of the three alternatives (h), (i), and (j), only (i) is correct.

14.31 (c), (f), (i), and (k)

The type parameter N in SubC1 does not parameterize the supertype SupC. The erasure of the signature of (3) is the same as the erasure of the signature of (1), i.e., name clash. Therefore, of the three alternatives (a), (b), and (c), only (c) is correct.

The type parameter N in SubC1 cannot be guaranteed to be a subtype of the type parameter T in SupC, i.e., incompatible return types for get() methods at (4) and (2). Also, methods cannot be overloaded if only return types are different. Therefore, of the three alternatives (d), (e), and (f), only (f) is correct.

The type parameter N in SubC2 is a subtype of the type parameter M which parameterizes the supertype SupC. The erasure of the signature of (5) is still the same as the erasure of the signature of (1), i.e., name clash. Therefore, of the three alternatives (g), (h), and (i), only (i) is correct.

The type parameter N in SubC1 is a subtype of the type parameter T (through M) in SupC, i.e., covariant return types for the get() methods at (6) and (2), which are overridden. Therefore, of the three alternatives (j), (k), and (l), only (k) is correct.

14.32 (a), (c), and (e)

(a) An enum type and its enum values are static. Since type parameters cannot be used in any static context, the parameterization of an enum type would be nonsense.

(c) Generic exceptions or error types are not allowed, because the exception handling mechanism is a runtime mechanism and the JVM is oblivious of generics.

(e) Anonymous classes do not have a name, but a class name is needed for declaring a generic class and specifying its type parameters.

14.33 (c)

The type parameter E in the class Tantrum has the upper bound Exception, and the method throwOne() can throw an exception that is a subtype of Exception.

The generic Tantrum class is instantiated correctly in the main() method, as is the non-generic class TantrumException that is a subtype of Exception.

14.34 (d)

Casts are permitted, as in (2)-(6), but can result in an unchecked warning. The assignment in (5) is from a raw type (List) to a parameterized type (List<Integer>), resulting in an unchecked assignment conversion warning. Note that in (5) the cast does not pose any problem. It is the assignment from generic code to legacy code that can be a potential problem, flagged as an unchecked warning.

In (6), the cast is against the erasure of List<Integer>, that is to say, List. The compiler cannot guarantee that obj is a List<Integer> at runtime, it therefore flags the cast with an unchecked warning.

Only reifiable types in casts do not result in an unchecked warning.

14.35 (e)

Instance tests in the scuddle() method use the reified type List<?>. All assignments in the main() method are type-safe.

14.36 (e), (f), (g), and (h)

The correct answers all create arrays that have a component type that is reifiable, and the assignment types are compatible.

(a) Cannot instantiate a type parameter.

(b) Cannot create an array whose component type is a type parameter.

(c) Cannot create a generic array of List<T>, as List<T> is not reifiable type..

(d) Cannot create an array of a type parameter.

(i) Unchecked assignment conversion warning, as the assignment is from a non-generic type to a generic type.

14.37 (c)

Erasure of E[] in the method copy() is Object[]. The array type Object[] is actually cast to Object[] at runtime, i.e., an identity cast. The method copy() returns an array of Object. In the main() method, the assignment of this array to an array of Strings results in a ClassCastException.

14.38 (e)

The method header in (1) is valid. The type of the varargs parameter can be generic. The type of the formal parameter aols is an array of Lists of T. The method prints each list.

The main() method in (2) can be declared as String..., as it is equivalent to String[].

The statement at (3) creates an array of Lists of Strings. The type parameter T is inferred to be String in the method call in (4).

15 Collections and Maps

15.1 (b) and (d)

It is recommended that (a) is fulfilled, but it is not a requirement. (c) is also not required, but such objects will lead to collisions in the hash table, as they will map to the same bucket.

15.2 (a), (b), (d), and (h)

(c) is eliminated since the hashCode() method cannot claim inequality if the equals() method claims equality. (e) and (f) are eliminated since the equals() method must be reflexive, and (g) is eliminated since the hashCode() method must consistently return the same hash value during the execution.

15.3 (b), (d), and (e)

(a) and (c) fail to satisfy the properties of an equivalence relation. (a) is not transitive and (c) is not symmetric.

15.4 (a) and (e)

(b) is not correct since it will throw an ArithmeticException when called on a newly created Measurement object. (c) and (d) are not correct since they may return unequal hash values for two objects that are equal according to the equals() method.

15.6 (a), (d), and (e)

Set, Collection, and Map are core interfaces in the collections framework. LinkedList is a class that implements the List interface. There is no class or interface named Bag.

15.7 (b) and (e)

The java.util package provides map implementations named HashMap and TreeMap. It does not provide any implementations named HashList, ArraySet, and ArrayMap.

15.8 (d)

The List interface is implemented by collections that maintain sequences of possibly non-unique elements. Elements retain their insertion ordering in the sequence. Collection classes implementing SortedSet only allow unique elements that are maintained in a sorted order.

15.9 (a), (c), and (f)

Only methods in (a), (c), and (f) are in the Iterator<E> interface.

15.10 (a), (b), (c), (d), and (g)

With blanks filled in:

Collection<Integer> myItems = new ArrayList<Integer>();
myItems.add(9); myItems.add(1); myItems.add(1);

Iterator<Integer> iterator = myItems.iterator();
while (iterator.hasNext()) {
  System.out.print(iterator.next());
}


15.11 (a)

The expression in the for(:) loop header (in this case, the call to the makeCollection() method) is only evaluated once.

15.12 (a), (b), and (c)

Changing the value of the variable does not affect the data structure being iterated over. The for(:) loop cannot run backwards. We cannot iterate over several data structures simultaneously in a for(:) loop. The syntax does not allow it.

15.13 (b)

A String is immutable. The call to the toUpperCase() method returns a new String object whose text representation is printed. The elements of the collection remain unchanged.

15.14 (c) and (d)

The for(:) loop does not allow the list to be modified structurally. In (a) and (b), the code will throw a java.util.ConcurrentModificationException. Note that the iterator in (d) is less restrictive than the for(:) loop, allowing elements to be removed in a controlled way.

15.15 (b), (c), (f), and (i)

In (b), (c), and (f), the array type returned by the toArray() method is not a subtype of the array type on the left-hand side, resulting in a compile-time error. The program will throw an ArrayStoreException in (i), because Integer objects cannot be stored in an array of type Long.

15.16 (a) and (c)

Some operations on a collection may throw an UnsupportedOperationException. This exception type is unchecked, and the code is not required to explicitly handle unchecked exceptions. A List allows duplicate elements. An ArrayList implements a resizable array. The capacity of the array will be expanded automatically when needed. The List interface defines a get() method, but there is no method by that name in the Collection interface.

15.17 (d)

The program will compile without error, and will print all primes below 25 when run. All the collection implementations used in the program implement the Collection interface. The implementation instances are interchangeable when denoted by Collection references. None of the operations performed on the implementations will throw an UnsupportedOperationException. The program finds the primes below 25 by removing all values divisible by 2, 3, and 5 from the set of values from 2 through 25.

15.18 (a), (b), and (d)

The methods add(), retainAll(), and iterator() are defined in the Collection interface. The get() and indexOf() methods are defined in the List interface.

15.19 (b)

The remove() method removes the last element returned by either next() or previous() method. The four next() calls return A, B, C, and D. D is subsequently removed. The two previous() calls return C and B. B is subsequently removed.

15.20 (a)

[1, 3, 2] is printed. First, "1" and "2" are appended to an empty list. Next, "3" is inserted between "1" and "2", and then the list is duplicated. The original list is concatenated with the copy. The sequence of elements in the list is now "1", "3", "2", "1", "3", "2". Then a sublist view allowing access to elements from index 2 to index 5 (exclusive) is created (i.e., the subsequence "2", "1", "3"). The sublist is cleared, thus removing the elements. This is reflected in the original list and the sequence of elements is now "1", "3", "2".

15.21 (b) and (d)

The methods add() and retainAll(), return the value true if the collection was modified during the operation. The contains() and containsAll() methods return a boolean value, but these membership operations never modify the current collection, and the return value indicates the result of the membership test. The clear() method does not return a value.

15.22 (c), (d), (e), and (f)

Sets cannot have duplicates. HashSet does not guarantee the order of the elements in (a) and (b), therefore there is no guarantee that the program will print [1, 9]. Because LinkedHashSet maintains elements in insertion order in (c) and (d), the program is guaranteed to print [1, 9]. Because TreeSet maintains elements sorted according to the natural ordering in (e) and (f), the program is guaranteed to print [1, 9].

15.23 (c)

Note that the methods higher() and lower() are “stricter” than the methods ceiling() and floor().

15.24 (c) and (d)

The output from each statement is shown below.

(a) [set, shell, soap]
(b) [set, shell]
(c) [soap, swan]
(d) [swan]
(e) [shell, soap]
(f) [set, shell]


15.25 (b), (c), and (d)

The method poll() is specified in the Queue interface which is implemented by the LinkedList and PriorityQueue classes, thus ruling out (a), (e), and (f). The NavigableSet interface specifies the pollFirst() and pollLast() methods.

15.26 (c)

(a) uses an iterator which does not guarantee the order of traversal. (b) traverses the queue, but only peeks at the (same) head element each time. (d) uses an iterator, but tries to change the queue structurally by calling the poll() method at the same time, resulting in a java.util.ConcurrentModificationException. Polling in (c) is done according to the priority ordering, which in this case is natural ordering for strings.

15.27 (c) and (d)

The Map<K,V> interface defines the methods remove() and values(). It does not define methods contains(), addAll(), and toArray(). Methods with these names are defined in the Collection interface, but Map does not inherit from Collection.

15.28 (b) and (d)

Although all the keys in a map must be unique, multiple identical values may exist. Since values are not unique, the values() method returns a Collection instance and not a Set instance. The collection objects returned by the keySet(), entrySet(), and values() methods are backed by the original Map object. This means that changes made in one are reflected in the other. Although implementations of SortedMap keep the entries sorted on the keys, this is not a requirement for classes that implement Map. For instance, the entries in a HashMap are not sorted.

15.29 (c) and (e)

The classes TreeSet and TreeMap implement the comparator() method. The comparator() method is defined in the SortedSet and SortedMap interfaces, and the TreeSet and TreeMap classes implement these interfaces.

15.30 (a), (c), and (d)

The key of a Map.Entry cannot be changed since the key is used for locating the entry within the map. There is no set() method. The setValue() method is optional.

15.31 (a), (b), and (e)

The output from the program is shown below.

3001|2010|2001|
3001|2010|2001|
2001|2010|3001|
3001|2010|2001|
2001|2010|3001|


First, the elements in the set navSet are ordered in reverse natural ordering. In the statement

NavigableSet<Integer> ss1 = new TreeSet<Integer>(navSet);


the signature of the constructor called is

TreeSet<Integer>(SortedSet<E> set)


resulting in the same ordering for the elements in the set ss1 as in the set navSet, i.e., reverse natural ordering. In the statement

NavigableSet<Integer> ss2 = new TreeSet<Integer>((Collection<Integer>)navSet);


the signature of the constructor called is

TreeSet<Integer>(Collection<? extends E> collection)


resulting in the elements in set ss2 having the same natural ordering as in the set navSet.

15.32 (b) and (d)

Both (a) and (c) result in a NullPointerException: (a) in the expression (frequency == 0) and (b) in the first assignment. In both cases, the reference frequency has the value null, which cannot be boxed or unboxed.

15.33 (b)

A map view method creates half-open intervals (i.e., the upper bound is not included), unless the inclusion of the bounds is explicitly specified. Clearing a map view clears the affected entries from the underlying map. The argument to the sumValues() method can be any subtype of Map, where the type of the value is Integer.

15.34 (b) and (e)

(a) throws a ConcurrentModificationException. We cannot remove an entry in a for(:) loop. (c) throws a ConcurrentModificationException as well, even though we use an iterator. The remove() method is called on the map, not on the iterator. The argument to the remove() method of the map must implement Comparable, Map.Entry does not, resulting in a ClassCastException in (d).

We can remove an entry from the underlying map when traversing the key set using an iterator, as in (b). (e) creates a map view of one entry and clears it, thereby also clearing it from the underlying map.

15.35 (d)

StringBuilders are mutable. A string builder’s state can be modified by any of its aliases, in this case by the reference value.

15.36 (a)

Strings are immutable. In (b) and (c) the argument value in the call to the method toggle() refers to the old string after completion of the call, so the value in the map is not updated with the new string.

15.37 (c)

The class StringBuilder does not implement the Comparable interface. The sort() method that takes a comparator does not place any such requirements on the element type. The program compiles, but throws a ClassCastException, as StringBuilder objects cannot be compared in reverse natural ordering.

15.38 (d)

The class StringBuilder does not implement the Comparable interface. The sort() method (without the comparator) requires that the element type of the list is Comparable. The program will not compile.

15.39 (a) and (f)

The largest value a match can return is the largest index, i.e., array.length-1 (==3). The key must be equal to the largest element in the array. If no match is found, a negative value is returned, which is computed as follows: - (insertion point + 1). The smallest value is returned for a key that is greater than the largest element in the array. This key must obviously be placed at the index array.length (==4), after the largest element, i.e., the insertion point is 4. The value of the expression - (insertion point + 1) is -5, which is the smallest value printed by the method.

15.40 (a), (c), (e), and (f)

The Arrays class has the following methods: asList, sort, binarySearch. The Collections class has the following methods: sort, binarySearch. The List interface has the following methods: indexOf, contains, toArray, subList—only indexOf is used to look up the index of an element in the list. The method names findIndex, search, and toList are not in any of these classes nor in the List interface.

15.41 (c)

The comparator orders the strings in descending rhyming ordering: string contents are reversed and then compared in reverse lexicographical ordering. (a) is sorted in reverse natural ordering. (b) is sorted in ascending rhyming ordering. (d) is sorted in natural ordering.

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

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