Java Puzzlers’ TM
Greatest Hits
Joshua Bloch
Neal Gafter
1 Java Puzzlers’ Greatest Hits
Introduction
• Eight more Java programming language puzzles
TM
Short program with curious behavior
What does it print? (multiple choice)
The mystery revealed
How to fix the problem
The moral
• Covers language and core libraries
No GUI or enterprise
2 Java Puzzlers’ Greatest Hits
1. “A Big Delight in Every Byte”
class Delight {
public static void main(String[] args) {
for (byte b = Byte.MIN_VALUE;
b < Byte.MAX_VALUE; b++) {
if (b == 0x90)
System.out.print("Joy! ");
}
}
}
3 Java Puzzlers’ Greatest Hits
What Does It Print?
class Delight {
public static void main(String[] args) {
for (byte b = Byte.MIN_VALUE;
b < Byte.MAX_VALUE; b++) {
if (b == 0x90)
System.out.print("Joy! ");
}
}
}
(a) Joy!
(b) Joy! Joy!
(c) Nothing
(d) None of the above
4 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) Joy!
(b) Joy! Joy!
(c) Nothing
(d) None of the above
Program compares a byte with an int;
byte is promoted with surprising results
5 Java Puzzlers’ Greatest Hits
Another Look
class Delight {
public static void main(String[] args) {
for (byte b = Byte.MIN_VALUE;
b < Byte.MAX_VALUE; b++) {
if (b == 0x90) // (b == 144)
System.out.print("Joy! ");
}
}
}
// (byte)0x90 == -112
// (byte)0x90 != 0x90
6 Java Puzzlers’ Greatest Hits
You Could Fix it Like This…
• Cast int to byte
if (b == (byte)0x90)
System.out.println("Joy!");
• Or convert byte to int, suppressing sign
extension with mask
if ((b & 0xff) == 0x90)
System.out.println("Joy!");
7 Java Puzzlers’ Greatest Hits
…But This is Even Better
public class Delight {
private static final byte TARGET = 0x90; // Won’t compile!
public static void main(String[] args) {
for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++)
if (b == TARGET)
System.out.print("Joy!");
}
}
Delight.java:2: possible loss of precision
found : int
required: byte
private static final byte TARGET = 0x90; // Won’t compile!
^
8 Java Puzzlers’ Greatest Hits
The Best Solution, Debugged
public class Delight {
private static final byte TARGET = (byte) 0x90; // Fixed
public static void main(String[] args) {
for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++)
if (b == TARGET)
System.out.print("Joy!");
}
}
9 Java Puzzlers’ Greatest Hits
The Moral
• a byte isn’t an int
• Be careful when mixing primitive types
• Compare like-typed expressions
Cast or convert one operand as necessary
Declared constants help keep you in line
10 Java Puzzlers’ Greatest Hits
2. “No Pain, No Gain”
public class Rhymes {
private static Random rnd = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch(rnd.nextInt(2)) {
case 1: word = new StringBuffer('P');
case 2: word = new StringBuffer('G');
default: word = new StringBuffer('M');
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
} Thanks to Mike “madbot” McCloskey
11 Java Puzzlers’ Greatest Hits
What Does It Print?
public class Rhymes {
private static Random rnd = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch(rnd.nextInt(2)) {
case 1: word = new StringBuffer('P');
case 2: word = new StringBuffer('G');
default: word = new StringBuffer('M');
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
}
(a) Pain, Gain, or Main (varies randomly)
(b) Pain or Main (varies randomly)
(c) Main (always)
(d) None of the above
12 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) Pain, Gain, or Main (varies randomly)
(b) Pain or Main (varies randomly)
(c) Main (always)
(d) None of the above: ain (always)
The program has three separate bugs.
One of them is quite subtle.
13 Java Puzzlers’ Greatest Hits
Another Look
public class Rhymes {
private static Random rnd = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch(rnd.nextInt(2)) { // No breaks!
case 1: word = new StringBuffer('P');
case 2: word = new StringBuffer('G');
default: word = new StringBuffer('M');
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
}
14 Java Puzzlers’ Greatest Hits
You Could Fix it Like This…
public class Rhymes {
private static Random rnd = new Random();
public static void main(String[] args) {
StringBuffer word = null;
switch(rnd.nextInt(3)) {
case 1: word = new StringBuffer("P"); break;
case 2: word = new StringBuffer("G"); break;
default: word = new StringBuffer("M"); break;
}
word.append('a');
word.append('i');
word.append('n');
System.out.println(word);
}
}
15 Java Puzzlers’ Greatest Hits
But This is Even Better
public class Rhymes {
public static void main(String args[]) {
String a[] = { "Main", "Pain", "Gain" };
System.out.println(randomElement(a));
}
private static Random rnd = new Random();
private static String randomElement(String[] a) {
return a[rnd.nextInt(a.length)];
}
}
16 Java Puzzlers’ Greatest Hits
The Moral
• Use common idioms
If you must stray, consult the documentation
• Chars are not strings; they’re more like ints
• Remember breaks in switch statement
• Watch out for fence-post errors
• Watch out for sneaky puzzlers
17 Java Puzzlers’ Greatest Hits
3. “The Name Game”
public class Names {
private final Map m = new IdentityHashMap();
public void Names() {
m.put("Mickey", "Mouse");
m.put("Mickey", "Mantle");
}
public int size() { return m.size(); }
public static void main(String args[]) {
Names names = new Names();
System.out.println(names.size());
}
}
18 Java Puzzlers’ Greatest Hits
What Does It Print?
public class Names {
private final Map m = new IdentityHashMap();
public void Names() {
m.put("Mickey", "Mouse");
m.put("Mickey", "Mantle");
}
public int size() { return m.size(); }
public static void main(String args[]) {
Names names = new Names();
System.out.println(names.size());
}
}
(a) 0
(b) 1
(c) 2
(d) It varies
19 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) 0
(b) 1
(c) 2
(d) It varies
No programmer-defined constructor.
(IdentityHashMap is a red herring.)
20 Java Puzzlers’ Greatest Hits
Another Look
public class Names {
private final Map m = new IdentityHashMap();
public void Names() { // Not a constructor!
m.put("Mickey", "Mouse");
m.put("Mickey", "Mantle");
}
public int size() { return m.size(); }
public static void main(String args[]) {
Names names = new Names(); // Invokes default!
System.out.println(names.size());
}
}
21 Java Puzzlers’ Greatest Hits
How Do You Fix It?
public class Names {
private final Map m = new HashMap();
public Names() { // No return type
m.put("Mickey", "Mouse");
m.put("Mickey", "Mantle");
}
public int size() { return m.size(); }
public static void main(String args[]) {
Names names = new Names();
System.out.println(names.size());
}
}
22 Java Puzzlers’ Greatest Hits
The Moral
• Method can have same name as constructor
Don’t ever do it
• Obey naming conventions
field, method(), Class, CONSTANT
• IdentityHashMap not a general-purpose Map
Uses identity in place of equality
Don’t use it unless you know it’s what you want
Useful for topology-preserving transformations
• (String literals are interned)
23 Java Puzzlers’ Greatest Hits
4. “Larger Than Life”
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private final int beltSize;
private static final int CURRENT_YEAR =
Calendar.getInstance().get(Calendar.YEAR);
private Elvis() { beltSize = CURRENT_YEAR - 1930; }
public int beltSize() { return beltSize; }
public static void main(String[] args) {
System.out.println("Elvis wears size " +
INSTANCE.beltSize() + " belt.");
}
}
24 Java Puzzlers’ Greatest Hits
What Does It Print?
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private final int beltSize;
private static final int CURRENT_YEAR =
Calendar.getInstance().get(Calendar.YEAR);
private Elvis() { beltSize = CURRENT_YEAR - 1930; }
public int beltSize() { return beltSize; }
public static void main(String[] args) {
System.out.println("Elvis wears size " +
INSTANCE.beltSize() + " belt.");
}
}
(a) Elvis wears size 0 belt.
(b) Elvis wears size 76 belt.
(c) Elvis wears size -1930 belt.
(d) None of the above.
25 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) Elvis wears size 0 belt.
(b) Elvis wears size 76 belt.
(c) Elvis wears size -1930 belt.
(d) None of the above.
The value of CURRENT_YEAR is used before it is
initialized, due to circularity in class initialization.
26 Java Puzzlers’ Greatest Hits
Another Look
// Static initialization proceeds top to bottom.
public class Elvis {
// Recursive initialization returns immediately!
public static final Elvis INSTANCE = new Elvis();
private final int beltSize;
private static final int CURRENT_YEAR =
Calendar.getInstance().get(Calendar.YEAR);
private Elvis() { beltSize = CURRENT_YEAR - 1930; }
public int beltSize() { return beltSize; }
public static void main(String[] args) {
System.out.println("Elvis wears size " +
INSTANCE.beltSize() + " belt.");
}
}
27 Java Puzzlers’ Greatest Hits
How Do You Fix It?
public class Elvis {
private final int beltSize;
private static final int CURRENT_YEAR =
Calendar.getInstance().get(Calendar.YEAR);
// Make instance after other initialization complete
public static final Elvis INSTANCE = new Elvis();
private Elvis() { beltSize = CURRENT_YEAR - 1930; }
public int beltSize() { return beltSize; }
public static void main(String[] args) {
System.out.println("Elvis wears size " +
INSTANCE.beltSize() + " belt.");
}
}
28 Java Puzzlers’ Greatest Hits
The Moral
• Watch out for circularities in static initialization
One or more classes may be involved
Circularities aren’t necessarily wrong but…
Constructors can run before class fully initialized
Static fields can be read before they’re initialized
• Several common patterns are susceptible
Singleton (Effective Java, Item 2)
Typesafe Enum (Effective Java, Item 21)
Service Provider Framework (Effective Java, Item 1)
29 Java Puzzlers’ Greatest Hits
5. “All Strung Out”
public class Puzzling {
public static void main(String[] args) {
String s = new String("blah");
System.out.println(s);
}
}
class String {
private final java.lang.String s;
public String(java.lang.String s) {
this.s = s;
}
public java.lang.String toString() {
return s;
}
}
Thanks to Mike “madbot” McCloskey
30 Java Puzzlers’ Greatest Hits
What Does It Print?
public class Puzzling {
public static void main(String[] args) {
String s = new String("blah");
System.out.println(s);
}
}
class String {
private final java.lang.String s;
public String(java.lang.String s) {
this.s = s;
}
public java.lang.String toString() {
return s;
}
}
(a) Won’t compile
(b) blah
(c) Throws an exception
(d) Other
31 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) Won’t compile
(b) blah
(c) Throws an exception at runtime
(d) Other
NoSuchMethodError is thrown because the
Puzzling class is missing a main method.
32 Java Puzzlers’ Greatest Hits
Another Look
public class Puzzling {
public static void main(String[] args) {
String s = new String("blah");
System.out.println(s);
}
}
class String {
private final java.lang.String s;
public String(java.lang.String s) {
this.s = s;
}
public java.lang.String toString() {
return s;
}
}
33 Java Puzzlers’ Greatest Hits
How Do You Fix It?
public class Puzzling {
public static void main(String[] args) {
MyString s = new MyString("blah");
System.out.println(s);
}
}
class MyString {
String s;
public MyString(String s) {
this.s = s;
}
public String toString() {
return s;
}
}
34 Java Puzzlers’ Greatest Hits
The Moral
• Avoid name reuse in all its guises
hiding, shadowing, overloading, obscuring
• Don’t even think about reusing platform
class names!
35 Java Puzzlers’ Greatest Hits
6. “Lazy Initialization”
public class Lazy {
private static boolean initialized = false;
static {
Thread t = new Thread(new Runnable() {
public void run() {
initialized = true;
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
public static void main(String[] args) {
System.out.println(initialized);
}
}
36 Java Puzzlers’ Greatest Hits
What Does It Print?
public class Lazy {
private static boolean initialized = false;
static {
Thread t = new Thread(new Runnable() {
public void run() {
initialized = true;
} (a) true
}); (b) false
t.start();
try { (c) It varies
t.join(); (d) None of the above
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
public static void main(String[] args) {
System.out.println(initialized);
}
}
37 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) true
(b) false
(c) It varies
(d) None of the above: it deadlocks
Intuition: You wouldn’t believe us if we told you.
38 Java Puzzlers’ Greatest Hits
Another Look
public class Lazy {
private static boolean initialized = false;
static {
Thread t = new Thread(new Runnable() {
public void run() {
initialized = true; // Deadlocks here!
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
public static void main(String[] args) {
System.out.println(initialized);
}
}
39 Java Puzzlers’ Greatest Hits
How Do You Fix It?
• Do the initialization in the main thread
If it hurts when you do that, don’t do that!
40 Java Puzzlers’ Greatest Hits
The Moral
• Avoid background threads in class initialization
• Keep class initialization simple
41 Java Puzzlers’ Greatest Hits
7. “Long Division”
public class LongDivision {
private static final long MILLIS_PER_DAY
= 24 * 60 * 60 * 1000;
private static final long MICROS_PER_DAY
= 24 * 60 * 60 * 1000 * 1000;
public static void main(String[] args) {
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
42 Java Puzzlers’ Greatest Hits
What Does It Print?
public class LongDivision {
private static final long MILLIS_PER_DAY
= 24 * 60 * 60 * 1000;
private static final long MICROS_PER_DAY
= 24 * 60 * 60 * 1000 * 1000;
public static void main(String[] args) {
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
(a) 5
(b) 1000
(c) 5000
(d) Throws an exception
43 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) 5
(b) 1000
(c) 5000
(d) Throws an exception
Computation does overflow
44 Java Puzzlers’ Greatest Hits
Another Look
public class LongDivision {
private static final long MILLIS_PER_DAY
= 24 * 60 * 60 * 1000;
private static final long MICROS_PER_DAY
= 24 * 60 * 60 * 1000 * 1000; // >> Integer.MAX_VALUE
public static void main(String[] args) {
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
45 Java Puzzlers’ Greatest Hits
How Do You Fix It?
public class LongDivision {
private static final long MILLIS_PER_DAY
= 24L * 60 * 60 * 1000;
private static final long MICROS_PER_DAY
= 24L * 60 * 60 * 1000 * 1000;
public static void main(String[] args) {
System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
}
}
46 Java Puzzlers’ Greatest Hits
The Moral
• When working with large numbers, watch out
for overflow—it’s a silent killer
• Just because variable can hold result doesn’t
mean computation won’t overflow
• When in doubt, use larger type
47 Java Puzzlers’ Greatest Hits
8. “It’s Elementary”
public class Elementary {
public static void main(String[] args) {
System.out.println(12345 + 5432l);
}
}
48 Java Puzzlers’ Greatest Hits
What Does It Print?
public class Elementary {
public static void main(String[] args) {
System.out.println(12345 + 5432l);
}
}
(a) -22430
(b) 17777
(c) 66666
(d) None of the above
49 Java Puzzlers’ Greatest Hits
What Does It Print?
(a) -22430
(b) 17777
(c) 66666
(d) None of the above
Program doesn’t say what you think it does!
50 Java Puzzlers’ Greatest Hits
Another Look
public class Elementary {
public static void main(String[] args) {
12345
System.out.println( l);
+ 5432
}
}
1 - the numeral one
l - the lowercase letter el
51 Java Puzzlers’ Greatest Hits
How Do You Fix It?
public class Elementary {
public static void main(String[] args) {
System.out.println(12345 + 5432L);
}
}
52 Java Puzzlers’ Greatest Hits
The Moral
• Always use uppercase el (L) for long literals
Lowercase el makes the code unreadable
5432L is clearly a long, 5432l is misleading
• Never use lowercase el as a variable name
Not this: List l = new ArrayList();
But this: List list = new ArrayList();
• Don’t code like my brother
53 Java Puzzlers’ Greatest Hits
Some people say
seeing is believing…
54 Java Puzzlers’ Greatest Hits
Fraser 1908
55 Java Puzzlers’ Greatest Hits
Fraser 1908
56 Java Puzzlers’ Greatest Hits
Fraser 1908
57 Java Puzzlers’ Greatest Hits
Logvinenko 1999
58 Java Puzzlers’ Greatest Hits
Logvinenko 1999
59 Java Puzzlers’ Greatest Hits
Logvinenko 1999
60 Java Puzzlers’ Greatest Hits
Kitaoka 1998
61 Java Puzzlers’ Greatest Hits
Kitaoka 1998
62 Java Puzzlers’ Greatest Hits
Kitaoka 1998
63 Java Puzzlers’ Greatest Hits
Todorovic 1997
64 Java Puzzlers’ Greatest Hits
Todorovic 1997
65 Java Puzzlers’ Greatest Hits
Todorovic 1997
66 Java Puzzlers’ Greatest Hits
What’s going on here?
• Our brains construct what we see
• System evolved over millions of years
• This sort of art has only existed for hundreds
• Illusions methodically exploit artifacts in system
• Studying illusions yields deep insight into perception
67 Java Puzzlers’ Greatest Hits
Shameless Commerce Division
• 95 Puzzles
• 52 Illusions
• Tons of fun
68 Java Puzzlers’ Greatest Hits
Send Us Your Puzzlers!
[email protected]
69 Java Puzzlers’ Greatest Hits
Java Puzzlers’ TM
Greatest Hits
Joshua Bloch
Neal Gafter
70 Java Puzzlers’ Greatest Hits