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.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.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.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.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.0f
→ 5.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.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. E
xpressions 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.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.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.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.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"
. T
he 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.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.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 a
s.
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 a
s 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 Reader
s 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.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.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 String
s are not comparable to Integer
s.
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 Object
s 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 Object
s 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 Integer
s 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 String
s 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 List
s 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 List
s of String
s. The type parameter T
is inferred to be String
in the method call in (4).
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)
StringBuilder
s 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)
String
s 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.