Still More
Java Puzzlers TM
Joshua Bloch
Neal Gafter
1 Still More Java Puzzlers
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, enterprise, or Tiger features
2 Still More Java Puzzlers
1. “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);
}
}
3 Still More Java Puzzlers
What Does It Print?
(a) 5
(b) 1000
(c) 5000
(d) Throws an exception
4 Still More Java Puzzlers
What Does It Print?
(a) 5
(b) 1000
(c) 5000
(d) Throws an exception
Computation does overflow
5 Still More Java Puzzlers
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);
}
}
6 Still More Java Puzzlers
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);
}
}
7 Still More Java Puzzlers
The Moral
• When working with large numbers watch out
for overflow—it’s a silent killer
• Just because variable is big enough to hold result
doesn’t mean computation is of correct type
• When in doubt, use long
8 Still More Java Puzzlers
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 madbot (also known as Mike McCloskey)
9 Still More Java Puzzlers
What Does It Print?
(a) Pain, Gain, or Main (varies at random)
(b) Pain or Main (varies at random)
(c) Main (always)
(d) None of the above
10 Still More Java Puzzlers
What Does It Print?
(a) Pain, Gain, or Main (varies at random)
(b) Pain or Main (varies at random)
(c) Main (always)
(d) None of the above: ain (always)
The program has three separate bugs.
One of them is quite subtle.
11 Still More Java Puzzlers
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);
}
}
12 Still More Java Puzzlers
How Do You Fix It?
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);
}
}
13 Still More Java Puzzlers
The Moral
• Use common idioms
─ If you must stray, consult the documentation
• Chars are not strings; they’re more like ints
• Always remember breaks in switch statement
• Watch out for fence-post errors
• Watch out for sneaky puzzlers
14 Still More Java Puzzlers
3. “The Name Game”
public class NameGame {
public static void main(String args[]) {
Map m = new IdentityHashMap();
m.put("Mickey", "Mouse");
m.put("Mickey", "Mantle");
System.out.println(m.size());
}
}
15 Still More Java Puzzlers
What Does It Print?
(a) 0
(b) 1
(c) 2
(d) It varies
16 Still More Java Puzzlers
What Does It Print?
(a) 0
(b) 1
(c) 2
(d) It varies
We’re using an IdentityHashMap, but string
literals are interned (they cancel each other out)
17 Still More Java Puzzlers
Another Look
public class NameGame {
public static void main(String args[]) {
Map m = new IdentityHashMap();
m.put("Mickey", "Mouse");
m.put("Mickey", "Mantle");
System.out.println(m.size());
}
}
18 Still More Java Puzzlers
How Do You Fix It?
public class NameGame {
public static void main(String args[]) {
Map m = new HashMap();
m.put("Mickey", "Mouse");
m.put("Mickey", "Mantle");
System.out.println(m.size());
}
}
19 Still More Java Puzzlers
The Moral
• IdentityHashMap not a general-purpose Map
─ Don’t use it unless you know it’s what you want
─ Uses identity in place of equality
─ Useful for topology-preserving transformations
• (String literals are interned)
20 Still More Java Puzzlers
4. “More of The Same”
public class Names {
private Map m = new HashMap();
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());
}
}
21 Still More Java Puzzlers
What Does It Print?
(a) 0
(b) 1
(c) 2
(d) It varies
22 Still More Java Puzzlers
What Does It Print?
(a) 0
(b) 1
(c) 2
(d) It varies
No programmer-defined constructor
23 Still More Java Puzzlers
Another Look
public class Names {
private Map m = new HashMap();
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());
}
}
24 Still More Java Puzzlers
How Do You Fix It?
public class Names {
private 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());
}
}
25 Still More Java Puzzlers
The Moral
• It is possible for a method to have the same
name as a constructor
• Don’t ever do it
• Obey naming conventions
─ field, method(), Class, CONSTANT
26 Still More Java Puzzlers
5. “Shades of Gray”
public class Gray {
public static void main(String[] args){
System.out.println(X.Y.Z);
}
}
class X {
static class Y {
static String Z = "Black";
}
static C Y = new C();
}
class C {
String Z = "White";
}
Thanks to Prof. Dominik Gruntz, Fachhochschule Aargau
27 Still More Java Puzzlers
What Does It Print?
(a) Black
(b) White
(c) Won’t compile
(d) None of the above
28 Still More Java Puzzlers
What Does It Print?
(a) Black
(b) White
(c) Won’t compile
(d) None of the above
Field Y obscures member class Y (JLS 6.3.2)
The rule: variable > type > package
29 Still More Java Puzzlers
Another Look
public class Gray {
public static void main(String[] args){
System.out.println(X.Y.Z);
}
}
class X {
static class Y {
static String Z = "Black";
}
static C Y = new C();
}
class C {
String Z = "White";
}
The rule: variable > type > package
30 Still More Java Puzzlers
How Do You Fix It?
public class Gray {
public static void main(String[] args){
System.out.println(Ex.Why.z);
}
}
class Ex {
static class Why {
static String z = "Black";
}
static See y = new See();
}
class See {
String z = "White";
}
31 Still More Java Puzzlers
The Moral
• Obey naming conventions
─ field, method(), Class, CONSTANT
─ Single-letter uppercase names reserved
for type variables (new in J2SE 1.5)
• Avoid name reuse, except overriding
─ Overloading, shadowing, hiding, obscuring
32 Still More Java Puzzlers
6. “It’s Elementary”
public class Elementary {
public static void main(String[] args) {
System.out.println(54321 + 5432l);
}
}
33 Still More Java Puzzlers
What Does It Print?
(a) -22430
(b) 59753
(c) 10864
(d) 108642
34 Still More Java Puzzlers
What Does It Print?
(a) -22430
(b) 59753
(c) 10864
(d) 108642
Program doesn’t say what you think it does!
35 Still More Java Puzzlers
Another Look
public class Elementary {
public static void main(String[] args) {
System.out.println(5432 1 + 5432 l);
}
}
1 - the numeral one
l - the lowercase letter el
36 Still More Java Puzzlers
How Do You Fix It?
We won’t insult your intelligence
37 Still More Java Puzzlers
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();
38 Still More Java Puzzlers
7. “Down For The Count”
public class Count {
public static void main(String[] args) {
final int START = 2000000000;
int count = 0;
for (float f = START; f < START + 50; f++)
count++;
System.out.println(count);
}
}
39 Still More Java Puzzlers
What Does It Print?
(a) 0
(b) 50
(c) 51
(d) None of the above
40 Still More Java Puzzlers
What Does It Print?
(a) 0
(b) 50
(c) 51
(d) None of the above
The termination test misbehaves due to floating
point “granularity”
41 Still More Java Puzzlers
Another Look
public class Count {
public static void main(String[] args) {
final int START = 2000000000;
int count = 0;
for (float f = START; f < START + 50; f++)
count++;
System.out.println(count);
}
}
// (float) START == (float) (START + 50)
42 Still More Java Puzzlers
How Do You Fix It?
public class Count {
public static void main(String[] args) {
final int START = 2000000000;
int count = 0;
for (int f = START; f < START + 50; f++)
count++;
System.out.println(count);
}
}
43 Still More Java Puzzlers
The Moral
• Don’t use floating point for loop indices
• Not every int can be expressed as a float
• Not every long can be expressed as a double
• If you must use floating point, use double
─ unless you’re certain that float provides enough
precision and you have a compelling performance
need (space or time)
44 Still More Java Puzzlers
8. “Line Printer”
public class LinePrinter {
public static void main(String[] args) {
// Note: \u000A is Unicode representation for newline
char c = 0x000A;
System.out.println(c);
}
}
45 Still More Java Puzzlers
What Does It Print?
(a) Two blank lines
(b) 10
(c) Won’t compile
(d) It varies
46 Still More Java Puzzlers
What Does It Print?
(a) Two blank lines
(b) 10
(c) Won’t compile: Syntax error!
(d) It varies
Unicode escape breaks comment in two
47 Still More Java Puzzlers
Another Look
// Unicode escapes are processed before comments!
public class LinePrinter {
public static void main(String[] args) {
// Note: \u000A is unicode representation for newline
char c = 0x000A;
System.out.println(c);
}
}
// This is what the parser sees
public class LinePrinter {
public static void main(String[] args) {
// Note:
is Unicode representation for newline
char c = 0x000A;
System.out.println(c);
}
}
48 Still More Java Puzzlers
How Do You Fix It?
public class LinePrinter {
public static void main(String[] args) {
// Escape sequences (like \n) are fine in comments
char c = '\n';
System.out.println(c);
}
}
49 Still More Java Puzzlers
The Moral
• Unicode escapes are dangerous
─ Equivalent to the character they represent!
• Use escape sequences instead, if possible
• If you must use Unicode escapes, use with care
─ \u000A (newline) can break string literals,
char literals, and single-line comments
─ \u0022 (") can terminate string literals
─ \u0027 (') can terminate character literals
50 Still More Java Puzzlers
Conclusion
• Java platform is simple and elegant
─ But it has a few sharp corners—avoid them!
• Keep programs clear and simple
• If you aren’t sure what a program does,
it probably doesn’t do what you want
• Don’t code like my brother
51 Still More Java Puzzlers
Shameless Commerce Division
52 Still More Java Puzzlers
Send Us Your Puzzlers!
• If you have a puzzler for us, send it to
[email protected]53 Still More Java Puzzlers
Still More
Java Puzzlers TM
Don’t code like my brother.
Joshua Bloch
Neal Gafter
54 Still More Java Puzzlers