Java Programmer Certification Exam

Learning materials

Java EE 6 Web Services Developer Certified Expert (OCEJWSD, 1Z0-897)

Notes taken after exam:

  • Which style of WSDL should I use explains the difference between RPC/encoded, RPC/literal, Document/literal, Document/literal/wrapped styles.
  • Forcing a secure connection explains the values for transport prerequisites (could be applied as security-constraint/user-data-constraint/transport-guarantee in web.xml for JAX-WS and as ejb/webservice-endpoint/transport-guarantee in sun-ejb-jar.xml for EJB-based services):
    • CONFIDENTIAL when the application requires that data be transmitted so as to prevent other entities from observing the contents of the transmission.
    • INTEGRAL when the application requires that the data be sent between client and server in such a way that it cannot be changed in transit.
    • NONE indicates that the container must accept the constrained requests on any connection, including an unprotected one.
  • A long as EJBs are normally pooled (see Why a pool of stateless EJB is needed and references), there is no guarantee that the same bean will serve all requests from the same client. Hence state is not predictable.
  • Base64 gives approx 33% overhead, see What is the worst possible increase in space usage?
  • Differences between SOAP 1.1 and SOAP 1.2 can be found in article Differences in SOAP versions. See also SCDJWS 5.0 Study Guide - SOAP & WS-I Basic Profile 1.1.
  • For usage of @XmlAttachmentRef to mark JAXB bean property, @WebMethod return value or parameter to specify binary content that should be serialized as MIME attachment (wsi:swaRef) see Using SwaRef with JAX-WS endpoints.
  • By default @WebService-annotated bean that has class name Hello is published as /HelloService
  • Detached signature is a signature where the signed entities are not attached to the actual signature fragment, enveloping signature is a signature over data that is inside the signature element, and an enveloped signature is a signature that is contained inside the data that it is signing (this the content of <Signature> element must be excluded from the calculations of the data digest and signature value). Check for other more complicated examples with partial document signing in article XML Digital Signature Service. <CanonicalizationMethod …/> defines the algorithm by which a particular physical representation of XML document can be consistently reduced to its canonical (simplest but still logically equivalent) form.

Oracle Certified Master Enterprise Architect

Java Puzzlers

Certification

One-minute drill

Chapter 1 (Declarations and Access Control)

Access and static imports

Feature Access Mode Feature may be statically imported into same-package source file Feature may be statically imported into different-package source file
Public Yes Yes
Protected Yes No
Default Yes No
Private No No

All possible combinations of features and modifiers

Feature \ Modifier public protected private final abstract static transient volatile synchronized native strictfp
top-level class :YES: :NO: :NO: :YES: :YES: :NO: :NO: :NO: :NO: :NO: :NO:
inner class :YES: :YES: :YES: :YES: :YES: :YES: :NO: :NO: :NO: :NO: :NO:
method class :NO: :NO: :NO: :YES: :YES: :NO: :NO: :NO: :NO: :NO: :NO:
interface :YES: :NO: :NO: :NO: :YES: :NO: :NO: :NO: :NO: :NO: :NO:
class variable :YES: :YES: :YES: :YES: :NO: :YES: :YES: :YES: :NO: :NO: :NO:
constructor :YES: :YES: :YES: :NO: :NO: :NO: :NO: :NO: :NO: :NO: :NO:
method :YES: :YES: :YES: :YES: :YES: :YES: :NO: :NO: :YES: :YES: :YES:
method variable :NO: :NO: :NO: :YES: :NO: :NO: :NO: :NO: :NO: :NO: :NO:
method parameter :NO: :NO: :NO: :YES: :NO: :NO: :NO: :NO: :NO: :NO: :NO:
code block :NO: :NO: :NO: :NO: :NO: :YES: :NO: :NO: :YES: :NO: :NO:

Questions

Is null a keyword?

Java2 last minute tutorial says that null is a keyword, but:

From JLS §4.1:

null is merely a special literal that can be of any reference type.

From JavaRanch SCJP FAQ and Java Language Keywords:

true, false, and null might seem like keywords, but they are actually literals

See also What is null in Java?

How to forbid the class to be inherited?

Declaring class as final or abstract class + private constructor

What is strictfp, transient, volatile?

strictfp is a keyword and can be used to modify a class or a method, but never a variable. Marking a class as strictfp means that any method code in the class will conform to the IEEE 754 standard rules for floating points. Without that modifier, floating points used in the methods might behave in a platform-dependent way (page 15). With strictfp, you can predict how your floating points will behave regardless of the underlying platform the JVM is running on. The downside is that if the underlying platform is capable of supporting greater precision, a strictfp method won't be able to take advantage of it (page 46).
The volatile modifier tells the JVM that a thread accessing the variable must always reconcile its own private copy of the variable with the master copy in memory (page 59). Volatile reads and writes are to go directly to main memory, prohibiting caching values in registers and bypassing processor-specific caches. See What is the volatile keyword? How and why would you use it? for more information.

What is Java identifier naming convention? What is a connecting character?

Identifiers must start with a letter, a currency character ($, , £, …), or a connecting character such as the underscore (_). Also see Character.isJavaIdentifierStart(char) and Character.isJavaIdentifierPart(char) (page 5, 6)

What does access to class A means?

That means altogether (below is also applicable to class fields) (page 32):
  1. refer A as a type
  2. extend A
  3. create instance of A
  4. access methods and fields of A

Can a private class be extended by another?

Top-level private classes are not allowed. However one nested class can extend another private nested class (if accessible, e.g. within one “parent” class of course).

Why creating a class in default package is bad idea?

Because it cannot be referred from other classes, even if public.

Three ways to access a method

(page 27)
  • Invoking a method declared in the same class.
    Is it always possible? What about abstract methods?
    :IDEA: Yes, for any method modifiers.
  • Invoking a method using a reference of the class.
    Is it always possible?
    :IDEA: Access control is applied, unless the instance of the same class is under operation. Protected methods can't be invoked via the reference, unless the instance class is in the same package as the caller.
  • Invoking an inherited method.
    Is it always possible?
    :IDEA: Access control is applied.

To what objects the protected modifier can be applied?

See table above.

Why abstract final is an invalid combination? What are other invalid combinations?

Invalid are following combinations: abstract native, abstract static, abstract strictfp, native strictfp (page 45)

Can interfaces be private?

Top-level interfaces may only be public or have a default package visibility. Nested interfaces can be private.

Can interface refer itself like below? Can class refer itself like below?

interface Searchable {
    Searchable getSearchbale();
}
public class Sample {
    public Sample() {
        Sample s = new Sample(); // Circular execution or compile time error?
    }
}

What happens if the following program is run (compile-time error, runtime exception, executes and exits)?

public class Mod {
    public static void main(String argv[]) {
    }
 
    public static native void amethod();
}

As there is no call to the native method, the class compiles and executes without error.

What happens for the code below (compile-time error, runtime exception, executes and exits, executes and never exits)?

public class A {
    public A() {
        this("dummy"); // Compile-time error: recursive constructor invocation
    }
 
    public A(int i) {
        this(99); // Compile-time error: recursive constructor invocation
    }
 
    public A(String s) {
        this(); // Compile-time error: recursive constructor invocation
    }
 
    public static final void main(String[]) {
        A o1 = new A();
        A o2 = new A(5);
    }
}

What is the difference in sense of inheritance between completely abstract class (with all methods marked as abstract) and interface?

Class can extend only one class but implement several interfaces, so interfaces are more flexible.

What is the practical difference in declaring the constants in public final class or interface?

By extending the interface the class can access the constants directly (page 23). Extending a class is not always possible, as multi-class inheritance is not possible, but one can use “import static” statement.

What are class members?

Methods and instance (nonlocal) variables are collectively known as members (page 24).

Is the following declaration correct?

1: public enum ImageFormat {
2:     TIFF, JPEG;
3:
4:     private static final void main(String[] args) {
5:     }
6: }

Yes it is. You can declare the main method in enum and even mark it private (but in this case JVM won't be able to run it, see Is it allowed to declare the main method private?).

What is the size of boolean type?

page 51 says “bit depth of a boolean is virtual-machine dependent” but for those who are curios from here:
  • If boolean is instance variable of a part of array (i.e. it is allocated on the heap) then its size is 1 byte.
  • If boolean is local variable (i.e. it is allocated on the stack) then its size is 4 bytes on ×32 platform (= minimum stack frame size).

What is the size of char?

2 bytes, as strings are UTF-16 in Java.

What is the pitfal in the situation when parent class changes the modifier of the method from private to protected?

The parent class had a originally private method, that was changed from private to protected. The child class:
  1. declares this method as private → attempt to narrow the visibility → error (early problem detection)
  2. declares this methods as protected → the method is overridden → pitfall.
    :IDEA: Mark the method in parent class as “final” (maybe temporary) when doing this refactoring.

Self-test

  1. C
  2. B, D
  3. A, E
  4. FA
  5. DD, E
  6. A
  7. C, DC, D, F
  8. EB
  9. AD
  1. C
  2. B, D
  3. A, E
  4. A
  5. D, E
  6. A
  7. AC, D, F
  8. B
  9. D

Chapter 2 (Object Orientation)

Downcasting

At compile time the error is generated if below is not satisfied:

  • java.lang.Object can be casted to any class or interface.
  • In case class A is casted to class B, then A should be in class hierarchy of B.
  • If class A is final, then downcasting is not possible.
  • If B is interface, then casting to B is allowed.

Questions

What are the three main principles of OOP? Explain in your words.

Please, depict the Deadly Diamond of Death.

See Deadly Diamond of Death (page 100).

What is tight encapsulation, loose coupling, and high cohesion in classes?

Encapsulation is really just a fancy name for the aggregation of data and behaviour. Your first goal in defining a good class should be to clearly define the data members that describe instances of that class, keeping in mind that this should be done only with variables of private accessibility. Next, consider how to represent the behaviour associated with these data. All behaviour should be accessed only via methods. By insisting that the variables inside an object are inaccessible outside the object, you ensure that the nature of those variables is irrelevant outside the object. This in turn means that you can freely change the nature of the storage for maintenance purposes, performance improvement, or any other reason. This is the essence of encapsulation.

Coupling is an object's reliance on knowledge of the internals of another entity's implementation. When object A is tightly coupled to object B, a programmer who wants to use or modify A is required to have an inappropriately extensive expertise in how to use B.

Cohesion is the degree to which a class or method resists being broken down into smaller pieces. Cohesion is desirable and is easy to recognize in its absence. Methods as well as classes can have or lack cohesion. A low-cohesion method can often be spotted by the presence of “and” in its name. For example, if you're reading the source code for a Java-based guitar-playing robot, you might come across a method named tuneDStringAndPlayDMinorScale(). Obviously this method performs two different tasks; the only reason they are together is that playing a scale on a string is often done right after tuning that string. But often isn't always. If you separate the code into tuneDString() and playDMinorScale(), then people have the option of playing a different scale, or not playing a scale at all, after they tune the string.

Suppose class Home has methods chopWood() and carryWater(). It also has a method called chopWoodAndCarryWater(), which just calls the other two methods. Which statements are true?

  1. chopWoodAndCarryWater() is an example of appropriate cohesion.
  2. chopWoodAndCarryWater() is an example of inappropriate cohesion.
  3. chopWoodAndCarryWater() is an example of appropriate coupling.
  4. chopWoodAndCarryWater() is an example of inappropriate coupling.

:IDEA: (2)

What is overloaded method?

Overloaded method – a method using the same identifier (name), but different arguments (page 45).

In terms of overriding / overloading, what decisions are done on compile time and what are done on runtime?

The decision on what overloaded method to call is made in compile time based on argument type(s); the decision on what overridden method to call is made in runtime based on actual instance type (page 113).

What are the problems in the following code?

 1: public abstract class A<T>
 2: {
 3:     abstract T a(int i) {}
 4:     public native b(String s);
 5:     static abstract void c();
 6:     static protected char d() {}
 7:     T protected e() { return null; }
 8:     static T x() {}
 9:     public void y() {}
10: }
11:
12: class B extends A<Object>
13: {
14:     Object a(int i) { return i; }
15:     private char d() {}
16:     private void y() {}
17: }

 1: public abstract class A<T>
 2: {
 3:     abstract T a(int i) {} // abstract method has a body
 4:     public native b(String s); // method with no return type
 5:     static abstract void c(); // "static abstract" is not valid combination of modifiers
 6:     static protected char d() {} // no "return" statement
 7:     T protected e() { return null; } // return type should go after method modifiers
 8:     static T x() {} // "T" can't be referenced in static contents
 9:     public void y() {} // OK
10: }
11:
12: class B extends A<Object>
13: {
14:     Object a(int i) { return i; } // OK, mind autoboxing
15:     private char d() { return 0; } // this instance method cannot override the parent static method
16:     private void y() {} // the visibility of A.y() cannot be reduced
17: }

The following example generates a compile-time error in line 2. Why?

1: String s = "xyz";
2: Integer i = (Integer) s;

How to cast from String to Integer?

If the casting is performed between siblings (e.g. source class and destination class have one common parent), the compiler raises an error (page 118). To overcome this one can use the following code (it will cause ClassCastException at runtime):
String s = "xyz";
Integer i = (Integer) (Object) s;

What happens for the following code?

(page 124)
 1: interface Writeable {
 2:     long write(int i);
 3: }
 4:
 5: class Basic {
 6:     protected long write(int n) {
 7:         System.out.println("A: " + n);
 8:         return n;
 9:     }
10: }
11:
12: abstract class AbstractWriteable extends Basic implements Writeable {
13:     public long write(String s) {
14:         System.out.println("B: " + s);
15:         return s.length();
16:     }
17: }
18:
19: class WriteableImpl extends AbstractWriteable {
20:     public int write(int m) {
21:         System.out.println("C: " + m);
22:         return Character.forDigit(m, 10); // returns char
23:     }
24:
25:     public static void main(String[] args) {
26:         WriteableImpl impl = new WriteableImpl();
27:         impl.write(5);
28:
29:         AbstractWriteable a = impl;
30:         a.write(4);
31:         a.write("4");
32:
33:         Basic b = a;
34:         b.write(3);
35:         b.write("3");
36:
37:         Writeable w = b;
38:         w.write('2');
39:     }
40: }
  1. The code does not compile, because abstract class AbstractWriteable goes not implement Writeable interface.
  2. The code does not compile, because abstract class AbstractWriteable tries to reduce the visibility of write(int i) method.
  3. The code does not compile, because class WriteableImpl goes not implement Writeable interface correctly.
  4. The code does not compile in line 38 with message The function Writeable.write(char) is not defined.
  5. The code compiles OK, what is the output in this case?
  6. What can you say about the type casting in lines 15, 22 and 38?

  • The code does not compile in line 12 with message The inherited method Basic.write(int) cannot hide the public abstract method in Writeable.
    :IDEA: Change the method visibility in line 6 to public.
  • The code does not compile in line 20 with message The return type is incompatible with Writeable.write(int).
    :IDEA: Change the return type in line 20 to long.
  • The code does not compile in line 35 with message The method write(int) in the type Basic is not applicable for the arguments (String).
    :IDEA: Re-write the code as ((AbstractWriteable) b).write("3");
  • The code does not compile in line 37 with message Type mismatch: cannot convert from Basic to Writeable.
    :IDEA: Re-write the code as Writeable w = (Writeable) b;

After all above is fixed, the output is:

Output


C: 5
C: 4
B: 4
C: 3
B: 3
C: 50

How make it possible that an instance method is called before the super constructor is completed? What is the pitfall?

 1: class Animal {
 2:     public Animal() {
 3:         callMe();
 4:     }
 5:
 6:     public void callMe() {
 7:     }
 8: }
 9:
10: class Monkey extends Animal {
11:     int a = 5;
12:
13:     public Monkey() {
14:         // callMe(); // we want to do it, but it's not allowed
15:         super();
16:     }
17:
18:     public void callMe() {
19:         System.out.println("I am called before super class initialization has completed");
20:         System.out.println("a=" + a);
21:     }
22: }

In the example above the class is allocated and all instance variables are set to their default values (0, null, …). However, as Monkey class initialization has not been executed, the method callMe() will print 0 to the output (page 142).

Why we need static variables? Provide practical examples.

  • Application-wide counters, e.g. the number of class instantiations.
  • Singletons.
  • Logging facilities.
  • Compiled regular expressions.
  • Pre-evaluated java.net.URLs, java.lang.Files.

What is bad about static variables?

  • Concurrency issues.
  • Dependency injection is odd.
  • It is problematic for GC to garbage collect the objects that are referred from static variables.

Static variables are initialized when the class is loaded. Is it possible to unload the class and load it again, so the statics are re-initialized?

In default implementation, the ClassLoader instance holds the list of all classes it has loaded and every Class instance has a reference to the ClassLoader it was loaded with. That means one need to release all instances of all classes loaded by a ClassLoader, as ClassLoader can be garbage collected only together with all classes it has loaded. That is possible only with custom ClassLoader implementation like in Application Server (see Unloading classes in java). That also means that objects referred by static variables cannot be garbage collected. See also JLS §12.7:

A class or interface may be unloaded if and only if its defining class loader may be reclaimed by the garbage collector as discussed in §12.6. Classes and interfaces loaded by the bootstrap loader may not be unloaded.

Very interesting project is jRebel that reloads java classes dynamically and allows even swifter development cycles by negating the need to ever redeploy.

When doesn't singleton remain singleton in Java?

From When is a Singleton not a Singleton by Joshua Fox:
  • multiple singletons simultaneously loaded by different class loaders
  • copies of a singleton object that has undergone serialization and deserialization

What is memory leak in Java?

See example of JDK memory leak

What's the difference between a ClassNotFoundException and NoClassDefFoundError?

ClassNotFoundException is thrown when the code is trying to load a class in runtime, e.g. passing its name to Class.forName().

NoClassDefFoundError is thrown when the JVM is asked to load a class indirectly (e.g. as imported class), but corresponding .class file was not found.

ExceptionInInitializerError is thrown if there was an exception in static initialization block.

The answer which is given in What's the difference between a ClassNotFoundException and NoClassDefFoundError? is incorrect.

Self-test

  1. B, E
  2. AE
  3. A
  4. ok
  5. C
  6. C
  7. AC
  8. F
  9. A
  10. B
  11. EC
  12. C
  13. A
  14. CA, C
  15. FD
  1. B, E
  2. E
  3. A
  4. ok
  5. C
  6. C
  7. C
  8. AF
  9. DA
  10. B
  11. EC
  12. C
  13. A
  14. AA, C
  15. BD

Chapter 3 (Assignments, Arrays, GC)

The rules that govern arithmetic promotion

For unary arithmetic operators (+a, -a, ++a, –a, a++, a–) two rules apply, depending on the type of the single operand:

  • If the operand is a byte, a short, or a char, it is converted to an int (unless the operator is ++ or --, in which case no conversion happens).
  • Else there is no conversion.

For binary arithmetic operators there are four rules, depending on the types of the two operands:

  • If one of the operands is a double, the other operand is converted to a double.
  • Else if one of the operands is a float, the other operand is converted to a float.
  • Else if one of the operands is a long, the other operand is converted to a long.
  • Else both operands are converted to int.

With these rules in mind for the following code

1: short s = 9;
2: int i = 10;
3: float f = 11.1f;
4: double d = 12.2;
5: if (-s * i >= f / d) {
6:     ...
7: }

what really happens in line 5 is:

  1. The short s is promoted to an int and then negated.
  2. The result of step 1 (an int) is multiplied by the int i. Because both operands are of the same type, and that type is not narrower than an int, no conversion is necessary. The result of the multiplication is an int.
  3. Before float f is divided by double d, f is widened to a double. The division generates a double-precision result.
  4. The result of step 2 (an int) is to be compared to the result of step 3 (a double). The int is converted to a double, and the two operands are compared. The result of a comparison is always of type boolean.

This promotion has an important consequence for the unsigned right-shift operator when applied to values that are narrower than int. For example the expression -64 >>> 4 (the unsigned right shift; -64 = 11000000) results -4 (11111100) instead of expected 12 (00001100) as it is first promoted to int and then shift takes place.

Operator overloading with auto-boxing and varargs

Widening is preferred over boxing and var-args are always considered last.

For the call of the method doit((byte) 5) the following method signatures will match in the following order:

  • doit(byte param)
  • doit(short param)
  • doit(int param)
  • doit(long param)
  • doit(float param)
  • doit(double param) (widening is checked first)
  • doit(Byte param) (autoboxing)
  • doit(Object param) (autoboxing + widening)
  • doit(byte… param) or doit(Byte… param) or doit(Object… param) (varargs are always considered last; if two of these three are defined then there is an ambiguity)

For the call of the method doit(new byte[] {1, 2}) the following method signatures will match in the following order:

  • doit(byte[] param) or doit(byte… param) (most specific methods; if both are defined then compile-time exception is raised as they have the same signature)
  • doit(Object param) (less specific, but arrays are objects)
  • doit(byte[]… param)
  • doit(Object… param)
  • doit(Object[]… param) (primitive arrays are not auto-boxed however it is applicable for arrays of objects like new Byte[] {1, 2})

As one can see the method doit(Object param) acts as catch-all for everything.

Questions

What are heap and stack? Why these two architectural concepts are needed? What is the difference between them?

  • Stack grows up. It is LIFO.
  • Stack is per-thread while heap is per application.
  • Stack is freed when function exists, thus it is forbidden to return objects on stack. And there are no memory leaks on stack.
  • Heap is a list of areas of different sizes. It can be fragmented.
  • Head allocation is more expensive, as the allocation function has to search for a best free block to allocate.

Why objects on the heap get initialized to default values, and objects on stack – not?

Perhaps this is the point for optimization: when the memory is allocated on the heap, it is in most cases a relatively big contiguous area which is easy to nullify. In the contrary the data is allocated on stack in small pieces (32-bit slots on ×32 platforms independently of the type) and it is quite expensive to clean them. Also in most cases they are initialized not with zero value by program logic (local variables usually refer / contain the data and only cycle variables start with zero).

What is implicit cast? Provide the examples.

(page 197)

Examine the following:

byte b = 0; // OK, implicit cast
int i = new Integer(200); // OK, unboxing
 
b = b + 5; // compilation error: cannot convert from int to byte
b += 5; // OK: implicit cast
b *= i; // OK: implicit cast, although looks like the result of multiplication is "int"

Do you feel confident in different base systems? How much is 0xA4 in octal?

0244 (10×161 + 4×160 = 20×81 + 4×80 = 16×81 + 4×81 + 4×80 = 2×82 + 4×81 + 4×80)

What is the application of octal numbering?

  • Unix access modes (?)

Which of below assignments are correct?

byte b = 10b; // Compilation error: "b" datatype suffix is incorrect
byte b1 = 20; // OK, casting is not necessary
char c = 19c; // Compilation error: "c" datatype suffix is incorrect; char c = 19 is fine (does not need casting)
char c1 = 0x1234; // OK
char c2 = \u1234; // Compilation error: the value should be enclosed into single quotes '\u1234'.
double d = 1.2d; // OK
double f = 3.2f; // OK, automatic widening from float to double
int i = 019; // Compilation error: "019" is out-of-range octal number
Short s = 1; // OK, narrowing primitive from int to short
Long l = 10; // Compilation error (which is not logical) as widening from "int" to "long" does not happen, see [1]. Correct is "Long l = 10L".
Object i = 2; // OK, auto-boxing to Integer
i = i + '2'; // OK
c = c + i; // Compilation error: right-side expression has "int" type and should be explicitly casted to "char"
c += 100L; // OK

[1] See Narrowing of primitive followed by boxing.

Which of below operators are correct? What is printed as output?

System.out.println(010|4);
int i = 10, j = 4;
System.out.println(i|j);
System.out.println(i||j);

The last line has compilation error, as operator || requires both operands to have boolean type. If that line is removed, the output is:
12
14

What is printed for this code?

byte x = 127;
x += 2;
System.out.println("x: " + x);

x: -127

Can any integer be represented as float and can any non-fractional float be represented as integer? What is printed in following examples? Does anything changes if strictfp modifier is applied for main method?

 1: public static void main(String[] args) {
 2:     System.out.println(Integer.MAX_VALUE - 255 == (int) (Integer.MAX_VALUE - 255f));
 3:     System.out.println(Integer.MAX_VALUE - 5 == (int) (Integer.MAX_VALUE - 5f));
 4:
 5:     doIntToFloatConversion(Integer.MAX_VALUE - 0xFF); // equals to 2^31 - 256
 6:     doIntToFloatConversion(Integer.MAX_VALUE - 5); // equals to 2^31 - 6
 7:
 8:     System.out.println("b: " + String.format("%.2f", Float.valueOf(33554432e10f))); // can you write this number?
 9: }
10:
11: static void doIntToFloatConversion(int i) {
12:     float f = i;
13:
14:     System.out.println("i: " + i + " (0x" + Integer.toString(i, 16) + ")");
15:     System.out.println("f: " + String.format("%.2f", Float.valueOf(f)));
16:     System.out.println("i: " + (int) f + " (0x" + Integer.toString((int) f, 16) + ")");
17:     System.out.println("l: " + (long) f + " (0x" + Long.toString((long) f, 16) + ")");
18: }

 1: true
 2: false
 3:
 4: i: 2147483392 (0x7fffff00)
 5: f: 2147483392.00
 6: i: 2147483392 (0x7fffff00)
 7: l: 2147483392 (0x7fffff00)
 8:
 9: i: 2147483642 (0x7ffffffa)
10: f: 2147483648.00
11: i: 2147483647 (0x7fffffff)
12: l: 2147483648 (0x80000000)
13:
14: b: 335544320000000000.00

In the following code, what are the possible types for variable result?

  1. byte, short, int, long, float, double
  2. short, int, long, float, double
  3. int, long, float, double
byte b = 11;
short s = 13;
result = b * ++s;

(3) int, long, float, double

Assuming you would like to check if the result of the multiplication has not caused an overflow. Will the following code do the job?

float tmp = (float) num1 * num2;
int result = (int) tmp;
 
if (result != tmp) {
    // ops, overflow
    throw new OverflowException();
}

No. If you replace tmp type with long (long tmp = (long) num1 * num2;) then it will. However casting from float is more intelligent: it will assign Integer.MAX_VALUE to result if the value does not fit the range. So the correct check with regards to above is if (result == Integer.MAX_VALUE) // ops, overflow.

Is the following operation allowed?

int[] a = new int[3];
long[] b = new long[4];
long[] c = new long[10];
 
b[0] = a[0]; // OK
b = a; // Type mismatch: cannot convert from int[] to long[]
b = c; // OK, size does not matter

What is the largest array index? Is the following allowed?

int[] a = new int[3];
a[1L] = 4; // Compilation error: index should be "int" value
a[2.0] = 2; // Compilation error: index should be "int" value

Which of the arrays is instantiated correctly?

int[][] a = new int[][];
int[][] b = new int[3][2];
int[][] c = new int[3][];
int[][] d = new int[][3];
int e[] = [10, 11, 12, 13];
Byte[] a = new Byte[] {100, 200, 300};

int[][] a = new int[][]; // error: does not define the dimensions
int[][] b = new int[3][2]; // OK, array is ready for use
int[][] c = new int[3][]; // OK, all 1st level elements are nulls
int[][] d = new int[][3]; // error: should define higher-level dimension prior to deeper dimension
int e[] = [10, 11, 12, 13]; // error: should enclose the values in curly brackets {10, 11, ...}.
Byte[] a = new Byte[] {100, 200, 300}; // error; one can in general create array like this, but the last two values exceed the byte boundary

What happens in the following code?

String[] arr = new String[2];
arr[0] = "text";
Object[] brr = arr;
brr[1] = Boolean.TRUE;

ArrayStoreException in runtime when brr[1] is assigned a Boolean value (instead of required String).

Implement a method, that removes the element array with index index. The array may be of any type (Object or primitive).

public static Object remove(Object array, int index) {
    int length = Array.getLength(array);
    Object result = Array.newInstance(array.getClass().getComponentType(), length - 1);
    // Copy elements to new array up to given index:
    System.arraycopy(array, 0, result, 0, index);
    if (index < length - 1) {
        // Copy the tail:
        System.arraycopy(array, index + 1, result, index, length - index - 1);
    }
 
    return result;
}

JVM reuses java.lang.Integer objects in range -128..127. What method in this class provides the support for this?

Integer.valueOf(int i);

Why is the code printing false in the first and true in the second line?

1: Integer a = 1000, b = 1000;
2: System.out.println(a == b);
3:
4: Integer c = 100, d = 100;
5: System.out.println(c == d);

Beginners are often confused about the JVM's cache behaviour, so this question tests that concept. The second output is true as we are comparing the references, because the JVM tries to save memory when the Integer falls within a range (from -128 to 127). In line 4, no new reference of type Integer is created for d. Instead of creating a new object for the Integer type reference variable d, it is only assigned with a previously created object referenced by c.

By the way, the higher boundary of the cache can be controlled by java.lang.Integer.IntegerCache.high system variable.

Are there any compilation problems with below code? In what order the blocks, marked by numbers, are called? What is the output?

 1: import java.io.File;
 2: import java.net.MalformedURLException;
 3: import java.net.URL;
 4:
 5: public class Test {
 6:
 7:     static final URL url;
 8:
 9:     static {
10:         try {
11:             url = new URL("http://www.google.com"); // 1: Is called first, because it is static class initializer
12:         }
13:         catch (MalformedURLException e) {}
14:     }
15:
16:     {
17:         final File file = new File("input.txt"); // 2: Is called first, because it is shared instance initialization block
18:         count++; // count is 1 after this line is executed
19:         message = "Test";
20:     }
21:
22:     int count = 5; // 3: Is called first, because it is instance variable initializer
23:
24:     {
25:         count++; // 4:
26:     }
27:
28:     final String message;
29:
30:     public Test() {
31:         super(); // 5: Is called first, because super class needs to be initialized first
32:         message = "Hello, user "; // 6:
33:     }
34:
35:     public static void main(String[] args) {
36:         Test t = new Test(); // 7: Is called first to invoke the constructor
37:         System.out.println(t.message + t.count);
38:     }
39: }

(page 236)
 1: import java.io.File;
 2: import java.net.MalformedURLException;
 3: import java.net.URL;
 4:
 5: public class Test {
 6:
 7:     static final URL url; // Compilation error: The blank final field url may not have been initialized
 8:
 9:     static {
10:         try {
11:             url = new URL("http://www.google.com"); // 1: The class is loaded by class loader and static initialization block is called
12:         }
13:         catch (MalformedURLException e) {}
14:     }
15:
16:     {
17:         final File file = new File("input.txt"); // 4: The instance initialization block is called; page 236
18:         count++; // Compilation problem: Cannot reference a field before it is defined
19:         message = "Test"; // That is legal to initialize the instance variable before it is declared
20:     }
21:
22:     int count = 5; // 5: Instance variables are initialized
23:
24:     {
25:         count++; // 6: The instance initialization block is called
26:     }
27:
28:     final String message;
29:
30:     public Test() {
31:         super(); // 3: The super constructor is called
32:         // 7: The initialization proceeds
33:         message = "Hello, user "; // Compilation problem: The final field message may already have been assigned
34:     }
35:
36:     public static void main(String[] args) {
37:         Test t = new Test(); // 2: The class instantiation is requested
38:         System.out.println(t.message + t.count); // The output is: "Hello, user 6"
39:     }
40: }

Look at another interesting example with singleton.

Does the following code compile?

 1: public class Test {
 2:     private static final byte MAX = getMax();
 3:
 4:     private static final byte getMax() {
 5:         return Byte.MAX_VALUE;
 6:     }
 7:
 8:     public boolean hasMax(byte[] bytes) {
 9:         for (int i = 0; i < bytes.length; i++) {
10:             switch (bytes[i]) {
11:                 case MAX:
12:                     return true;
13:             }
14:         }
15:
16:         return false;
17:     }
18: }

Error in line 11: case expressions must be constant expressions.

What will happen in following example?

  1. Class does not compile because of error in line 17.
  2. Program executes OK. What is the output? How to modify the code that another overloaded function is invoked in line 16?
 1: class Test {
 2:
 3:     public void print(int a) {
 4:         System.out.println("I " + a);
 5:     }
 6:
 7:     public void print(int... a) {
 8:         System.out.println("A " + Arrays.toString(a));
 9:     }
10:
11:     public void print(Object a) {
12:         System.out.println("O " + a);
13:     }
14:
15:     public void test() {
16:         print(1);
17:         print();
18:         print(null);
19:         print(1.2f);
20:     }
21: }

(vararg matching, page 111)
I 1
A []
A null
O 1.2

The code variation with explanations:

 1: class Test {
 2:
 3:     public void print(int a) {
 4:         System.out.println("I " + a);
 5:     }
 6:
 7:     public void print(int... a) {
 8:         System.out.println("A " + Arrays.toString(a));
 9:     }
10:
11:     public void print(Object a) {
12:         System.out.println("O " + a);
13:     }
14:
15:     public void test() {
16:         print(1); // 1st function is called
17:         print(new int[] {1}); // The example of explicit 2nd function
18:         print(); // 2nd function is called, but what is passed: NULL or empty array?
19:         print(null); // 2nd function is called because it is less specific
20:         print(1.2f); // 3rd function is called after float is boxed and widened
21:     }
22: }

What will happen in following examples?

  1. Class does not compile because of ambiguous overload in line 7.
  2. Program executes OK. What is the output? How to modify the code that another overloaded function is invoked instead?
 1: class Test1 {
 2:
 3:     public void print(byte n1, byte n2) {
 4:         System.out.println("X " + n1 + n2);
 5:     }
 6:
 7:     public void print(int n1, int n2) {
 8:         System.out.println("Y " + n1 + n2);
 9:     }
10:
11:     public void test() {
12:         print(1, 2);
13:     }
14: }
 1: class Test2 {
 2:
 3:     public void print(float n1, float n2) {
 4:         System.out.println("X " + n1 + n2);
 5:     }
 6:
 7:     public void print(double n1, double n2) {
 8:         System.out.println("Y " + n1 + n2);
 9:     }
10:
11:     public void test() {
12:         print(1, 2);
13:         print(1.0, 2);
14:     }
15: }
 1: class Test3 {
 2:
 3:     public void print(int n1, int n2) {
 4:         System.out.println("X " + n1 + n2);
 5:     }
 6:
 7:     public void print(float n1, float n2) {
 8:         System.out.println("Y " + n1 + n2);
 9:     }
10:
11:     public void print(double n1, double n2) {
12:         System.out.println("Z " + n1 + n2);
13:     }
14:
15:     public void test() {
16:         print(1l, 2);
17:     }
18: }

(primitive type casting)
Y 12
X 1.02.0
Y 1.02.0
Y 1.02.0

Same exercise with more scenarios:

 1: class Test1 {
 2:
 3:     public void print(byte n1, byte n2) {
 4:         System.out.println("X " + n1 + n2);
 5:     }
 6:
 7:     public void print(int n1, int n2) {
 8:         System.out.println("Y " + n1 + n2);
 9:     }
10:
11:     public void test() {
12:         print(1, 2); // 2nd function is called
13:         print((byte) 1, 2); // still 2nd function is called, as 1st argument is casted back to integer
14:         print((byte) 1, (byte) 2); // now 1st function is called due to explicit cast
15:     }
16: }
 1: class Test2 {
 2:
 3:     public void print(float n1, float n2) {
 4:         System.out.println("X " + n1 + n2);
 5:     }
 6:
 7:     public void print(double n1, double n2) {
 8:         System.out.println("Y " + n1 + n2);
 9:     }
10:
11:     public void test() {
12:         print(1, 2); // 1st function is called, as float is the type of the minimal size to fit integer
13:         print(1.0, 2); // 2nd function is called, because all numbers with floating point are treated as double
14:         print((float) 1.0, 2); // 1st function is called and 2nd argument is automatically casted to float
15:         print(1.0f, 2); // 1st function is called, as above
16:         print((double) 1, (double) 2); // 2nd function is called due to explicit cast
17:         print(1d, 2d); // 2nd function is called, as above
18:     }
19: }
 1: class Test3 {
 2:
 3:     public void print(int n1, int n2) {
 4:         System.out.println("X " + n1 + n2);
 5:     }
 6:
 7:     public void print(float n1, float n2) {
 8:         System.out.println("Y " + n1 + n2);
 9:     }
10:
11:     public void print(double n1, double n2) {
12:         System.out.println("Z " + n1 + n2);
13:     }
14:
15:     public void test() {
16:         print(1l, 2); // 2nd function is called, as float is the type of the minimal size to fit integer
17:         print(1, 2); // 1st function is called
18:         print(1.0, 2); // 3rd function is called
19:     }
20: }

It is possible to use e.g. post-increment operator on numerical objects. Why this is not GC-friendly?

public void power(Object n) {
    Integer b = (Integer) n;
    for (int i = 0; i < 5; i++)
        b *= b; // auto-unboxing, incrementing and auto-boxing (page 245)
    System.out.println("b=" + b); // What is the output? It will be 2^(2*2*2*2*2) = 2^32 = 0
}
 
public void test() {
    power(2); // auto-boxing to Integer and widening to Object (page 252)
}

What code can you write to ensure that Integer variables are garbage collected at a particular point in this code?

  1. System.gc();
  2. System.free();
  3. Set the value of each variable to null
  4. None of the above
public void amethod() {
    Integer i = new Integer(1);
    Integer j = new Integer(2);
    Integer k = new Integer(3);
 
    // insert here
}

The last answer is correct, as you can only suggest garbage collection, therefore you cannot be certain that it will run at any particular point in your code. However using the certain technique (see Which is a better way to force GC?) one can check that garbage collector was actually run.

A difference between WeakReference and SoftReference in Java?

Though both WeakReference and SoftReference helps garbage collector and memory efficient, WeakReference becomes eligible for garbage collection as soon as last strong reference is lost but SoftReference even thought it can not prevent GC, it can delay it until JVM absolutely needs memory. In this respect SoftReference suits best long-leaving caches (perhaps controlled by TTL), while WeakReference is best for the data which can change in runtime:
  • Classes, methods, fields and other reflection-related information (from one side stronger reference will prevent the class and all classloader hierarchy to be unloaded and from another side newly loaded class may be different).

While testing some code that you are developing, you notice that an ArrayIndexOutOfBoundsException is thrown. What is the appropriate reaction?

  1. Enclose the offending code in a try block, with a catch block for ArrayIndexOutOfBoundsException that prints out a descriptive message.
  2. Declare that the method that contains the offending code throws ArrayIndexOutOfBoundsException.
  3. None of the above.

:IDEA: (3) – none of the above. Since ArrayIndexOutOfBoundsException is a runtime exception, it indicates a faulty algorithm that should not be released. The only appropriate response is to find and fix the bug.

Self-test

  1. C
  2. BC
  3. C
  4. F
  5. A
  6. D
  7. HA, B
  8. CE
  9. A
  10. C, F
  11. B
  12. A
  13. E
  1. BC
  2. C
  3. GC
  4. F
  5. CA
  6. D
  7. HA, B
  8. CE
  9. GA
  10. C, F
  11. B
  12. FA
  13. E

Chapter 4 (Operators)

Questions

What happens in the following example?

(page 297)
Object n = null;
 
if (!(n instanceof Integer)) // it is known that instanceof returns false when the left operand is null
{
    Integer i = (Integer) n; // OK, but may look like NullPointerException or ClassCastException at runtime
}

If String value starts the concatenation expression, Java automatically evaluates the result as concatenation of Strings. What happens in following situation? What is the output?

(page 299)
int m = 1, n = 2;
String s = null;
System.out.println(m + n + s); // OK or NullPointerException?
System.out.println(s + m + n); // OK or NullPointerException?

3null
null12

Does below code compile?

 1: public class Test {
 2:
 3:     public static Integer callMe() {
 4:         int a = 0;
 5:         return(++(a++)); // Compilation error: Invalid argument to operation ++/--
 6:     }
 7:
 8:     public static int runMe() {
 9:         int b = 0;
10:         return(++b + b++);
11:     }
12:
13:     public static void main(String[] args) {
14:         callMe() + 1; // Compilation error: The left-hand side of an assignment must be a variable
15:         runMe()++; // Compilation error: Invalid argument to operation ++/--
16:     }
17: }

Does below code compile? What is the result in runtime?

System.out.println(+~5);

-6 (~ is the bit inversion operator, and + does not do anything)

See also what are bit operators good for.

What is the contents of the array in the following example?

int a[] = { 4, 5 };
int b = 1;
a[b] = b = 0;

a = { 4, 0 }

This is because [] operator has highest precedence so it is evaluated first (see Java operator precedence table) and then assignment operator is evaluated from right-to-left: (a[0] = (b = 0)).

Which of below comparisons are compile-time correct? What is the result in runtime?

byte b = 10;
char c = 40;
double d = 10.0D;
assert (c > b); // OK, results true
assert (b == d); // OK, results true
assert (Byte.valueOf(b).equals(Double.valueOf(d))); // OK, results false

Self-test

  1. D
  2. D
  3. BE
  4. G
  5. ok
  6. C
  7. A
  8. C
  9. C, F
  10. D
  1. D
  2. D
  3. AE
  4. G
  5. ok
  6. C
  7. A
  8. C
  9. C, F
  10. D

Chapter 5 (Flow Control, Exceptions, and Assertions)

Mind the | and & logical operators that evaluate both arguments.

Using Assertions

Assertions are commonly used to check preconditions, postconditions, and class invariants.

A precondition is a constraint that must be met on entry of a method. If a method's preconditions are not met, the method should terminate at once before it can do any damage. A method's preconditions are typically functions of its arguments and the state of its object. Argument range checking at the start of a method is a common form of precondition testing.

A postcondition is a constraint that must be met on return from a method. If a method's postconditions are not met, the method should not be allowed to return. A method's postconditions are typically functions of its return value and the state of its object. In a general sense, if a precondition fails, the problem lies in the method's caller, whereas if a postcondition fails, the problem lies in the method itself.

A class invariant is a constraint on a class's state that must be met before and after execution of any non-private method of a class. Private methods might be used to restore the required state after execution of a non-private method.

Questions

Agree/disagree with the compilation problems in the following code

(page 339)
public class Test {
    enum Format {
        JPEG, PNG, GIF // (1) Compilation error: ";" is missing at the end of enumeration list
    }
 
    public void process(Format format) {
        int i;
 
        if (format != null) {
            switch (format) {
                default: // (2) Compilation error: "default" statement should go last after all "case" statements
                    i = 0;
                case Format.JPEG: // (3) Compilation error: The qualified case label Format.JPEG must be replaced with the unqualified enum constant JPEG
                    i = 1;
                    break;
                case Format.PNG:
                    i = 2;
                    break;
            }
        }
 
        System.out.println("i=" + i); // (4) Compile error: The local variable may not have been initialized
    }
}

(3) and (4) are valid errors.

What are the rules to be followed for labelling the statements? What is wrong with the following code?

(page 354, Labelled Statements)
 1: public class Test {
 2:     public void test(int i) {
 3:         outer : i++;
 4:
 5:         if (i > 2) break outer;
 6:
 7:         System.out.print(i + " ");
 8:
 9:         test(i);
10:     }
11:
12:     public static void main(String[] args) {
13:         new Test().test(0);
14:     }
15: }

This code does not compile in line 3: the label should mark a block statement (if, for, while, do or empty block) for example:
 1: public class Test {
 2:     public void test(int i) {
 3:         outer : {
 4:             i++;
 5:
 6:             if (i > 2) break outer;
 7:
 8:             System.out.print(i + " ");
 9:
10:             test(i);
11:         }
12:     }
13:
14:     public static void main(String[] args) {
15:         new Test().test(0);
16:     }
17: }

The above code will produce 1 2.

1: for (int i = 7, long j = 0; i < 10; j++) { }
2: for (int i = 7, j = 0;; j++) { }
3: int j; for (int k = 0; j + k != 10; j++, k++) { }
4: int i; for (i++, int j = 0; i < 10; j++) { }
5: for (String s = ""; s.length() < 10; s += '!') { }
6: for (final String s : new String[]{"test"}) { }
7: int k; for (k: new int[]{1, 2, 3}) { }

Declarations in lines 2, 3, 5, 6 are correct. Interesting related post: foreach with variable name equal to field name results compilation error.

Instances of what classes may be not handled when exceptional situation occurs?

(page 367)

Error and RuntimeException

Pitfall of exception handling

(page 368)

Let's assume that you have programmed the interface for your library:

import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.xml.sax.SAXException;
 
public interface XMLConfigurator {
    String getXMLConfigurationValue(String configurationName) throws ParserConfigurationException, SAXException, TransformerException, IOException;
}

and some implementation:

import ...;
 
public class XMLConfiguratorImpl implements XMLConfigurator {
 
    String getXMLConfigurationValue(String configurationName) throws ParserConfigurationException, SAXException,
                TransformerException, IOException {
        return ""; // in practise should do something sensible
    }
}

but when somebody was using your library for example like below:

import ...;
 
public class MainModule {
 
    XMLConfigurator xmlConfigurator = new XMLConfiguratorImpl();
 
    public void readConfiguration() {
        String value;
 
        try {
            value = xmlConfigurator.getXMLConfigurationValue("color");
        }
        catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
        catch (SAXException e) {
            e.printStackTrace();
        }
        catch (TransformerException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

he found out that exception handling was complicated to handle (a lot of catch clauses). So you decided to change API to following:

import ...;
 
public interface XMLConfigurator {
    String getXMLConfigurationValue(String configurationName) throws Exception;
}

so now the exception handling looks much easier:

try {
    value = xmlConfigurator.getXMLConfigurationValue("color");
}
catch (Exception e) {
    e.printStackTrace();
}

Questions:

  1. Does interface implementation XMLConfiguratorImpl need any changes to confirm a new API?
  2. What is the pitfall of new exception handling approach? What is the better solution?

The implementation does not need to be changed, as interface is widening the range of exceptions. However in exception handler one would need to distinguish between checked IOException and other runtime exceptions, e.g. NullPointerException. Better solution would be to introduce new exception that will wrap all others:
public interface XMLConfigurator {
    String getXMLConfigurationValue(String configurationName) throws XMLConfiguratorException;
}

What will happen when you attempt to compile and run the following code?

public void amethod() {
    outer : for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            System.out.println("i=" + i + " j= " + j);
            if (i > 0) break outer;
        }
    }
    System.out.println("Continuing with i set to =" + i);
}
  1. Compile time error
  2. i=0 j= 0
    i=0 j= 1
    i=1 j= 0
  3. i=0 j= 0
    i=0 j= 1
    i=1 j= 0
    i=2 j= 0
  4. i=0 j= 0
    i=0 j= 1

(1) as i variable is referred when it is out of scope.

Self-test

  1. B, F
  2. B
  3. CC, D
  4. C, D, E
  5. A, C, D, FA, D, F
  6. E
  7. C
  8. D
  9. F, GD, F
  10. D
  11. FH
  12. C
  13. B, DB, E
  14. D, EE
  15. BD
  16. C, D
  1. B, F
  2. B
  3. CC, D
  4. C, D, E
  5. A, D, F
  6. E
  7. C
  8. FD
  9. FD, F
  10. D
  11. H
  12. C
  13. B, E
  14. AE
  15. BD
  16. C, D

Chapter 6 (Strings, I/O, Serialization, Formatting, and Parsing)

Questions

What is the difference between StringBuilder and StringBuffer, Vector and ArrayList?

Why would it be more secure to store sensitive data (such as a password, social security number, etc.) in a character array rather than in a String?

In Java, Strings are immutable and are stored in the string pool. What this means is that, once a String is created, it stays in the pool in memory until being garbage collected. Therefore, even after you're done processing the string value (e.g., the password), it remains available in memory for an indeterminate period of time thereafter (again, until being garbage collected) which you have no real control over. Therefore, anyone having access to a memory dump can potentially extract the sensitive data and exploit it.

In contrast, if you use a mutable object like a character array, for example, to store the value, you can set it to blank once you are done with it with confidence that it will no longer be retained in memory.

Why isn't String's length() accurate?

It isn't accurate because it will only account for the number of characters within the String. In other words, it will fail to account for code points outside of what is called the BMP, that is, code points with a value of U+10000 or greater.

The reason is historical: when Java was first defined, one of its goal was to treat all text as Unicode, but at that time Unicode did not define code points outside of the BMP. By the time Unicode defined such code points, it was too late for char to be changed.

This means that code points outside the BMP are represented with two chars in Java, in what is called a surrogate pair. Technically, a char in Java is a UTF-16 code unit.

The correct way to count the real numbers of characters within a String, i.e. the number of code points, is either:

str.codePointCount(0, str.length())

or, with Java8:

str.codePoints().count()

What is the output?

char[] numbers = new char[] { '1', '2', '3' };
System.out.println(numbers + "");
System.out.println(numbers);
System.out.println((Object) numbers);

Since there is an overloaded method PrintWriter#println(char x[]) (note that is only true for char[]), the output will be similar to:
[C@3890c1ee
123
[C@3890c1ee

Consider the following code. Are the given assertions true or false? What are the conclusions?

 1: import java.io.ByteArrayInputStream;
 2: import java.io.ByteArrayOutputStream;
 3: import java.io.IOException;
 4: import java.io.ObjectInputStream;
 5: import java.io.ObjectOutputStream;
 6: import java.io.Serializable;
 7:
 8: public class Test {
 9:
10:     enum Type {
11:         FATHER, MOTHER
12:     }
13:
14:     public static class Parent implements Serializable {
15:         Parent parent;
16:         Type   type;
17:
18:         public Parent(Type type) {
19:             parent = this;
20:             this.type = type;
21:         }
22:     }
23:
24:     public static void main(String[] args) throws IOException, ClassNotFoundException {
25:         Parent parent = new Parent(Type.MOTHER);
26:         Parent parentClone;
27:
28:         assert parent == parent.parent; // true or false?
29:
30:         ByteArrayOutputStream bos = new ByteArrayOutputStream();
31:
32:         ObjectOutputStream os = new ObjectOutputStream(bos);
33:         os.writeObject(parent);
34:         os.close();
35:
36:         ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
37:         parentClone = (Parent) is.readObject();
38:         is.close();
39:
40:         assert parent == parentClone; // true or false?
41:         assert parent.parent == parentClone.parent; // true or false?
42:         assert parentClone == parentClone.parent; // true or false?
43:         assert parentClone.type == parent.type; // true or false?
44:     }
45: }

The answer is: true, false, false, true, true. From above we can conclude, that deserialized objects are new instances in JVM (line 40). However the same instances in object graph (e.g. loops) are maintained (line 42). Also enums are unique within JVM (line 43).

Enums can be compared with == operator. How JVM deals with serialization/deserialization of enum values (objects) to achieve this consistency?

(page 294)

See ObjectInputStream#readEnum(boolean), which uses T Enum#valueOf(Class<T> enumType, String name) to support the consistency.

Why readObject() / writeObject() are not a part of java.io.Serializable interface to expose the API to serialization framework? What is java.io.Externalizable interface for?

As interface methods are always public, these methods will allow other objects to read the state of Serializable objects, thus impacting the security.

java.io.Externalizable interface may be implemented by a class to have a control over serialization for all fields in class hierarchy, while using readObject() / writeObject() the class can control the serialization of this class fields but parent(s) are processed in normal way (they may also implement these methods).

What is the difference between DataInputStream#readLine(), RandomAccessFile#readLine(), RandomAccessFile#readUTF(), LineNumberReader#readLine(), BufferedReader#readLine()?

DataInputStream#readLine() and RandomAccessFile#readLine() can be used to read 7-bit character (ASCII) strings as these methods are not Unicode-aware. With RandomAccessFile#readUTF() one can read UTF-8 string but it should be prefixed with number of UTF-8 characters to read given as 2-byte short. LineNumberReader#readLine() and BufferedReader#readLine() do the character conversion, so they are the most correct way to read 8-bit strings (in native character set) or Unicode strings.

What is the problem with this code?

final Path path = Paths.get(...);
 
Files.lines(path).forEach(System.out::println);

The problem is that the Stream returned by Files.lines() is not closed. You should use:
try (
    final Stream<String> stream = Files.lines(path);
) {
    stream.forEach(System.out::println);
}

Stream extends BaseStream, and BaseStream extends AutoCloseable. While this has no influence on streams you obtain from collections for instance, the stream returned by Files.lines() is I/O bound. Neglecting to close it correctly may lead to a resource leak in the event of an error occurring while processing the stream.

What the following code produces?

NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(3);
System.out.println(nf.parse("222.5678"));
System.out.println(nf.format(123.4687));

maximumFractionDigits are only applicable for formatting the number as string, not for parsing. The number is rounded up to this limit when formatted:
222.5678
123.469

Self-test

  1. E
  2. C
  3. B, C, D
  4. A
  5. failed
  6. OK
  7. AA, B
  8. CC, F
  9. D, F
  10. E
  11. D
  12. AA, B
  13. D, F, G
  14. B
  15. F

Chapter 7 (Generics and Collections)

Questions

Consider the following code. Are the given assertions true or false? What are the conclusions?

 1: import java.util.HashMap;
 2: import java.util.Map;
 3:
 4: public class Test {
 5:
 6:     public static void main(String[] args) {
 7:         Map<Number, String> m = new HashMap();
 8:
 9:         m.put(13, "super");
10:
11:         String s1 = "super";
12:
13:         m.put(13L, s1);
14:
15:         String s2 = new String("super");
16:
17:         m.put((short) 13, s2);
18:
19:         String s3 = new StringBuffer().append("super").toString();
20:
21:         m.put((byte) 13, s3);
22:
23:         String t = m.get(13);
24:
25:         assert m.size() == 4; // true or false?
26:         assert "super" == t; // true or false?
27:         assert m.get(13L) == t; // true or false?
28:         assert m.get((short) 13) == t; // true or false?
29:         assert m.get((byte) 13) == t; // true or false?
30:     }
31: }

(page 553)

true, true, true, false, false. From above we can conclude, that implementation of equals() and hashCode() methods treats numbers with the same “logical” value differently and result depends on class type (though it could have been implemented in Number parent class). Also string pool has no impact on newly created String object (lines 15 & 28) and StringBuffer objects (lines 19 & 29). Also note widening to Object after autoboxing in lines 9, 13, 17, 21, 23.

Different Enum.hashCode() method implementation

What if default Enum.hashCode() method implementation
1: public final int hashCode() { // note that the method is final and you can't override it
2:     return super.hashCode();
3: }

is replaced by the following:

1: public final int hashCode() {
2:     return ordinal();
3: }

Is it still a valid hashCode() implementation? Is it better or worse hashCode() implementation in comparison to original?

Yes, it is a valid implementation. As all enum ordinals are unique, it gives a perfect distribution. However if enums of different types are mixed in one hash table then original version is better as ordinals will cause collisions.

What exception is thrown in the runtime when attempt to add the element to submap is made which is outside the boundaries of this submap?

(page 589)

IllegalArgumentException is thrown (see TreeMap$NavigableSubMap#put(K, V))

The difference between LinkedHashSet and PriorityQueue?

PriorityQueue is implementation of binary heap, i.e. it guarantees that lowest (or highest) priority element always remain at the head of the queue. This collection is unordered, e.g. elements may appear in the order different from how they have been added. In contrast iterator of LinkedHashSet does guarantee the order on which elements are added.

Why there is no Arrays#sort() method for primitive type array with comparator?

(page 593)

Because there is no way to use primitives as class parametrization. In order to approach this challenge properly one need to create ComparableXXX interface, Arrays.sort() and Arrays.binarySearch() methods for each primitive type.

Why there are Arrays#equals(Object[], Object[]) and Arrays#toString(Object[]) methods?

(page 593)

Because array objects do not override toString() and equals() methods, which are inherited from Object and thus do not perform by element stringification / comparison.

Why Collection interface has T[] toArray(T[]) method, but another overloaded version without argument (Object[] toArray()) returns Object[]?

(page 604)

Because of the type erasure there is no way to recover the parametrization class unless it is passed explicitly. Some implementations, like TypedList can do this, because they store the target class as class instance variable.

There are exactly two compile-time problems with code below. Find them.

(page 629)
 1: public class Class<Class extends Serializable> {
 2:     Class Class; // Here "Class" is "this" Class or parametrization type or java.lang.Class?
 3:
 4:     public Class(Class Class) {
 5:         this.Class = Class;
 6:     }
 7:
 8:     java.lang.Class<?> getClass(Class Class) {
 9:         return Class.getClass();
10:     }
11: }
12:
13: class SuperClass extends Class<Number> {
14:
15:     @Override
16:     java.lang.Class<?> getClass(Integer Class) {
17:         return super.getClass(Class);
18:     }
19: }

  • The Class class does not have a default constructor. So SuperClass should define a constructor that will invoke super one.
  • SuperClass intends to override the getClass(T) method, but the signature should match exactly the parametrization type (getClass(Number) and not getClass(Integer)).

See also Различные пространства имён.

What is the difference between following helper methods declarations? Mark all lines, that cause compilation problems.

(page 640)
 1: public static <A extends Number> List<A> process1(List<A> nums) {
 2:     return nums;
 3: }
 4:
 5: public static <A extends Number, B extends Number> List<A> process2(List<B> nums) {
 6:     return nums; // The return type does not match the passed parameter, as A is not the same as B.
 7: }
 8:
 9: public static void test() {
10:     List<Number> listN = null;
11:     List<Integer> listI = null;
12:
13:     listN = process1(listN);
14:     listI = process1(listI);
15:
16:     listI = process1(listN); // In this line and the next line there is an error, because List<Number> and List<Integer> are different collections,
17:     listN = process1(listI); // which cannot be casted to each other, even though Integer is a Number.
18:
19:     listN = process2(listN);
20:     listI = process2(listI);
21:
22:     listI = process2(listN);
23:     listN = process2(listI);
24: }

Will the following code compile?

Collection<? extends Number> c = new ArrayList<Long>();
ArrayList<Number> list = new ArrayList<Number>();
list.addAll(c);

Yes. However c.add(Integer.valueOf(1)) is not allowed as given collection is homogeneous collection of one particular type.

What is the issue with following implementation of compareTo() method in Java?

public int compareTo(Object o) {
   Employee emp = (Employee) o;
   return this.id - emp.id;
}

where id is an integer number.

There is nothing wrong in this Java code until you guarantee that id is always positive. This Java question becomes tricky when you can't guarantee that id is positive or negative. If id becomes negative than subtraction may overflow and produce an incorrect result. Correct implementation:
public int compareTo(Object o) {
   Employee emp = (Employee) o;
   return (this.id < emp.id ) ? -1 : (this.id > emp.id) ? 1 : 0;
}

When do Double and BigDecimal give equals() != (compareTo() == 0)?

Though most of the classes will be equal if there compareTo() return true e.g. java.lang.String, but it's not mandatory. compareTo() may be inconsistent to equals(), which means compareTo() may return zero, but object will not be equal by equals() method. One of the prime example of this is java.math.BigDecimal class, whose equals() method return true if two BigDecimal objects are equal in both value and scale e.g. 6.0 and 6.00 will not be equal, but compareTo() will return zero given numbers are compared. For this reason BigDecimal class can produce unexpected behaviour when stored in SortedSet or SortedMap.

See:

Self-test

  1. B
  2. B, D
  3. CE
  4. C, D
  5. B, E, G
  6. B
  7. D
  8. B, C, E, FB, E, F
  9. HB
  10. A, B, C, D, E, FA, B, C, D, F
  11. A, D
  12. B, F
  13. A, DA
  14. B
  15. G
  16. G
  1. B
  2. B, D
  3. E
  4. C, D
  5. B, E, G
  6. B, C, DB
  7. D
  8. B, E, F
  9. EB
  10. A, B, C, D, F
  11. A, D
  12. B, F
  13. A, DA
  14. B
  15. DG
  16. CD, G

Chapter 8 (Inner Classes)

Questions

How to instantiate anonymous class that extend another class that has no default constructor? Correct compile-time problem(s) with code below. What is printed to output when they are corrected?

 1: public class Test {
 2:
 3:     public class Inner {
 4:         String caption;
 5:
 6:         public Inner(String caption) {
 7:             this.caption = caption;
 8:         }
 9:
10:         public String toString() {
11:             return "Caption: " + caption;
12:         }
13:     }
14:
15:     public Inner test(String title) {
16:         return new Inner("Main dialog") {
17:             public String toString() {
18:                 return "Title: " + title;
19:             }
20:         };
21:     }
22:
23:     public static void main(String[] args) {
24:         System.out.println("Result: " + Test().test("Popup"));
25:     }
26: }

The output is Result: Title: Popup after one fixes problem in line 18 (access to non-final variable title) and line 24 (Test() looks like a method call but should be a class instantiation: new Test().test(…)).

Look at the example below. Check for compile-time errors. What is produced in the output?

 1: public class Test {
 2:
 3:     static class Parent {
 4:         private final String name;
 5:
 6:         public Parent(String name) {
 7:             this.name = name;
 8:         }
 9:
10:         public String getName() {
11:             return name;
12:         }
13:     }
14:
15:     public static void main(String[] args) {
16:         Test test = new Test();
17:
18:         test.print(test.new Parent("John") {
19:             public String getName() {
20:                 return "Actual: " + super.getName();
21:             }
22:         });
23:     }
24:
25:     void print(Parent parent) {
26:         System.out.println(parent.getName());
27:     }
28: }

This code has the compilation error in line 18: the static classes should be instantiated without the reference to enclosing class instance. If you remove static modifier or change the instantiation to test.print(new Parent("John") ... then the program produces Actual: John.

Consider the following example. Are there any problems with this code?

 1: public class Outer {
 2:     private int i;
 3:
 4:     static class Inner<T extends Child> {
 5:         public void test(T o) {
 6:             Outer outer = o;
 7:             outer.i = 1; // Can "i" be accessed here?
 8:             outer.j = 2; // Can "j" be accessed here?
 9:
10:             Child child = o;
11:             child.i = 3; // Can "i" be accessed here?
12:             child.j = 4; // Can "j" be accessed here?
13:         }
14:     }
15:
16:     public static void main(String[] args) {
17:         new Outer.Inner().test(new Child());
18:     }
19: }
20:
21: class Child extends Outer {
22:     private int j;
23:
24:     public Child() {
25:         super.i = 5; // Can "i" be accessed here?
26:     }
27: }

true, false, false, false, false. Check bug#7022052 for discussion concerning the problem of accessing the private variable from inner class in line 7: Java6 compiles this without error, but Java7 will raise a compile-time error (Java8 is fine with it again).

Self-test

  1. B, D
  2. B, C
  3. B, E
  4. G
  5. BE
  6. A
  7. D
  8. C
  9. C, G
  10. C
  11. B
  12. EA
  1. B, D
  2. BB, C
  3. B, E, DB, E
  4. BG
  5. E
  6. A
  7. D
  8. BC
  9. GC, G
  10. C
  11. B
  12. A

Chapter 9 (Threads & Concurrency)

Suspending

Suspending a thread is a mechanism that allows any arbitrary thread to make another thread unready for an indefinite period of time. The suspended thread becomes ready when some other thread resumes it. This might feel like a useful technique, but it is very easy to cause deadlock in a program using these methods – a thread has no control over when it is suspended (the control comes from outside the thread) and it might be in a critical section, holding an object lock at the time. The exact effect of suspend() and resume() is much better implemented using wait() and notify().

Questions

What is Thread.yield()?

Causes the currently executing thread object to temporarily pause and allow other threads to execute (from JavaDoc).

What will happen when you attempt to compile and run the following code?

  1. Compilation and sample output 0 1 2 3
  2. Compilation and sample output 1 2 2 2
  3. Compilation and sample output 3 3 3 3
  4. Compilation and no output
  5. Compile time error: class Sample is an abstract class. It can't be instantiated.
public class Sample extends Thread {
    public static void main(String argv[]) {
        System.out.print(Thread.currentThread().getId() + " ");
        Sample r = new Sample();
        r.run();
    }
 
    public void run() {
        for (int i = 0; i < 3; i++)
            System.out.print(Thread.currentThread().getId() + " ");
    }
}

:IDEA: (3) because each newly created thread is actually not started and thus running in current thread.

Which one statement is always true about the following application?

  1. When the application is run, thread hp1 will execute; threads hp2 and hp3 will never get the CPU.
  2. When the application is run, thread hp1 will execute to completion, thread hp2 will execute to completion, then thread hp3 will execute to completion.
  3. When the application is run, all three threads (hp1, hp2, and hp3) will execute concurrently, taking time-sliced turns in the CPU.
  4. None of the above scenarios can be guaranteed to happen in all cases.
class HiPri extends Thread {
    HiPri() {
        setPriority(10);
    }
 
    public void run() {
        System.out.println("Another thread starting up.");
        while (true) { }
    }
 
    public static void main(String args[]) {
        HiPri hp1 = new HiPri();
        HiPri hp2 = new HiPri();
        HiPri hp3 = new HiPri();
        hp1.start();
        hp2.start();
        hp3.start();
    }
}

:IDEA: (3) As the thread code has infinite loop, options (1) and (2) are not possible.

Assuming the thread is not interrupted, which one of the following statements is correct for below code?

  1. The code will not compile, because InterruptedException is not thrown by Thread.sleep().
  2. At line 2, the thread will stop running. Execution will resume in at most 100 milliseconds.
  3. At line 2, the thread will stop running. It will resume running in exactly 100 milliseconds.
  4. At line 2, the thread will stop running. It will resume running some time after 100 milliseconds have elapsed.
1: try {
2:     Thread.sleep(100);
3: } catch (InterruptedException e) { }

:IDEA: (4)

Suppose threads aThread and bThread are both accessing a shared object named sharedOb, and aThread has just executed sharedOb.wait();. What code can bThread execute in order to guarantee to get aThread out of the waiting state, no matter what other conditions prevail?

  1. aThread.notify();
  2. aThread.notifyAll();
  3. aThread.interrupt();
  4. sharedOb.notify();
  5. sharedOb.notifyAll();

:IDEA: (3), (5). When a thread is interrupted, it leaves the wait state and enters the code block that handled InterruptedException. (4) does not work because there might be multiple threads waiting for sharedOb and only one of them will be notified.

How can you catch an exception thrown by another thread?

This can be done using Thread.UncaughtExceptionHandler. Here's a simple example:
Thread otherThread = new Thread();
...
otherThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    public void uncaughtException(Thread th, Throwable ex) {
        System.out.println("Uncaught exception: " + ex);
    }
});
otherThread.start();

How to implement a thread pool executor that does not reject the task if tasks' queue is full, but rather blocks?

The default behaviour of thread pool executor is to throw RejectedExecutionException if queue becomes full and no new workers can be spawned. The challenge is that ThreadPoolExecutor#execute(Runnable) calls BlockingQueue#offer(E) which according to contract should not block. The quick and dirty solution would be to turn that method into blocking:
import java.util.concurrent.LinkedBlockingQueue;
 
public class BoundedBlockingQueue<E> extends LinkedBlockingQueue<E> {
    public BoundedBlockingQueue(int maxSize) {
        super(maxSize);
    }
 
    @Override
    public boolean offer(E e) {
        // turn offer() and add() into a blocking calls (unless interrupted)
        try {
            put(e);
            return true;
        }
        catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
        return false;
    }
}
 
...
 
ExecutorService executorService = new ThreadPoolExecutor(threadsNumber, threadsNumber, 0L, TimeUnit.MILLISECONDS, new BoundedBlockingQueue(threadsNumber * 5));

Alternative way is to set rejection policy to one that executes the rejected job by a caller (it makes sense because the caller usually waits until all jos are executed):

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
 
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
 
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

What is difference between ExecutorService.submit() and Executor.execute() method?

Answer of this Java interview question is that former returns a Future which can be used to retrieve result from worker thread.

There is also a difference when looking at exception handling. If your task throws an exception and if it was submitted with execute() this exception will go to the uncaught exception handler (when you haven't provided one explicitly1), the default one will just print the stack trace to System.err). For a task that was submitted with submit() and that terminates with an exception, the Future.get() will re-throw this exception wrapped in an ExecutionException.

What are synchronization abstractions semaphore and mutex used for? Make a comparison. Provide examples.

  • Mutex is a binary flag used to protect a shared resource by ensuring mutual exclusion inside critical sections of code. Mutex allows to lock the access to shared resource by one thread, while semaphore allows sharing of the resource among several threads.
  • Semaphore uses mutex in its implementation (introduced by Edsger Dijkstra in 1962).
  • Mutex can be treated as binary semaphore.

Examples of mutex usage:

  • Persons want to share a bathroom. Only one person may be inside a bathroom at any moment.
  • Get a message from the queue. If two threads access the queue concurrently, they may pickup the same message for processing.

Examples of semaphore usage:

  • There can be at most 4 persons in the lift.
  • There can be no more than 10 concurrently logged-in uses in the system.
  • Limited number of threads in a thread pool or connections in the connection pool.
  • The clients may put not more than 50 messages into message processing queue. When the consumer processes the message from the queue it will signal to provider that he is ready to process another message.
  • … any other producer-consumer problem with capacity limitations.

Comparison:

See also:

Difference between ReentrantLock and synchronized keyword

:ADD: Benefits of ReentrantLock in Java:

:DEL: Disadvantages of ReentrantLock in Java:

  • Major drawback of using ReentrantLock in Java is wrapping method body inside try-finally block to release a lock in finally section, which makes code unreadable and hides business logic.

Why should you check condition for waiting in a loop?

Its possible for a waiting thread to receive false alerts and spurious wake up calls, if it doesn't check the waiting condition in loop, it will simply exit even if condition is not met. As such, when a waiting thread wakes up, it cannot assume that the state it was waiting for is still valid. It may have been valid in the past, but the state may have been changed after the notify() method was called and before the waiting thread woke up. That's why it always better to call wait() method from loop, you can even create template for calling wait and notify in Eclipse.

Is it possible for Thread 2 to print "x=0"?

Consider the following Java code snippet, which is initializing two variables and both are not volatile, and two threads T1 and T2 are modifying these values as following, both are not synchronized:
int x = 0;
boolean bExit = false;

Thread 1 (not synchronized):

x = 1;
bExit = true;

Thread 2 (not synchronized):

if (bExit == true)
    System.out.println("x=" + x);

Answer of this question is yes, it's possible that thread T2 may print x=0. Why? Because without any instruction to compiler e.g. synchronized or volatile, bExit=true might come before x=1 in compiler reordering. Also, x=1 might not become visible in Thread 2, so Thread 2 will load x=0. Now, how do you fix it?

One suggests to make both threads synchronized on a common mutex, another one said make both variables volatile. Both are correct, as it will prevent reordering and guarantee visibility.

But the best answer is you just need to make bExit as volatile, then Thread 2 can only print x=1. x does not need to be volatile because x cannot be reordered to come after bExit=true when bExit is volatile.

Compare the sleep() and wait() methods in Java from the perspective of holding the lock/monitor

When wait() method is called thread releases the given object lock. But when sleep() method is called from synchronized block or method, the thread doesn't release the lock that this thread holds.

What is the volatile keyword? How and why would you use it? Can volatile make a non-atomic operation to atomic?

In Java, each thread has its own stack, including its own copy of variables it can access. When the thread is created, it copies the value of all accessible variables into its own stack. The volatile keyword basically says to the JVM “Warning, this variable may be modified in another Thread”. In all versions of Java, the volatile keyword guarantees global ordering on reads and writes to a variable. This implies that every thread accessing a volatile field will read the variable's current value instead of (potentially) using a cached value.

The volatile keyword is also useful for 64-bit types like long and double since they are written in two operations. Without the volatile keyword you risk stale or invalid values.

In Java5 or later, volatile modifier also helps to prevent reordering of code by compiler and volatile reads and writes establish a happens-before relationship, much like acquiring and releasing a mutex. Basically, Java Memory model inserts a write barrier after you write to a volatile variable and a read barrier before you read it. Which means, if you write to volatile field then its guaranteed that any thread accessing that variable will see the value you wrote.

Using volatile may be faster than a lock, but it will not work in some situations. The range of situations in which volatile is effective was expanded in Java5; in particular, in double checked locking (DCL) of singleton class without volatile modifier it's possible for another thread in Java to see half initialized state of INSTANCE variable, but with volatile variable guaranteeing happens-before relationship, all the write will happen on volatile INSTANCE before any read of INSTANCE variable. See Double-checked locking now works correctly.

volatile makes reading / writing of the value consistent, but it does not make read+write operation atomic: l++ (long l) can still be interrupted after the value was read but before it was written back. In that respect AtomicLong behaves correctly.

One common example for using volatile is for a flag to terminate a thread. If you've started a thread, and you want to be able to safely interrupt it from a different thread, you can have the thread periodically check a flag (i.e., to stop it, set the flag to true). By making the flag volatile, you can ensure that the thread that is checking its value will see that it has been set to true without even having to use a synchronized block. For example:

public class Foo extends Thread {
    private volatile boolean close = false;
    public void run() {
        while(!close) {
            // do work
        }
    }
    public void close() {
        close = true;
        // interrupt here if needed
    }
}

See also When do you use volatile variables?.

Self-test

  1. C
  2. D
  3. ED
  4. B, F
  5. D
  6. A
  7. A, B, DA, D
  8. E
  9. DG
  10. B
  11. A, F, H
  12. A, C, DA, C, D, E, F
  13. A, C, DC, D
  14. DA
  15. D, FF
  16. C, EF
  17. C, D, EC, E
  1. C
  2. D
  3. D
  4. B, F
  5. D
  6. A
  7. A, B, DA, D
  8. E
  9. G
  10. B
  11. A, F, H
  12. A, C, D, E, F
  13. C, D
  14. B, DA
  15. F
  16. C, EF
  17. C, E

Chapter 10 (Development)

Self-test

  1. C, E
  2. CB
  3. C
  4. A, CA
  5. D
  6. C, DC, D, E
  7. AA, C
  8. E
  9. B, C, E, FC, F
  10. A, C, GC, G
  11. CA
  12. C, F, GF, G
  1. C, E
  2. CB
  3. C
  4. A
  5. D
  6. DC, D, E
  7. A, C
  8. E
  9. C, F
  10. C, G
  11. CA
  12. F, G

Questions mixup

Which of the following statements are true?

  • enum is a keyword starting from Java6.
    :NO: enum is a keyword starting from Java5.
  • default is a reserved word.
    :YES:
  • true is a literal.
    :YES:
  • Using of static imports is a better alternative to implementing an interface that declares only constants.
    :YES: Interfaces should serve their goals.
  • Marking all class fields as private makes them protected from accidental corruption and improves encapsulation.
    :YES:
  • The benefit of encapsulation is that the implementation of a class can be changed without breaking code that uses it.
    :YES:
  • Constructors are not inherited.
    :YES:
  • Constructors can be overridden.
    :NO: Constructors can only be overloaded.
  • The default constructor is explicitly declared constructor that takes no arguments.
    :NO: As from JLS §8.8.9, default constructor is one implicitly declared by compiler. The explicitly declared constructor is called no-args constructor.
  • The default constructor takes a parameter of void.
    :NO: The default constructor takes no parameters.
  • Any method may contain a call to this() or super().
    :NO: Methods are not allowed to use this(), and may use super() only if it overrides the method from the parent class.
  • A final method cannot be overloaded.
    :NO: A final method cannot be overridden, but overloading is fine.
  • A static final method cannot be hidden.
    :YES: Attempt to define a method with the same signature in descendant class will cause compile-time error.
  • A static method may not be overridden.
    :YES: Once you've declared a method as static, it cannot be overridden by non-static, however it can be hidden by another static method (see Overriding Vs Hiding).
  • When a package-private method is overridden by a package-private method in a different package compile-time error occurs.
    :NO: A package-private method cannot be directly overridden by a method in a different package and compiler does not trigger an error, so the class in each package will use it's own package-private method.
  • An abstract class must contain at least one abstract method.
    :NO: Abstract class need not contain any abstract methods.
  • An abstract class must be extended.
    :NO: The compiler does not care whether or not a class is extended.
  • An abstract class may not have any final methods.
    :NO:
  • A final class must contain at least one final method.
    :NO:
  • An anonymous inner class cannot have any constructors except the default one.
    :YES:
  • An anonymous inner class may implement at most one interface.
    :YES:
  • An anonymous inner class that implements one interface may extend a parent class other than java.lang.Object.
    :NO:
  • A class defined within a method can access any variables accessible by the enclosing method.
    :NO: A method class may only access the fields of the enclosing class and final local variables and parameters. This limitation was introduced to prevent anonymous class from modification of local variables (see Cannot refer to a non-final variable inside a class defined in a method) once they are out-of-scope (for example, enclosing method returns anonymous class instance that modifies some local primitive variable that is destroyed once enclosing method has returned). However in Java8 this limitation is softened by introduction of effectively final variable (this is one which is not explicitly modified, see Why are only final variables accessible in anonymous class?).
  • An instance of a nested class can be created without an instance of its enclosing class.
    :YES:
  • You might want to define a method as native to access hardware that Java does not know about.
    :YES:
  • An enum may be subclassed.
    :NO:
  • An enum may implement an interface.
    :YES:
  • If enum declares a constructor, it should have private modifier.
    :NO: It may have a default access (no modifiers).
  • By default the methods name() and toString() of enum return its constant name.
    :YES:
  • The following path org/my company/module/Service.java is a valid location for Java class in corresponding package.
    :NO: As package name may not contain spaces, the given file location is invalid.
  • A Java file containing a top-level class and an inner class will only produce one .class output file after compilation.
    :NO: An extra .class file (like A$1.class) will be created.
  • It is possible to load the same class by two ClassLoaders.
    :YES: And more over it will break singleton concept in the scope of JVM (this is a desired behaviour).
  • If the source file contains large number of imports, it affects the compilation time (so it takes slightly more time).
    :YES: However it does not affect the runtime, as classes are loaded on demand.
  • The following code will instantiate the corresponding inner class: Outer.Inner i = Outer().new Inner();
    :NO: as code does not compile. The correct is Outer.Inner i = new Outer().new Inner();
  • When new operator is called from a function, the memory is allocated on the stack.
    :NO: new operator always allocates the memory on heap.
  • Arguments are always passed by value in Java.
    :NO: Primitives and object references are passed by value. Objects are passed by reference.
  • (x instanceof String[]) == (x != null && x.getClass().isArray() && x.getClass().getComponentType() == String.class)
    :YES:
  • In order to place the string instance s to string pool, one need to call s.intern().
    :YES:
  • All string literals are interned.
    :YES: See JLS §3.10.5:

    String literal always refers to the same instance of class String, because string literals – or, more generally, strings that are the values of constant expressions, are “interned”.

  • java.io.Console#readPassword() returns char[] because String is not secure as it can be interned and thus stay in memory uncontrolled long.
    :YES:
  • String.substring() may result a memory leak before Java7.
    :YES: in certain extent, as before before Java7 String.substring() created a new String instance that held the reference to parent character array (and kept start/end index in it), hence 1GB long string could not be freed just because e.g. 5 chars of it could be still referenced. In Java7 the new character array is always created, which solves the problem but results worth memory usage in case when the string and substrings are used, plus O(n) complexity to create a substring.
  • Most JVMs use the double indirection technique for object references, which allows garbage collector to relocate objects and reduce memory fragmentation.
    :YES:
  • The call to System.free() will suggest the JVM to perform garbage collection.
    :NO: Correct is System.gc().
  • Runtime.halt(int) method executes shutdown hooks the same way as System.exit(int).
    :NO: as shutdown hooks are not executed
  • double can contain a 64-bit integer.
    :NO: Not every 64-bit integer may fit into double precisely.
  • The range of values for int type depends on underlying platform: for 32-bit platforms it is [-232 .. 232 - 1] and for 64-bit platforms it is [-264 .. 264 - 1].
    :NO: int type is fixed to range [-232 .. 232 - 1] and does not depend on the platform.
  • myarray.length() returns the number of elements in the given array.
    :NO: Correct syntax is myarray.length
  • The expression long microsPerDay = 24 * 60 * 60 * 1000 * 1000; will correctly assign the number of microseconds in a day to the variable.
    :NO: The given expression will cause an overflow as integer is multiplied by an integer all the time and Java does not perform type promotion from target type. Correct would be long microsPerDay = 24L * 60 * 60 * 1000 * 1000;
  • System.out.println(myarray.toString()); will print all array elements to the screen.
    :NO: Correct will be Arrays.toString(myarray); otherwise you will see something like [Ljava.lang.Object;@64ab4d on console.
  • It is possible to create a two-dimensional array in Java (e.g. the array where a[x][y] == a[x*sizeof(row) + y]).
    :NO: Each row in Java array is an independent array.
  • Assignment Float a[] = {1.2}; is correct.
    :NO: Autoboxing works fine, but the number has type double so correct would be Float a[] = {1.2f}; or Double a[] = {1.2};. Note that integer array elements a automatically downcasted to short or byte (with range checking): byte a[] = {1, 2};.
  • Declaration char a = '\u000A'; does not cause a compile-time error.
    :NO: It's very tricky... The special characters 0x0A (line feed) and 0x0D (carriage return) as well as other unicode characters are interpreted literally as “end of line” and are processed very early in the translation process (at pre-compilation stage). So the compiler will trigger an error.
  • Knowing that 0x5C is backslash (\), the following code System.out.println("\uuuuu005c""); does not cause a compile-time error.
    :YES: The series of u symbols after slash are treated as one and escape unicode sequence is converted to character at pre-compiler stage. Thus compiler really sees System.out.println("\"");
  • The following code String path = "map.txt"; // Taken from C:\cygwin\usr\share\map.txt does not cause compile-time error.
    :NO: Pre-compiler raises an error Invalid unicode \usr, even if it is placed in comment.
  • short[] a = { 0 }; Cloneable c = a; is a correct assignment.
    :YES: Arrays can be casted to Cloneable / Serializable interfaces and to Object class.
  • The statement Byte a[] = null; Byte b[][] = null; boolean result = a == b; is correct and results true.
    :NO: The statement causes a compile-time error Incompatible operand types Byte[] and Byte[][].
  • new String() instanceof List causes compile-time error.
    :YES: Not syntax error, as one may expect, but Inconvertible types; cannot cast 'java.lang.String' to 'java.util.List' as left and right operands should have common ancestor in class tree.
  • The code Math.ceil(Math.random() * 10) always generates integer numbers from 1 to 10.
    :NO: First, the range will be [0..10), and the numbers will be float.
  • The initialization operator (byte b = 2) and incrementing, subtraction, multiplication, division operators (e.g. b += 2) are the only cases in Java when explicit casting is not necessary.
    :YES:
  • Unary arithmetic operators ++ and -- do not perform promotion on their operands.
    :YES:
  • The + expression when one of the operands is not primitive numeric type, a string or a literal, is illegal.
    :YES: and in particular String s = true + ""; is correct expression. Mind also the null, true, false literals can only be added (or added to) a string.
  • When a byte is added to a char, then the result type is short.
    :NO: The result type is int.
  • The expression i ^ i always results 1 for any i of type int.
    :NO: It always results 0 for any non-fractional type (byte, short, int and, surprise, char).
  • int x = 6; x = ~x; is a correct expression.
    :YES:
  • The result of this expression -50 >> 1 is 25.
    :NO: The result is -25 as >> operator is a signed right shift, and shifting by one is the same as dividing by two.
  • byte x = -1; x >>>= 5; results a positive value in x.
    :NO: It would be true if x has int type. However in this case byte is widened to int, non-signed shift takes place and the result is downcasted to byte.
  • The result of this expression -5 % 2 is 1.
    :NO: The result is -1.
  • ArithmeticException is always raised by division operator when division by zero occurs (e.g. float a = 20.0f / 0).
    :NO: For floating point calculations the above expression results e.g. Float.POSITIVE_INFINITY (or analogous Double.POSITIVE_INFINITY).
  • ArithmeticException can only be raised by division operator when division by zero occurs (e.g. int a = 5 / 0).
    :NO: It can also be raised by modulo operator (e.g. int a = 5 % 0).
  • 0.1 * 3 == 0.3
    :NO: Because floating numbers cannot be represented precisely, hence should be rounded before exact matching.
  • For float variable x, the most appropriate way to test for a NaN is if (x != x) ... // then x is NaN.
    :NO: Above method works OK, as for any x, x != Float.NaN (and all comparison operators are false – if you know SQL, this is very close to NULL), but the most readable code is if (Float.isNaN(x)) ... // then x is NaN.
  • Integer.MIN_VALUE == -Integer.MIN_VALUE
    :YES: As integer minimal value does not have it's positive counter-part, its negation results the same value. This is also true on binary level.
  • Math.abs(Integer.MIN_VALUE) == Integer.MAX_VALUE
    :NO: Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE
  • Math.min(Double.MIN_VALUE, 0.0d) returns 0.0.
    :YES: Because Double.MIN_VALUE is 2-1074 – a double constant whose magnitude is the least among all positive double values.
  • In case the type of x is a class and the type of y is an interface then the assignment x = y; is never legal.
    :NO: It is always legal in case x is Object and is not legal in all other cases.
  • The argument for a switch statement may be of type long.
    :NO: Only char, byte, short, int or enumeration type are allowed.
  • The code for(;;) {} causes the endless loop.
    :YES:
  • It is possible to define two variables in for-statement this way: for (int i = 0, int j = 0; i < 10 && j < 10; i++) ...;
    :NO: Correct would be for (int i = 0, j = 0; i < 10 && j < 10; i++) ...;
  • The statement is valid: if (Boolean.valueOf("True")) { ... }
    :YES: The expression for if-statement should be either boolean or Boolean (unboxed).
  • Assertions are used to enforce class invariants.
    :YES:
  • The code int x = 5; assert (x == 6) ? "x == 6" : "x != 6"; with result AssertionError with message x != 6.
    :NO: It does not compile, because 1st part of assert statement should be a boolean expression, not a string.
  • Checking the postconditions of public and private methods is appropriate with assertions.
    :YES: Only preconditions of a public method is advised to check explicitly and throw IllegalArgumentException when conditions are not satisfied.
  • The exception's stack trace is constructed when printStackTrace() method is called.
    :NO: It is recorded when the exception is constructed. printStackTrace() method can be called from different place(s) in the code irrelevant to the place where the exception was raised.
  • Long.valueOf(5).equals(5)
    :NO: The argument value passed to equals() is auto-boxed to Integer, which is not equal to Long. However Integer.valueOf(1).equals(1) is true.
  • new Integer(0) == 0.
    :NO: The new object is compared by reference with boxed (cached) integer.
  • Suppose o1 and o2 are references to instances of java.lang.Object. If (o1 == o2) == false, then o1.equals(o2) can be true.
    :NO: It comes from implementation of Object#equals() method which checks the object references. For other object types (different from java.lang.Object) the above statement can be true.
  • return (int) Math.random(); is a valid hashCode() method implementation for any class.
    :YES: It always returns 0 as double in range [0..1.0) is always casted to 0 which does not violate the contract of hashcode function.
  • Arrays.toString(Object[]) can deal with nested arrays and circular object references.
    :NO: The other function Arrays.deepToString(Object[]) can deal with all above.

  • Java collection classes make it unnecessary to use arrays.
    :NO: One can still benefit from arrays in certain applications (raster graphics, encryption, binary file formats).
  • The elements of collection can be ordered by using the Collections.order() method.
    :NO: The order of the elements is a feature of a collection implementation, and there is no Collections.order() method.
  • The ordered collection can be transformed to sorted collection by using Collections.sort() method.
    :NO: Not every ordered collection can be sorted by Collections.sort() for example, LinkedHashSet is an ordered collection. For List interface implementations the above statement is also false, as it will temporary sort the collection which will become unsorted in general case when a new element is added.
  • Vector<Map> v; is a correct Java5 declaration.
    :YES: Even though the parametrization of Map is not defined, this “mixture” is allowed in Java5.
  • new TreeSet<Object>() {{ add(null); }}; does not cause NullPointerException in Java6.
    :YES: In Java6 (and earlier) insertion of null into empty TreeSet is acceptable but from Java7 onward it is non-acceptable (see bug#5045147). In Java6 it causes various side-effects as: add(null); add(obj); causes NullPointerException depending on whether obj.compareTo(Object) is null-safe, add(null); remove(null); also causes NullPointerException.
  • Is Stream an Iterable?
    :NO: And the philosophical reason is that normally streams cannot be iterated from the beginning. Workarounds:
    1. Java 7+: Create Iterable on-the-fly:
      Stream<String> lines = ...;
      for (String line : (Iterable<String>) () -> lines.iterator()) {
          ...
      }
    2. Java 8+: Do functional interface cast:
      Stream<String> lines = ...;
      for (String line : (Iterable<String>) lines::iterator) {
          ...
      }
  • The following code creates a stream from the iterator:
    Iterable<Object> iterable = Arrays.asList(...);
     
    StreamSupport.stream(iterable.spliterator(), false);


    :YES:

  • The following code filters out optionals which are empty:
    listOfOptionals.stream().flatMap(Optional::stream)...;


    :YES:


  • File dir = new File("."); dir.chDir(".."); will change to the directory above the current directory.
    :NO: There is no chDir() method. And there is no way to change the current directory in Java.
  • StreamReader class is designed to read characters from the stream (e.g. file or socket).
    :NO: There is no such class.
  • BufferedInputStream#peak() method can be used to get the next character from the stream without shifting the stream position forward.
    :NO: There is no peak() method in this class.
  • OutputStreamWriter must take a character encoding as a constructor parameter.
    :NO: It may take a character encoding as a constructor parameter.
  • InputStreamReader may act as a constructor to OutputStreamReader to convert between character sets.
    :NO: InputStreamReader accepts only InputStream as constructor argument.
  • It is possible to configure timeout for socket write() operation.
    :NO: Socket#setSoTimeout() sets it only for ServerSocket.accept(), SocketInputStream.read() and DatagramSocket.receive() operations (see How to implement timeout for socket write() operation?).
  • All constructors of RandomAccessFile must take a string mode parameter.
    :YES:
  • rw, r, w are valid mode parameters for RandomAccessFile constructor.
    :NO: w is not valid mode. However rws (open for reading and writing with immediate updating of data and metadata changes) and rwd (open for reading and writing with immediate updating of data but not metadata changes) are valid modes.
  • The default encoding for OutputStreamWriter is ASCII.
    :NO: The default encoding is determined by file.encoding system property.
  • System.out is the instance of OutputStream.
    :NO: It is an instance of PrintStream.
  • The function OutputStream#write(int b) writes an integer to the stream.
    :NO: It actually writes a byte.
  • AutoCloseable is an empty interface that extends Closeable and marks resources that can participate in try-with-resources construction.
    :NO: Vice versa, Closeable extends AutoCloseable. The reason why AutoCloseable was introduced is because there is a demand for close() to throw any exception (for example, ResultSet wants to throw SQLException), see also Implement Closeable or AutoCloseable?
  • Java can display Unicode characters only if underlying filesystem supports unicode.
    :NO: Java can display unicode characters independently.
  • UTF-8 characters are all 8 bits.
    :NO: UTF-8 characters may have 1, 2, 3, 4, 5 or 6 byte sequences.
  • Out of JdbcRowSet, JoinRowSet, CachedRowSet, FilteredRowSet and WebRowSet only CachedRowSet is a disconnected row set.
    :NO: Out of these five only JdbcRowSet| is connected RowSet, rest of them are disconnected row sets, see Difference between Connected vs Disconnected RowSet in Java JDBC.

  • In order to provide a custom class deserialization one need to implement the private void readExternal() method for that class.
    :NO: The method should be either private Object readObject(java.io.ObjectInputStream in) (if class implements Serializable) or public void readExternal(java.io.ObjectInput in) (if class implements Externalizable).
  • static variables may not be transient.
    :NO: You can declare static transient int a; however static variables are anyway not serialized.
  • There is no limitation on constructors for the class that implement Externalizable interface for it to be correctly deserialized.
    :NO: This class must have a no-args constructor.
  • If the class implements Serializable and does not implement Externalizable, its nearest superclass that doesn't implement Serializable must have a no-args constructor.
    :YES:
  • The code public Animal clone() { return (Animal) super.clone(); } is valid clone() implementation of class Animal that extends Object.
    :NO: The CloneNotSupportedException must be dealt with.
  • To encrypt the object during serialization, one need to wrap it into javax.crypto.SealedObject.
    :YES: And to perform automatic signing of serialized data, wrap the object into java.security.SignedObject.
  • Byte class has readResolve() method to enforce the flyweight pattern.
    :NO: None of the primitive wrappers resolve the object from cache during deserialization.

  • wait()/notify() methods is better (recommended) way to block a thread than pause()/suspend().
    :YES: because pause()/suspend() are deprecated as may cause a deadlock (locks of the thread are not released when it is paused).
  • start() method is used to tell a suspended thread that it has the opportunity to run.
    :NO: start() method is necessary to start a thread, but notify() is used to tell a pool of threads waiting for lock release that one of them can run.
  • A Java monitor must either extend Thread or implement Runnable.
    :NO: A monitor can be an instance of any class.
  • In order to specify which thread is notified, one need to call notify() method on that thread.
    :NO: There is no way to specify the thread to be notified: any (but only one) thread waiting for a given object will be notified.
  • The thread that calls notify() gives up the lock.
    :NO: This also means that the thread that called notify() and didn't exit the synchronized block (e.g. sleeps for a while) still holds that lock and prevents other threads from being run (see Does notify/notifyall release the lock being held).
  • In order to release a lock on the object obj one need to call obj.wait().
    :YES:
  • An object maintains one lock per each declared non-static synchronized method.
    :NO: There is only one lock per object regardless of number of synchronized methods.
  • A synchronized method should not call a different synchronized method of the current object as it will lock the thread.
    :NO: Once the lock is acquired by thread it may enter as many sections synchronized on this lock (object) as needed.
  • Is it possible to write code that can execute only if the current thread owns multiple locks.
    :YES: For example, you may embed one synchronized code block into another so the code in the most deep block will run only if thread has acquired all locks.
  • To ensure that multithreaded code does not deadlock, all threads need to yield from time to time.
    :NO:
  • Marking all class methods with synchronized modifier is enough to avoid concurrency issues with this class.
    :NO: All Vector methods are synchronized, but still the class is not thread-safe and will throw IllegalModificationException when one attempts to remove an element in one thread, while iterating over the elements in another.
  • The waiting thread should check the wakeup invariant in a loop to protect from spurious wakeup.
    :YES:
  • StringBuffer is generally faster than StringBuilder.
    :NO: It is vice versa as StringBuffer has all methods synchronized.
  • Synchronizing only a subset of a method can be useful when you want to hold the lock as briefly as possible, so that other threads can get their turn as soon as possible.
    :YES:

  • String q; if (!(Optional.ofNullable(q).filter(s -> !s.trim().isEmpty())).isPresent()) { ... } is equivalent to if (org.apache.commons.lang3.StringUtils.isBlank(q)) { ... }
    :NO: Actually it is equivalent to if (StringUtils.isNotBlank(q)) { … }
  • List<String> list = ...; if (Optional.ofNullable(list).orElseGet(() -> new ArrayList<String>()).size() == 0) { ... } is equivalent to if (org.apache.commons.collections4.CollectionUtils.isEmpty(list)) { ... }
    :YES:


  • Java6 uses time-slicing scheduling system for threads which is better than pre-emptive (co-operative) system, used in previous Java versions.
    :NO: Java scheduling system is platform dependent.
  • Directly subclassing Thread gives you access to more functionality of the Java threading capability than implementing the Runnable interface.
    :NO:
  • Threads are guaranteed to run with the priority that you set using the setPriority() method.
    :NO: Java does not make any promises about priority at runtime.
  • Threads inherit their priority from their parent thread.
    :YES: There is no “default” launch priority.
  • A thread created by a daemon thread is initially a non-daemon thread.
    :NO: A thread created by a daemon thread is initially also a daemon thread (and non-daemon thread spawns a non-daemon thread).
  • Garbage collector is a daemon thread.
    :YES: See Is garbage collector a daemon thread?
  • When an application begins running there is one non-daemon thread whose job is to execute main().
    :YES:
  • The JVM runs until there are no daemon threads.
    :NO: The JVM runs until there are no non-daemon threads. See also What is daemon thread in java.
  • Provided that there is a function public static <T> List<Future<T>> invokeAll(Stream<Callable<T>> tasks) { ... }'' the expression ''invokeAll(ids.map((id) -> () -> id)); is correct.
    :YES: See How to force lambda type to a specific one?

Other problems throughout the book

  • Remember that in chapter 1, when we talk about classes, we're referring to non-inner classes, or top-level classes (page 68). That is why following corrections apply:
    There can be only one public [top-level] class per source code file (page 11).
    A [top-level] class declaration with the public keyword gives all classes from all packages access to the public class (page 14).
    Protected and private class modifiers have sense for nested classes.
  • All interface methods are implicitly public and abstract (page 20).
    I can add that interface methods may only be marked with public and abstract modifiers or any their combination. If any is omitted, it is assumed.
  • All variables defined in an interface must be public, static, and final (page 20).
    I would say, the valid modifiers for variables are public, static, and final or any their combination. If any is omitted, it is assumed.
  • The test #15 in chapter 2 (page 170):
    The point that varargs methods have the lowest priority is discussed in chapter 3 in more details. Knowledge given in previous chapters is not enough to answer this question correctly.
  • In chapter 3 on page 206:

    Array elements are always, always, always given default values, regardless of where the array itself is declared or instantiated.

    declared should be read as defined. Imagine the code int[] a; that declares the array a which is not given the default values, while the code int[] a = {0}; declares and defines the array a.

  • The test answer #15 in chapter 3 (page 281):
    getBidValue() function is not defined in line 12. The code in answers section differs from one in questions on page 272, which is correct.
  • In chapter 4 on page 312 it is said concerning increment/decrement operators that

    … both operands are fully evaluated before the operator is applied.

    however these operators have only one operand (+= and family are called “assignment operators”).

  • In chapter 6 on page 456 in the example in the bottom of the page the array of strings is allocated and lost: String[] files = new String[100];. The same in chapter 7 on page 587: TreeSet<Integer> subset = new TreeSet<Integer>(); subset = (TreeSet) times.headSet(1600);
  • In chapter 7 on page 571 the runtime error cannot happen if the compilation succeeded.
  • In chapter 7 on page 577 the last bullet point should read “Comparator, which is passed as the secondthird argument”
  • On page 77 (chapter 3) it is said:

    Only variables and methods may be declared protected.

    Of course, the authors of the book have not introduced the inner classes by that moment, but for the sake of complicity I won't make statements like that.

  • On page 79 (chapter 3) it is said:

    A private method may be overridden by a private, default, protected, or public method.

    I think, that we can speak only about re-declaration of a private method, as overriding assumes that we can call a parent method (super()) which is impossible for private methods (see question regarding overriding and overloading static methods).

  • On page 107 (chapter 4) the code
    int i = 12;
    byte b = I

    should be read as:

    int i = 12;
    byte b = i;
  • On page 128 (chapter 4) the answer for test 12 is E. Any type, including classes with any kind of modifier, may appear inside the parentheses of a cast. however the test does not have this option (see page 125):

    Which of the following may legally appear as the new type (between the parentheses) in a cast operation? A. Abstract classes B. Final classes C. Primitives D. All of the above

  • On page 157 (chapter 5) in test 3 the option C is:
    int j = 0;
    for (int k=0, j+k != 10; j++,k++) {
        System.out.println("j=" + j + ", k=" + k);
    }

    but should be

    ...
    for (int k=0; j+k != 10; j++,k++) {
    ...

    otherwise it does not compile (and can't be the right answer).

  • On page 161 (chapter 5) test 10 says:

    Consider the following code:

    1. public class Assertification {
    2.   public static void main(String[] args) {
    3.     assert args.length == 0;
    4.   }
    5. }

    Which of the following conditions must be true in order for the code to throw an AssertionError? Assume you are using release 5.0. (Choose all that apply.) A. The source code must be compiled with the -source 1.5 flag. …

    and the answer on page 165 is A, B, D, while the explanation contradicts with answer (I suppose it should be B, D):

    A, B, D. … 5.0 does not require a -source flag. So A is not a requirement.

  • On page 192 (chapter 6) there is an error in example:
    17. add(
    18.   new Button("Hello " + 1) {
    19.     // initializer
    20.       addActionListener(
    21.         new ActionListener() {

    should read:

    17. add(
    18.   new Button("Hello " + 1) {
    19.     { // initializer
    20.       addActionListener(
    21.         new ActionListener() {

    otherwise it does not compile.

  • On page 211 (chapter 6) for test 10 it is said:

    A is illegal because the list of names must be terminated by a semicolon.

    which is false statement: the semicolon at the end of list is optional. The corresponding prove is on page 207:

    10. Which of the following are legal enums? A. enum Animals { LION, TIGER, BEAR } …

  • On page 211 (chapter 6) for test 15 is is said:

    A. An anonymous inner class must appear inside a block of code.

    I am not completely agreed with this statement. Consider the code below:

    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
     
    public class Test {
     
        private MouseAdapter adapter = new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
            }
        };
    }

    This code creates an anonymous class not in code section, but in class member declaration section. However, technically this code is transformed into:

    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
     
    public class Test {
     
        private MouseAdapter adapter;
     
        {
            adapter = new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    super.mouseClicked(e);
                }
            };
        }
    }

    which correlates with the statement, but my position is still the same.

  • The page 246 (chapter 7) states the following as the answer for question 7:

    A monitor is an instance of any class that has synchronized code.

    I disagree. The more correct is just to say: “A monitor can be an instance of any class”. Consider the following example:

    public class Test {
     
        public void doJob()    {
            int a = 1;
     
            synchronized (Boolean.TRUE) {
                a++;
            }
        }
    }

    What is the monitor in this case: instance of class Test (that has synchronized code) or instance of class Boolean?

  • The page 295 (chapter 8) reads the following:

    %b Formats a boolean value (wrapper or primitive)

    Well, you can't pass the primitive to Formatter#format() as varargs are always passed as objects.

  • The page 307 (chapter 8) states the following for question 12:
    Map<String> names = new HashMap<String>();

    This declaration is not correct, as Map needs two parametrization types. It should be Set<String> names = new HashSet<String>() – then the answers are matching the question.

  • The question 6 on page 340 (chapter 9) is missing ; on line 8.
  • The question 4 on page 477 (Appendix A) has labels for answers shifted, as A. Which statements are true? is wrongly labelled.
  • The question 28 on page 484 (Appendix A) has a minor problem: E. .start() not actually an answer, but a part of previous line.
programming/java/java_certification.txt · Last modified: 2015/03/26 17:45 by dmitry
 
 
Recent changes RSS feed Driven by DokuWiki