Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Java Puzzle

Download as pdf or txt
Download as pdf or txt
You are on page 1of 275
At a glance
Powered by AI
The presentation covers common Java programming pitfalls and how to avoid them. It also aims to teach Java concepts in an engaging way.

It will print 'woof' because static methods are resolved at compile-time based on the type of the reference variable rather than the actual object. So both invocations call Dog.bark().

It will print false because the Name class violates the hashCode contract by not overriding it to be consistent with equals(). So objects that are equal according to equals() may have different hash codes.

Java Puzzles

Filipp Shubin

Overall Presentation Goal

Learn some of the quirks of programming in general and the Java language in particular; Have fun!

Learning Objectives
As a result of this presentation, you will be able to:

Avoid some common programming pitfalls Have some fun while you are learning

1. All I Get is Static


01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 class Dog { public static void bark() { System.out.print("woof "); } } class Basenji extends Dog { public static void bark() { } } public class Bark { public static void main(String args[]) { Dog woofer = new Dog(); Dog nipper = new Basenji(); woofer.bark(); nipper.bark(); } }

What Does It Print?


(a) woof (b) woof woof (c) It varies

What Does It Print?


(a) woof (b) woof woof (c) It varies No dynamic dispatch on static methods

Another Look
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 class Dog { public static void bark() { System.out.print("woof "); } } class Basenji extends Dog { public static void bark() { } } public class Bark { public static void main(String args[]) { Dog woofer = new Dog(); Dog nipper = new Basenji(); woofer.bark(); nipper.bark(); } }

How Do You Fix It?


Remove static from the bark method

The Moral
Static methods can't be overridden

They can only be hidden

Dont hide static methods Never invoke static methods on instances


Not Instance.staticMethod() But Class.staticMethod()

2. What's in a Name?
01 public class Name { 02 private String first, last; 03 public Name(String first, String last) { 04 this.first = first; 05 this.last = last; 06 } 07 public boolean equals(Object o) { 08 if (!(o instanceof Name)) return false; 09 Name n = (Name)o; 10 return n.first.equals(first) && 11 n.last.equals(last); 12 } 13 public static void main(String[] args) { 14 Set s = new HashSet(); 15 s.add(new Name("Donald", "Duck")); 16 System.out.println( 17 s.contains(new Name("Donald", "Duck"))); 18 } 19 }

What Does It Print?


(a) True (b) False (c) It varies

What Does It Print?


(a) True (b) False (c) It varies
Donald is in the set, but the set cant find him. The Name class violates the hashCode contract.

Another Look
01 public class Name { 02 private String first, last; 03 public Name(String first, String last) { 04 this.first = first; 05 this.last = last; 06 } 07 public boolean equals(Object o) { 08 if (!(o instanceof Name)) return false; 09 Name n = (Name)o; 10 return n.first.equals(first) && 11 n.last.equals(last); 12 } 13 public static void main(String[] args) { 14 Set s = new HashSet(); 15 s.add(new Name("Donald", "Duck")); 16 System.out.println( 17 s.contains(new Name("Donald", "Duck"))); 18 } 19 }

How Do You Fix It?


Add a hashCode method:
public int hashCode() { return 31 * first.hashCode() + last.hashCode(); }

The Moral
If you override equals, override hashCode Obey general contracts when overriding See Effective Java, Chapter 3

3. Indecision
01 class Indecisive { 02 public static void main(String[] args) { 03 System.out.println(waffle()); 04 } 05 06 static boolean waffle() { 07 try { 08 return true; 09 } finally { 10 return false; 11 } 12 } 13 }

What Does It Print?


(a) true (b) false (c) None of the above

What Does It Print?


(a) true (b) false (c) None of the above
The finally is processed after the try.

Another Look
01 class Indecisive { 02 public static void main(String[] args) { 03 System.out.println(waffle()); 04 } 05 06 static boolean waffle() { 07 try { 08 return true; 09 } finally { 10 return false; 11 } 12 } 13 }

The Moral
Avoid abrupt completion of finally blocks

Wrap unpredictable actions with nested trys Don't return or throw exceptions

4. The Saga of the Sordid Sort


01 public class SordidSort { 02 public static void main(String args[]) { 03 Integer big = new Integer( 2000000000); 04 Integer small = new Integer(-2000000000); 05 Integer zero = new Integer(0); 06 Integer[] a = new Integer[] {big, small, zero}; 07 Arrays.sort(a, new Comparator() { 08 public int compare(Object o1, Object o2) { 09 return ((Integer)o2).intValue() 10 ((Integer)o1).intValue(); 11 } 12 }); 13 System.out.println(Arrays.asList(a)); 14 } 15 }

What Does It Print?


(a) [-2000000000, 0, 2000000000] (b) [2000000000, 0, -2000000000] (c) [-2000000000, 2000000000, 0] (d) It varies

What Does It Print?


(a) [-2000000000, 0, 2000000000] (b) [2000000000, 0, -2000000000] (c) [-2000000000, 2000000000, 0] (d) It varies (behavior is undefined)
The comparator is broken!

It relies on int subtraction Int too small to hold difference of 2 arbitrary ints

Another Look
01 public class SordidSort { 02 public static void main(String args[]) { 03 Integer big = new Integer( 2000000000); 04 Integer small = new Integer(-2000000000); 05 Integer zero = new Integer(0); 06 Integer[] a = new Integer[] {big,small,zero}; 07 Arrays.sort(a, new Comparator() { 08 public int compare(Object o1, Object o2) { 09 return ((Integer)o2).intValue() 10 ((Integer)o1).intValue(); 11 } 12 }); 13 System.out.println(Arrays.asList(a)); 14 } 15 }

How Do You Fix It?


Replace comparator with one that works
01 02 03 04 05 06 public int compare(Object o1, Object o2) { int i1 = ((Integer)o1).intValue(); int i2 = ((Integer)o2).intValue(); return (i2 < i1 ? -1 : (i2 == i1 ? 0 : 1)); }

The Moral
ints aren't integers! Think about overflow This particular comparison technique

OK only if max - min <= Integer.MAX_VALUE For example: all values positive

Dont write overly clever code

5. You're Such a Character


01 public class Trivial { 02 public static void main(String args[]) { 03 System.out.print("H" + "a"); 04 System.out.print('H' + 'a'); 05 } 06 }

What Does It Print?


(a) HaHa (b) Ha (c) None of the above

What Does It Print?


(a) HaHa (b) Ha (c) None of the above: It prints Ha169

'H' + 'a' evaluated as int, then converted to String. Ouch.

The Moral

Use string concatenation (+) with care


At least one operand must be a String If it isn't, cast or convert" + 'H' + 'a');

Be glad operator overloading isn't supported

6. The Case of the Constructor


01 public class Confusing { 02 public Confusing(Object o) { 03 System.out.println("Object"); 04 } 05 public Confusing(double[] dArray) { 06 System.out.println("double array"); 07 } 08 public static void main(String args[]) { 09 new Confusing(null); 10 } 11 }

What Does It Print?


(a) Object (b) double array (c) None of the above

What Does It Print?


(a) Object (b) double array (c) None of the above

When multiple overloadings apply, the most specific wins

Another Look
01 public class Confusing { 02 public Confusing(Object o) { 03 System.out.println("Object"); 04 } 05 public Confusing(double[] dArray) { 06 System.out.println("double array"); 07 } 08 public static void main(String args[]) { 09 new Confusing(null); 10 } 11 }

How Do You Fix It?


There may be no problem If there is, use a cast:
New Confusing((Object)null);

The Moral
Avoid overloading If you overload, avoid ambiguity If you do have ambiguous overloadings, make their behavior identical If you are using a "broken" class, make intentions clear with a cast

7. A Big Delight in Every Byte


01 public class ByteMe { 02 public static void main(String[] args) { 03 for (byte b = Byte.MIN_VALUE; 04 b < Byte.MAX_VALUE; b++) { 05 if (b == 0x90) 06 System.out.print("Byte me! "); 07 } 08 } 09 }

What Does It Print?


(a) (nothing) (b) Byte me! (c) Byte me! Byte me!

What Does It Print?


(a) (nothing) (b) Byte me! (c) Byte me! Byte me!

Program compares a byte with an int

byte is promoted with surprising results

Another Look
01 public class ByteMe { 02 public static void main(String[] args) { 03 for (byte b = Byte.MIN_VALUE; 04 b < Byte.MAX_VALUE; b++) { 05 if (b == 0x90) // (b == 144) 06 System.out.print("Byte me! "); 07 } 08 } 09 } 10 11 // But (byte)0x90 == -112

How Do You Fix It?


Cast int to byte
if (b == (byte)0x90) System.out.println("Byte me!");

Or convert byte to int, suppressing sign extension with mask


if ((b & 0xff) == 0x90) System.out.println("Byte me!");

The Moral
Bytes aren't ints Be careful when mixing primitive types Compare like-typed expressions

Cast or convert one operand as necessary

8. Time for a Change


If you pay $2.00 for a gasket that costs $1.10, how much change do you get?
01 public class Change { 02 public static void main(String args[]) 03 { 04 System.out.println(2.00 - 1.10); 05 } 06 }

What Does It Print?


(a) 0.9 (b) 0.90 (c) It varies (d) None of the above

What Does It Print?


(a) 0.9 (b) 0.90 (c) It varies (d) None of the above: 0.89999999999999

Decimal Values can't be represented exactly by float or double

How Do You Fix It?


01 02 03 04 05 06 07 08 09 10 11 12 13 14 import java.math.BigDecimal; public class Change2 { public static void main(String args[]) { System.out.println( new BigDecimal("2.00").subtract( new BigDecimal("1.10"))); } } public class Change { public static void main(String args[]) { System.out.println(200 - 110); } }

The Moral
Avoid float and double where exact answers are required Use BigDecimal, int, or long instead

9. A Private Matter
01 02 03 04 05 06 07 08 09 10 11 12 13 class Base { public String name = "Base"; } class Derived extends Base { private String name = "Derived"; } public class PrivateMatter { public static void main(String[] args) { System.out.println(new Derived().name); } }

What Does It Print?


(a) Derived (b) Base (c) Compiler error in class Derived: Can't assign weaker access to name (d) None of the above

What Does it Print?


(a) Derived (b) Base (c) Compiler error in class Derived: Can't assign weaker access to name (d) None of the above: Compiler error in class PrivateMatter: Can't access name Private method can't overrides public, but private field can hide public

Another Look
01 02 03 04 05 06 07 08 09 10 11 12 13 class Base { public String name = "Base"; } class Derived extends Base { private String name = "Derived"; } public class PrivateMatter { public static void main(String[] args) { System.out.println(new Derived().name); } }

How Do You Fix It?


01 02 03 04 05 06 07 08 09 10 11 12 13 class Base { public String getName() { return "Base"; } } class Derived extends Base { public String getName() { return "Derived"; } } public class PrivateMatter { public static void main(String[] args) { System.out.println(new Derived().getName()); } }

The Moral
Avoid hiding

Violates subsumption Use accessor methods instead

Avoid public fields

10. Loopy Behavior


01 public class Loopy { 02 public static void main(String[] args) { 03 final int start = Integer.MAX_VALUE 04 100; 05 final int end = Integer.MAX_VALUE; 06 int count = 0; 07 for (int i = start; i <= end; i++) 08 count++; 09 System.out.println(count); 10 } 11 }

What Does It Print?


(a) 100 (b) 101 (c) (nothing)

What Does It Print?


(a) 100 (b) 101 (c) (nothing) The loop test is broken - infinite loop!

Another Look
01 public class Loopy { 02 public static void main(String[] args) { 03 final int start = Integer.MAX_VALUE 04 100; 05 final int end = Integer.MAX_VALUE; 06 int count = 0; 07 for (int i = start; i <= end; i++) 08 count++; 09 System.out.println(count); 10 } 11 }

How Do You Fix It?


Change loop variable from int to long
for (long i = start; i <= end; i++) count++;

The Moral
ints aren't integers! Think about overflow Use necessary

larger type if

11. Random Behavior


01 public class RandomSet { 02 public static void main(String[] args) { 03 Set s = new HashSet(); 04 for (int i = 0; i < 100; i++) 05 s.add(randomInteger()); 06 System.out.println(s.size()); 07 } 08 09 private static Integer randomInteger() { 10 return new Integer(new Random().nextInt()); 11 } 12 }

What Does It Print?


(a) A number close to 1 (b) A number close to 50 (c) A number close to 100 (d) None of the above

What Does It Print?


(a) A number close to 1 (b) A number close to 50 (c) A number close to 100 (d) None of the above A new random number generator is created each iteration and the seed changes rarely if at all.

Another Look
01 public class RandomSet { 02 public static void main(String[] args) { 03 Set s = new HashSet(); 04 for (int i=0; i<100; i++) 05 s.add(randomInteger()); 06 System.out.println(s.size()); 07 } 08 09 private static Integer randomInteger() { 10 return new Integer(new Random().nextInt()); 11 } 12 }

How Do You Fix It?


01 public class RandomSet { 02 public static void main(String[] args) { 03 Set s = new HashSet(); 04 for (int i=0; i<100; i++) 05 s.add(randomInteger()); 06 System.out.println(s.size()); 07 } 08 09 private static Random rnd = new Random(); 10 11 private static Integer randomInteger() { 12 return new Integer(rnd.nextInt()); 13 } 14 }

The Moral
Use one Random instance for each sequence In most programs, one is all you need In multithreaded programs, you may want multiple instances for increased concurrency
Seed explicitly or risk identical sequences Generally ok to use one instance to seed others

12.Making a Hash of It
01 public class Name { 02 private String first, last; 03 public Name(String first, String last) { 04 if (first == null || last == null) 05 throw new NullPointerException(); 06 this.first = first; this.last = last; 07 } 08 public boolean equals(Name o) { 09 return first.equals(o.first) && last.equals(o.last); 10 } 11 public int hashCode() { 12 return 31 * first.hashCode() + last.hashCode(); 13 } 14 public static void main(String[] args) { 15 Set s = new HashSet(); 16 s.add(new Name("Mickey", "Mouse")); 17 System.out.println( 18 s.contains(new Name("Mickey", "Mouse"))); 19 } 20 }

What Does It Print?


(a) true (b) false (c) It varies

What Does It Print?


(a) true (b) false (c) It varies Name overrides hashCode but not equals. The two Name instances are unequal.

Another Look
01 public class Name { 02 private String first, last; 03 public Name(String first, String last) { 04 if (first == null || last == null) 05 throw new NullPointerException(); 06 this.first = first; this.last = last; 07 } 08 public boolean equals(Name o) { // Accidental overloading 09 return first.equals(o.first) && last.equals(o.last); 10 } 11 public int hashCode() { // Overriding 12 return 31 * first.hashCode() + last.hashCode(); 13 } 14 public static void main(String[] args) { 15 Set s = new HashSet(); 16 s.add(new Name("Mickey", "Mouse")); 17 System.out.println( 18 s.contains(new Name("Mickey", "Mouse"))); 19 } 20 }

How Do You Fix It?

Replace the overloaded equals method with an overriding equals method


01 public boolean equals(Object o) { 02 if (!(o instanceof Name)) 03 return false; 04 Name n = (Name)o; 05 return n.first.equals(first) && n.last.equals(last); 06 }

The Moral
If you want to override a method:
Make sure signatures match The compiler doesnt check for you Do copy-and-paste declarations!

13. Ping Pong


01 class PingPong { 02 public static synchronized void main(String[] a) { 03 Thread t = new Thread() { 04 public void run() { 05 pong(); 06 } 07 }; 08 09 t.run(); 10 System.out.print("Ping"); 11 } 12 13 static synchronized void pong() { 14 System.out.print("Pong"); 15 } 16 }

What Does It Print?


(a) PingPong (b) PongPing (c) It varies

What Does It Print?


(a) PingPong (b) PongPing (c) It varies Not a multithreaded program!

Another Look
01 class PingPong { 02 public static synchronized void main(String[] a) { 03 Thread t = new Thread() { 04 public void run() { 05 pong(); 06 } 07 }; 08 09 t.run(); // Common typo! 10 System.out.print("Ping"); 11 } 12 13 static synchronized void pong() { 14 System.out.print("Pong"); 15 } 16 }

How Do You Fix It?


01 class PingPong { 02 public static synchronized void main(String[] a) { 03 Thread t = new Thread() { 04 public void run() { 05 pong(); 06 } 07 }; 08 09 t.start(); 10 System.out.print("Ping"); 11 } 12 13 static synchronized void pong() { 14 System.out.print("Pong"); 15 } 16 }

The Moral
Invoke Thread.start, not Thread.run
Common error Can be very difficult to diagnose

(Thread shouldnt implement Runnable)

14. Shifty
01 public class Shifty { 02 public static void main(String[] args) { 03 int distance = 0; 04 while ((-1 << distance) != 0) 05 distance++; 06 System.out.println(distance); 07 } 08 }

What Does It Print?


(a) 31 (b) 32 (c) 33 (d) None of the above

What Does It Print?


(a) 31 (b) 32 (c) 33 (d) None of the above: infinite loop! Shift distances are calculated mod 32.

Another Look
01 public class Shifty { 02 public static void main(String[] args) { 03 int distance = 0; 04 while ((-1 << distance) != 0) 05 distance++; 06 System.out.println(distance); 07 } 08 } 09 10 // (-1 << 32) == -1

How Do You Fix It?


01 public class Shifty { 02 public static void main(String[] args) { 03 int distance = 0; 04 for (int val = -1; val != 0; val <<= 1) 05 distance++; 06 System.out.println(distance); 07 } 08 }

The Moral
Shift distances are computed mod 32 (or 64) Its impossible to shift out an entire int (or long) using any shift operator or distance Use care when shift distance is not a literal

15. Line Printer


01 public class LinePrinter { 02 public static void main(String[] args) { 03 // Note: \u000A is Unicode representation for newline 04 char c = 0x000A; 05 System.out.println(c); 06 } 07 }

What Does It Print?


(a) Two blank lines (b) 10 (c) Wont compile (d) It varies

What Does It Print?


(a) Two blank lines (b) 10 (c) Wont compile: Syntax error! (d) It varies The Unicode escape in the comment breaks it in two. The second half is garbage.

Another Look 01 // Unicode escapes are processed before comments!


02 public class LinePrinter { 03 public static void main(String[] args) { 04 // Note: \u000A is unicode representation for newline 05 char c = 0x000A; 06 System.out.println(c); 07 } 08 } 01 02 03 04 05 06 07 08 09 // 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); } }

How Do You Fix It?


01 public class LinePrinter { 02 public static void main(String[] args) { 03 // Escape sequences (like \n) are fine in comments 04 char c = '\n'; 05 System.out.println(c); 06 } 07 }

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

16. All Strung Out


01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 public class Puzzling { public static void main(String[] args) { String s = new String("blah"); System.out.println(s); } } class String { java.lang.String s; public String(java.lang.String s) { this.s = s; } public java.lang.String toString() { return s; } }

What Does It Print?


(a) Wont compile (b) blah (c) Throws an exception at runtime (d) Other

What Does It Print?


(a) Wont compile (b) blah (c) Throws an exception at runtime (d) Other NoSuchMethodError is thrown because the Puzzling class is missing a main method.

Another Look
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 public class Puzzling { public static void main(String[] args) { String s = new String("blah"); System.out.println(s); } } class String { java.lang.String s; public String(java.lang.String s) { this.s = s; } public java.lang.String toString() { return s; } }

How Do You Fix It?


01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 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; } }

The Moral
Avoid name reuse in all its guises
hiding, shadowing, overloading

Dont even think about reusing platform class names!

17. Reflection Infection


01 import java.lang.reflect.*; 02 03 public class Reflector { 04 public static void main(String[] args) throws Exception { 05 Set s = new HashSet(); 06 s.add("foo"); 07 Iterator i = s.iterator(); 08 Method m = 09 i.getClass().getMethod("hasNext", new Class[0]); 10 System.out.println(m.invoke(i, new Object[0])); 11 } 12 }

What Does It Print?


(a) Wont compile (b) true (c) Throws exception (d) None of the above

What Does It Print?


(a) Wont compile (b) true (c) Throws exception IllegalAccessError (d) None of the above Attempts to invoke a method on a private class

Another Look
01 import java.lang.reflect.*; 02 03 public class Reflector { 04 public static void main(String[] args) throws Exception { 05 Set s = new HashSet(); 06 s.add("foo"); 07 Iterator i = s.iterator(); 08 Method m = 09 i.getClass().getMethod("hasNext", new Class[0]); 10 System.out.println(m.invoke(i, new Object[0])); 11 } 12 }

How Do You Fix It?


01 import java.lang.reflect.*; 02 03 public class Reflector { 04 public static void main(String[] args) throws Exception { 05 Set s = new HashSet(); 06 s.add("foo"); 07 Iterator i = s.iterator(); 08 Method m = 09 Iterator.class.getMethod("hasNext", 10 new Class[0]); 11 System.out.println(m.invoke(i, new Object[0])); 12 } 13 }

The Moral
Reflection has its own access rules Avoid reflection when possible If you must use reflection
Instantiate using reflection Cast to an interface type Access via interface

Avoid extralinguistic mechanisms

18. String Cheese


01 public class StringCheese { 02 public static void main(String args[]) { 03 byte b[] = new byte[256]; 04 for(int i = 0; i < 256; i++) 05 b[i] = (byte)i; 06 String str = new String(b); 07 for(int i = 0; i < str.length(); i++) 08 System.out.print((int)str.charAt(i) + " "); 09 } 10 }

What Does It Print?


(a) The numbers from 0 to 255 (b) The numbers from 0 to 127 then -128 to -1 (c) It varies (d) None of the above

What Does It Print?


(a) The numbers from 0 to 255 (b) The numbers from 0 to 127 then -128 to -1 (c) It varies* (d) None of the above The sequence depends on the default charset,which depends on OS and locale.
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 8364 65533 8218 402 8222 8230 8224 8225 710 8240 352 8249 338 65533 381 65533 65533 8216 8217 8220 8221 8226 8211 8212 732 8482 353 8250 339 65533 382 376 160 161 162 163 164 165 166 167 168 169 170 171 172

Another Look
01 public class StringCheese { 02 public static void main(String args[]) { 03 byte b[] = new byte[256]; 04 for(int i = 0; i < 256; i++) 05 b[i] = (byte)i; 06 String str = new String(b); 07 for(int i = 0; i < str.length(); i++) 08 System.out.print((int)str.charAt(i) + " "); 09 } 10 }

String(byte[] bytes) Constructs a new String by decoding the specified array of bytes using the platforms default charset. [from API Spec.]

How Do You Fix It?


If you want it to print numbers from 0-255 in order:
01 public class StringCheese { 02 public static void main(String args[]) { 03 byte b[] = new byte[256]; 04 for(int i = 0; i < 256; i++) 05 b[i] = (byte)i; 06 String str = new String(b, "ISO-8859-1"); 07 for(int i = 0; i < str.length(); i++) 08 System.out.print((int)str.charAt(i) + " "); 09 } 10 }

ISO-8859-1 indicates the Latin1 charset.

The Moral
Converting bytes to chars uses a charset If you dont specify one, you get default
Depends on OS and locale

If you need predictability, specify a charset

19. Elvis Lives!


01 public class Elvis { 02 public static final Elvis INSTANCE = new Elvis(); 03 private final int beltSize; 04 05 private static final int CURRENT_YEAR = 06 Calendar.getInstance().get(Calendar.YEAR); 07 08 private Elvis() { beltSize = CURRENT_YEAR - 1930; } 09 public int beltSize() { return beltSize; } 10 11 public static void main(String[] args) { 12 System.out.println("Elvis wears size " + 13 INSTANCE.beltSize() + " belt."); 14 } 15 }

What Does It Print?


(a) Elvis wears size 0 belt. (b) Elvis wears size 73 belt. (c) Elvis wears size -1930 belt. (d) None of the above.

What Does It Print?


(a) Elvis wears size 0 belt. (b) Elvis wears size 73 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.

Another Look
01 // Static initialization proceeds top to bottom.
02 public class Elvis {

03

04 05 06 07 08 09 10 11 12 13 14 15 16 17 }

public static final Elvis INSTANCE = new Elvis(); private final int beltSize; private static final int CURRENT_YEAR = Calendar.getInstance().get(Calendar.YEAR);

// Recursive initialization returns immediately!

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."); }

How Do You Fix It?


01 public class Elvis { 02 private final int beltSize; 03 04 private static final int CURRENT_YEAR = 05 Calendar.getInstance().get(Calendar.YEAR); 06 07 // Make instance after other initialization complete 08 public static final Elvis INSTANCE = new Elvis(); 09 10 private Elvis() { beltSize = CURRENT_YEAR - 1930; } 11 public int beltSize() { return beltSize; } 12 13 public static void main(String[] args) { 14 System.out.println("Elvis wears size " + 15 INSTANCE.beltSize() + " belt."); 16 } 17 }

The Moral
Watch out for circularities in static initialization
One or more classes may be involved Circularities arent necessarily wrong but
" Constructors can run before class fully initialized " Static fields can be read before theyre initialized

Several common patterns are susceptible


Singleton (Effective Java, Item 2) Typesafe Enum (Effective Java, Item 21) Service Provider Framework (Effective Java, Item 1)

20. Whats the Point?


01 class Point { 02 protected final int x, y; 03 private final String name; // Cached at construction time 04 protected String makeName() { return "[" + x + "," + y + "]"; } 05 public final String toString() { return name; } 06 Point(int x, int y) { 07 this.x = x; this.y = y; 08 this.name = makeName(); 09 } 10 } 11 12 public class ColorPoint extends Point { 13 private final String color; 14 protected String makeName() { return super.makeName() + ":" + color; } 15 ColorPoint(int x, int y, String color) { 16 super(x, y); 17 this.color = color; 18 } 19 public static void main(String[] args) { 20 System.out.println(new ColorPoint(4, 2, "purple")); 21 } 22 }

What Does It Print?


(a) [4,2]:purple (b) [4,2]:null (c) Throws exception at runtime (d) None of the above

What Does It Print?


(a) [4,2]:purple (b) [4,2]:null (c) Throws exception at runtime
(d) None of the above

Superclass constructor runs a subclass method before the subclass instance is initialized.

01 class Point { 02 protected final int x, y; 03 private final String name; 04 protected String makeName() { return "[" + x + "," + y + "]"; } 05 public final String toString() { return name; } 06 Point(int x, int y) { 07 this.x = x; this.y = y; 08 this.name = makeName(); // (3) Invokes subclass method 09 } 10 } 11 12 public class ColorPoint extends Point { 13 private final String color; 14 // (4) Subclass method executes before subclass constructor body! 15 protected String makeName() { return super.makeName() + ":" + color; } 16 ColorPoint(int x, int y, String color) { 17 super(x, y); // (2) Chains to superclass constructor 18 this.color = color; // (5) Initializes blank final instance field 19 } 20 public static void main(String[] args) { // (1) Invoke subclass cons. 21 System.out.println(new ColorPoint(4, 2, "purple")); 22 } 23 }

Another Look

01 class Point { 02 protected final int x, y; 03 private String name; // Lazily initialized (cached on first use) 04 protected String makeName() { return "[" + x + "," + y + "]"; } 05 public final synchronized String toString() 06 { return (name == null ? (name = makeName()) : name); } 07 Point(int x, int y) { 08 this.x = x; this.y = y; 09 // (name initialization removed) 10 } 11 } 12 13 public class ColorPoint extends Point { 14 private final String color; 15 protected String makeName() { return super.makeName() + ":" + color; } 16 ColorPoint(int x, int y, String color) { 17 super(x, y); 18 this.color = color; 19 } 20 public static void main(String[] args) { 21 System.out.println(new ColorPoint(4, 2, "purple")); 22 } 23 }

How Do You Fix It?

The Moral
Never call overridable methods from constructors, directly or indirectly Also applies to pseudo-constructors
readObject() clone()

See Effective Java, Item 15

21. Long Division


01 public class LongDivision { 02 private static final long MILLIS_PER_DAY 03 = 24 * 60 * 60 * 1000; 04 private static final long MICROS_PER_DAY 05 = 24 * 60 * 60 * 1000 * 1000; 06 07 public static void main(String[] args) { 08 System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY); 09 } 10 }

What Does It Print?


(a) 5 (b) 1000 (c) 5000 (d) Throws an exception

What Does It Print?


(a) 5 (b) 1000 (c) 5000 (d) Throws an exception

Computation does overflow

Another Look
01 public class LongDivision { 02 private static final long MILLIS_PER_DAY 03 = 24 * 60 * 60 * 1000; 04 private static final long MICROS_PER_DAY 05 = 24 * 60 * 60 * 1000 * 1000; // >> Integer.MAX_VALUE 06 07 public static void main(String[] args) { 08 System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY); 09 } 10 }

How Do You Fix It?


01 public class LongDivision { 02 private static final long MILLIS_PER_DAY 03 = 24L * 60 * 60 * 1000; 04 private static final long MICROS_PER_DAY 05 = 24L * 60 * 60 * 1000 * 1000; 06 07 public static void main(String[] args) { 08 System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY); 09 } 10 }

The Moral
When working with large numbers watch
out for overflowits a silent killer

Just because variable is big enough to hold


result doesnt mean computation is of correct type

When in doubt, use long

22. No Pain, No Gain


01 public class Rhymes { 02 private static Random rnd = new Random(); 03 public static void main(String[] args) { 04 StringBuffer word = null; 05 switch(rnd.nextInt(2)) { 06 case 1: word = new StringBuffer('P'); 07 case 2: word = new StringBuffer('G'); 08 default: word = new StringBuffer('M'); 09 } 10 word.append('a'); 11 word.append('i'); 12 word.append('n'); 13 System.out.println(word); 14 } 15 }
Thanks to madbot (also known as Mike McCloskey)

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

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.

Another Look
01 public class Rhymes { 02 private static Random rnd = new Random(); 03 public static void main(String[] args) { 04 StringBuffer word = null; 05 switch(rnd.nextInt(2)) { // No breaks! 06 case 1: word = new StringBuffer('P'); 07 case 2: word = new StringBuffer('G'); 08 default: word = new StringBuffer('M'); 09 } 10 word.append('a'); 11 word.append('i'); 12 word.append('n'); 13 System.out.println(word); 14 } 15 }

01 public class Rhymes { 02 private static Random rnd = new Random(); 03 public static void main(String[] args) { 04 StringBuffer word = null; 05 switch(rnd.nextInt(3)) { 06 case 1: word = new StringBuffer("P"); break; 07 case 2: word = new StringBuffer("G"); break; 08 default: word = new StringBuffer("M"); break; 09 } 10 word.append('a'); 11 word.append('i'); 12 word.append('n'); 13 System.out.println(word); 14 } 15 }

How Do You Fix It?

The Moral
Use common idioms
If you must stray, consult the documentation

Chars are not strings; theyre more like ints Always remember breaks in switch
statement

Watch out for fence-post errors Watch out for sneaky puzzlers

23. The Name Game


01 public class NameGame { 02 public static void main(String args[]) { 03 Map m = new IdentityHashMap(); 04 m.put("Mickey", "Mouse"); 05 m.put("Mickey", "Mantle"); 06 System.out.println(m.size()); 07 } 08 }

What Does It Print?


(a) 0 (b) 1 (c) 2 (d) It varies

What Does It Print?


(a) 0 (b) 1 (c) 2 (d) It varies Were using an IdentityHashMap, but string literals are interned (they cancel each other out)

Another Look
01 public class NameGame { 02 public static void main(String args[]) { 03 Map m = new IdentityHashMap(); 04 m.put("Mickey", "Mouse"); 05 m.put("Mickey", "Mantle"); 06 System.out.println(m.size()); 07 } 08 }

How Do You Fix It?


01 public class NameGame { 02 public static void main(String args[]) { 03 Map m = new HashMap(); 04 m.put("Mickey", "Mouse"); 05 m.put("Mickey", "Mantle"); 06 System.out.println(m.size()); 07 } 08 }

The Moral
IdentityHashMap not a general-purpose
Map Dont use it unless you know its what you want Uses identity in place of equality Useful for topology-preserving transformations

(String literals are interned)

24. More of The Same


01 public class Names { 02 private Map m = new HashMap(); 03 public void Names() { 04 m.put("Mickey", "Mouse"); 05 m.put("Mickey", "Mantle"); 06 } 07 08 public int size() { return m.size(); } 09 10 public static void main(String args[]) { 11 Names names = new Names(); 12 System.out.println(names.size()); 13 } 14 }

What Does It Print?


(a) 0 (b) 1 (c) 2 (d) It varies

What Does It Print?


(a) 0 (b) 1 (c) 2 (d) It varies

No programmer-defined constructor

Another Look
01 public class Names { 02 private Map m = new HashMap(); 03 public void Names() { // Not a constructor! 04 m.put("Mickey", "Mouse"); 05 m.put("Mickey", "Mantle"); 06 } 07 08 public int size() { return m.size(); } 09 10 public static void main(String args[]) { 11 Names names = new Names(); // Invokes default! 12 System.out.println(names.size()); 13 } 14 }

How Do You Fix It?


01 public class Names { 02 private Map m = new HashMap(); 03 public Names() { // No return type 04 m.put("Mickey", "Mouse"); 05 m.put("Mickey", "Mantle"); 06 } 07 08 public int size() { return m.size(); } 09 10 public static void main(String args[]) { 11 Names names = new Names(); 12 System.out.println(names.size()); 13 } 14 }

The Moral
It is possible for a method to have
the same name as a constructor

Dont ever do it
Obey naming conventions
field, method(), Class, CONSTANT

25. Shades of Gray


01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 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

What Does It Print?


(a) Black (b) White (c) Wont compile (d) None of the above

What Does It Print?


(a) Black (b) White (c) Wont compile (d) None of the above Field Y obscures member class Y (JLS 6.3.2) The rule: variable > type > package

Another Look
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 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

How Do You Fix It?


01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 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"; }

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

26. Its Elementary


01 public class Elementary { 02 public static void main(String[] args) { 03 System.out.println(54321 + 5432l); 04 } 05 }

What Does It Print?


(a) (b) (c) (d) -22430 59753 10864 108642

What Does It Print?


(a) (b) (c) (d) -22430 59753 10864 108642

Program doesnt say what you think it does!

Another Look
01 public class Elementary { 02 public static void main(String[] args) { 03 04 05 } } System.out.println(5432

+ 5432

l);

1 l

- the numeral one - the lowercase letter el

How Do You Fix It?

We wont insult your intelligence

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();

27. Down For The Count


01 public class Count { 02 public static void main(String[] args) { 03 final int START = 2000000000; 04 int count = 0; 05 for (float f = START; f < START + 50; f++) 06 count++; 07 System.out.println(count); 08 } 09 }

What Does It Print?


(a) 0 (b) 50 (c) 51 (d) None of the above

What Does It Print?


(a) 0 (b) 50 (c) 51 (d) None of the above

The termination test misbehaves due to floating point granularity.

Another Look
01 public class Count { 02 public static void main(String[] args) { 03 final int START = 2000000000; 04 int count = 0; 05 for (float f = START; f < START + 50; f++) 06 count++; 07 System.out.println(count); 08 } 09 }

// (float) START == (float) (START + 50)

How Do You Fix It?


01 public class Count { 02 public static void main(String[] args) { 03 final int START = 2000000000; 04 int count = 0; 05 for (int f = START; f < START + 50; f++) 06 count++; 07 System.out.println(count); 08 } 09 }

The Moral
Dont 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 youre certain that float provides enough precision and you have a compelling performance need (space or

28. Classy Fire


01 public class Classifier { 02 public static void main(String[] args) { 03 System.out.println( 04 classify('n') + classify('+') + classify('2')); 05 } 06 static String classify(char ch) { 07 if ("0123456789".indexOf(ch) >= 0) 08 return "NUMERAL "; 09 if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0) 10 return "LETTER "; 11 /* (Operators not supported yet) 12 * if ("+-*/&|!=".indexOf(ch) >= 0) 13 * return "OPERATOR "; 14 */ 15 return "UNKNOWN "; 16 } 17 }

What Does It Print?


(a) LETTER OPERATOR NUMERAL (b) LETTER UNKNOWN NUMERAL (c) Throws an exception (d) None of the above

What Does It Print?


(a) LETTER OPERATOR NUMERAL (b) LETTER UNKNOWN NUMERAL (c) Throws an exception (d) None of the above

As for the intuition, youll see in a moment...

Another Look
01 public class Classifier { 02 public static void main(String[] args) { 03 System.out.println( 04 classify('n') + classify('+') + classify('2')); 05 } 06 static String classify(char ch) { 07 if ("0123456789".indexOf(ch) >= 0) 08 return "NUMERAL "; 09 if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0) 10 return "LETTER "; 11 /* (Operators not supported yet) 12 * if ("+-*/&|!=".indexOf(ch) >= 0) 13 * return "OPERATOR "; 14 */ 15 return "UNKNOWN "; 16 } 17 }

How Do You Fix It?


01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 public class Classifier { public static void main(String[] args) { System.out.println( classify('n') + classify('+') + classify('2')); } static String classify(char ch) { if ("0123456789".indexOf(ch) >= 0) return "NUMERAL "; if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0) return "LETTER "; if (false) { // (Operators not supported yet) if ("+-*/&|!=".indexOf(ch) >= 0) return "OPERATOR "; } return "UNKNOWN "; } }

The Moral
You cannot reliably block-comment
out code
Comments do not nest

Use if (false) idiom or //


comments

29. The Joy of Hex


01 public class JoyOfHex { 02 public static void main(String[] args) { 03 System.out.println( 04 Long.toHexString(0x100000000L + 0xcafebabe)); 05 } 06 }

What Does It Print?


(a) cafebabe (b) 1cafebabe (c) ffffffffcafebabe (d) Throws an exception

What Does It Print?


(a) cafebabe (b) 1cafebabe (c) ffffffffcafebabe (d) Throws an exception

0xcafebabe is a negative number

Another Look
01 public class JoyOfHex { 02 public static void main(String[] args) { 03 System.out.println( 04 Long.toHexString(0x100000000L + 0xcafebabe)); 05 } 06 }

1 1 1 1 1 1 1

0xffffffffcafebabeL + 0x0000000100000000L 0x00000000cafebabeL

How Do You Fix It?


01 public class JoyOfHex { 02 public static void main(String[] args) { 03 System.out.println( 04 Long.toHexString(0x100000000L + 0xcafebabeL)); 05 } 06 }

The Moral
Decimal literals are all positive; not so for hex
> Negative decimal constants have minus

sign > Hex literals are negative if the high-order bit is set

Widening conversion can cause sign extension Mixed-mode arithmetic is tricky avoid it

30. Animal Farm


01 public class AnimalFarm { 02 public static void main(String[] args) { 03 final String pig = "length: 10"; 04 final String dog = "length: "+pig.length(); 05 System.out.println("Animals are equal: " 06 + pig == dog); 07 } 08 }

What Does It Print?


(a) Animals are equal: true (b) Animals are equal: false (c) It varies (d) None of the above

What Does It Print?


(a) Animals are equal: true (b) Animals are equal: false (c) It varies (d) None of the above: false

The + operator binds tighter than ==

Another Look
01 public class AnimalFarm { 02 public static void main(String[] args) { 03 final String pig = "length: 10"; 04 final String dog = "length: "+pig.length(); 05 System.out.println("Animals are equal: " 06 + pig == dog); 07 } 08 }

System.out.println( ("Animals are equal: " + pig) == dog);

How Do You Fix It?


01 public class AnimalFarm { 02 public static void main(String[] args) { 03 final String pig = "length: 10"; 04 final String dog = "length: "+pig.length(); 05 System.out.println("Animals are equal: " 06 + (pig == dog)); 07 } 08 }

The Moral
Parenthesize when using string concatenation
Spacing can be deceptive; parentheses never lie

Dont depend on interning of string constants Use equals, not ==, for strings

31. A Tricky Assignment


01 public class Assignment { 02 public static void main(String[] a) throws Exception { 03 int tricky = 0; 04 for (int i = 0; i < 3; i++) 05 tricky += tricky++; 06 System.out.println(tricky); 07 } 08 }

What Does It Print?


(a) 0 (b) 3 (c) 14 (d) None of the above

What Does It Print?


(a) 0 (b) 3 (c) 14 (d) None of the above

Operands are evaluated left to right. Postfix increment returns old value.

Another Look
01 public class Assignment { 02 public static void main(String[] a) throws Exception { 03 int tricky = 0; 04 for (int i = 0; i < 3; i++) 05 tricky += tricky++; 06 System.out.println(tricky); 07 } 08 }

Another Look
01 public class Assignment { 02 public static void main(String[] a) throws Exception { 03 int tricky = 0; 04 for (int i = 0; i < 3; i++) 05 tricky += tricky++; 06 System.out.println(tricky); 07 } 08 }

Another Look

05

tricky += tricky++; (0)

(tricky == 0)

Another Look

05

tricky += tricky++; (0)

(tricky == 0)

Another Look

05

tricky += tricky++; 0 0

(tricky == 1)

Another Look

05

tricky += tricky++; 0 0

(tricky == 1)

Another Look

05

tricky += tricky++; 0 0

(tricky == 0)

Another Look
01 public class Assignment { 02 public static void main(String[] a) throws Exception { 03 int tricky = 0; 04 for (int i = 0; i < 3; i++) 05 tricky += tricky++; 06 System.out.println(tricky); 07 } 08 }

How Do You Fix It?


01 public class Assignment { 02 public static void main(String[] a) throws Exception { 03 int tricky = 0; 04 for (int i = 0; i < 3; i++) { 05 tricky++; 06 tricky += tricky; // or tricky *= 2; 07 } 08 System.out.println(tricky); 09 } 10 }

The Moral
Dont depend on details of expression evaluation Dont assign to a variable twice in one expression Postfix increment returns old value (Operands are evaluated left to right)

32. Thrown for a Loop


01 public class Loop { 02 public static void main(String[] args) { 03 int[][] tests = { { 6, 5, 4, 3, 2, 1 }, { 1, 2 }, 04 { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } }; 05 int successCount = 0; 06 try { 07 int i = 0; 08 while (true) { 09 if (thirdElementIsThree(tests[i++])) 10 successCount++; 11 } 12 } catch (ArrayIndexOutOfBoundsException e) { } 13 System.out.println(successCount); 14 } 15 private static boolean thirdElementIsThree(int[] a) { 16 return a.length >= 3 & a[2] == 3; 17 } 18 }

What Does It Print?


(a) 0 (b) 1 (c) 2 (d) None of the above

What Does It Print?


(a) 0 (b) 1 (c) 2 (d) None of the above

Not only is the program repulsive, but it has a bug

Another Look
01 public class Loop { 02 public static void main(String[] args) { 03 int[][] tests = { { 6, 5, 4, 3, 2, 1 }, { 1, 2 }, 04 { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } }; 05 int successCount = 0; 06 try { 07 int i = 0; 08 while (true) { 09 if (thirdElementIsThree(tests[i++])) 10 successCount++; 11 } 12 } catch (ArrayIndexOutOfBoundsException e) { } 13 System.out.println(successCount); 14 } 15 private static boolean thirdElementIsThree(int[] a) { 16 return a.length >= 3 & a[2] == 3; 17 } 18 }

How Do You Fix It?


01 public class Loop { 02 public static void main(String[] args) { 03 int[][] tests = { { 6, 5, 4, 3, 2, 1 }, { 1, 2 }, 04 { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } }; 05 int successCount = 0; 06 for (int[] test : tests) 07 if (thirdElementIsThree(test)) 08 successCount++; 09 System.out.println(successCount); 10 } 11 12 private static boolean thirdElementIsThree(int[] a) { 13 return a.length >= 3 && a[2] == 3; 14 } 15 }

The Moral
Use exceptions only for exceptional conditions
> Never use exceptions for normal

control flow

Beware the logical AND and OR operators


> Document all intentional uses of & and |

on boolean

33. Sum Fun


01 class Cache { 02 static { initIfNecessary(); } 03 private static int sum; 04 public static int getSum() { 05 initIfNecessary(); 06 return sum; 07 } 08 private static boolean initialized = false; 09 private static synchronized void initIfNecessary() { 10 if (!initialized) { 11 for (int i = 0; i < 100; i++) 12 sum += i; 13 initialized = true; 14 } 15 } 16 public static void main(String[] args) { 17 System.out.println(getSum()); 18 } 19 }

What Does It Print?


(a) 4950 (b) 5050 (c) 9900 (d) None of the above

What Does It Print?


(a) 4950 (b) 5050 (c) 9900 (d) None of the above

Lazy initialization + eager initialization = a mess

Another Look
01 class Cache { 02 static { initIfNecessary(); } 03 private static int sum; 04 public static int getSum() { 05 initIfNecessary(); 06 return sum; 07 } 08 private static boolean initialized = false; 09 private static synchronized void initIfNecessary() { 10 if (!initialized) { 11 for (int i = 0; i < 100; i++) 12 sum += i; 13 initialized = true; 14 } 15 } 16 public static void main(String[] args) { 17 System.out.println(getSum()); 18 } 19 }

Another Look
01 class Cache { 02 static { initIfNecessary(); } 03 private static int sum; 04 public static int getSum() { 05 initIfNecessary(); 06 return sum; 07 } 08 private static boolean initialized = false; // Ouch! 09 private static synchronized void initIfNecessary() { 10 if (!initialized) { 11 for (int i = 0; i < 100; i++) 12 sum += i; 13 initialized = true; 14 } 15 } 16 public static void main(String[] args) { 17 System.out.println(getSum()); 18 } 19 }

How Do You Fix It?


01 class Cache { 02 private static final int SUM = computeSum(); 03 04 private static int computeSum() { 05 int result = 0; 06 for (int i = 0; i < 100; i++) 07 result += i; 08 return result; 09 } 10 11 public static int getSum() { 12 return SUM; 13 } 14 15 public static void main(String[] args) { 16 System.out.println(getSum()); 17 } 18 }

The Moral
Use eager or lazy initialization, not both
> Prefer eager initialization to lazy

Think about class initialization Avoid complex class initialization sequences

34. The Mod Squad


01 public class Mod { 02 public static void main(String[] args) { 03 final int MODULUS = 3; 04 int[] histogram = new int[MODULUS]; 05 06 int i = Integer.MIN_VALUE; 07 // This loop iterates over all int values 08 do { 09 histogram[Math.abs(i) % MODULUS]++; 10 } while (i++ != Integer.MAX_VALUE); 11 12 for (int j = 0; j < MODULUS; j++) 13 System.out.print(histogram[j] + " "); 14 } 15 }

What Does It Print?


(a) 1431655765 1431655765 1431655765 (b) 1431655765 1431655766 1431655765 (c) Throws an exception (d) None of the above

Hint: 232 / 3 = 1,431,655,765

What Does It Print?


(a) 1431655765 1431655765 1431655765 (b) 1431655765 1431655766 1431655765 (c) Throws an exception: array out of bounds (d) None of the above

Math.abs doesnt always return a nonnegative

Another Look
01 public class Mod { 02 public static void main(String[] args) { 03 final int MODULUS = 3; 04 int[] histogram = new int[MODULUS]; 05 06 int i = Integer.MIN_VALUE; 07 // This loop iterates over all int values 08 do { 09 histogram[Math.abs(i) % MODULUS]++; 10 } while (i++ != Integer.MAX_VALUE); 11 12 for (int j = 0; j < MODULUS; j++) 13 System.out.println(histogram[j] + " "); 14 } 15 }

How Do You Fix It?


Replace:
histogram[Math.abs(i) % MODULUS]++;

With:
histogram[mod(i, MODULUS)]++; private static int mod(int i, int modulus) { int result = i % modulus; return result < 0 ? result + modulus : result; }

The Moral
Math.abs can return a negative value
Twos-complement integers are asymmetric int arithmetic overflows silently

i mod m Math.abs(i) % m

35. Package Deal


01 package click; 02 public class CodeTalk { 03 public void doIt() { printMessage(); } 04 void printMessage() { System.out.println("Click"); } 05 } ___________________________________________________________ 01 package hack; 02 import click.CodeTalk; 03 public class TypeIt { 04 private static class ClickIt extends CodeTalk { 05 void printMessage() { System.out.println("Hack"); } 06 } 07 public static void main(String[] args) { 08 new ClickIt().doIt(); 09 } 10 }

What Does It Print?


(a) Click (b) Hack (c) Wont compile (d) None of the above

What Does It Print?


(a) Click (b) Hack (c) Wont compile (d) None of the above

There is no overriding in this program

Another Look
01 package click; 02 public class CodeTalk { 03 public void doIt() { printMessage(); } 04 void printMessage() { System.out.println("Click"); } 05 } ___________________________________________________________ 01 package hack; 02 import click.CodeTalk; 03 public class TypeIt { 04 private static class ClickIt extends CodeTalk { 05 void printMessage() { System.out.println("Hack"); } 06 } 07 public static void main(String[] args) { 08 new ClickIt().doIt(); 09 } 10 }

How Do You Fix It?


If you want overriding
Make printMessage public or protected Use @Override to ensure that you got overriding

The Moral
Package-private methods cant be overridden by methods outside their package If you cant see it, you cant override it

36. Lazy Initialization


01 public class Lazy { 02 private static boolean initialized = false; 03 static { 04 Thread t = new Thread(new Runnable() { 05 public void run() { 06 initialized = true; 07 } 08 }); 09 t. start(); 10 try { 11 t.join(); 12 } catch (InterruptedException e) { 13 throw new AssertionError(e); 14 } 15 } 16 public static void main(String[] args) { 17 System.out.println(initialized); 18 } 19 }

What Does It Print?


(a) true (b) false (c) It varies (d) None of the above

What Does It Print?


(a) true (b) false (c) It varies (d) None of the above: it deadlocks

Intuition: You wouldnt believe us if we told you.

Another Look
01 public class Lazy { 02 private static boolean initialized = false; 03 static { 04 Thread t = new Thread(new Runnable() { 05 public void run() { 06 initialized = true; // Deadlocks here! 07 } 08 }); 09 t. start(); 10 try { 11 t.join(); 12 } catch (InterruptedException e) { 13 throw new AssertionError(e); 14 } 15 } 16 public static void main(String[] args) { 17 System.out.println(initialized); 18 } 19 }

How Do You Fix It?


Dont use background threads in class initialization > If it hurts when you go like that, dont go like that!

The Moral
Never use background threads in class initialization

Keep class initialization simple Dont code like my brother

37. Odd Behavior


01 public class OddBehavior { 02 public static void main(String[] args) { 03 List<Integer> list = Arrays.asList(-2, -1, 0, 1, 2); 04 05 boolean foundOdd = false; 06 for (Iterator<Integer> it=list.iterator();it.hasNext(); ) 07 foundOdd = foundOdd || isOdd(it.next()); 08 09 System.out.println(foundOdd); 10 } 11 12 private static boolean isOdd(int i) { 13 return (i & 1) != 0; 14 } 15 }

What Does It Print?


01 public class OddBehavior { 02 public static void main(String[] args) { 03 List<Integer> list = Arrays.asList(-2, -1, 0, 1, 2); 04 05 boolean foundOdd = false; 06 for (Iterator<Integer> it=list.iterator(); it.hasNext(); ) 07 foundOdd = foundOdd || isOdd(it.next()); 08 09 System.out.println(foundOdd); 10 } 11 12 private static boolean isOdd(int i) { 13 return (i & 1) != 0; (a) true 14 } 15 } (b) false

(c) Throws exception (d) None of the above

What Does It Print?


(a) true (b) false (c) Throws exception (d) None of the above: Nothing Infinite loop

Conditional OR operator (||) shortcircuits iterator

Another Look
public class OddBehavior { public static void main(String[] args) { List<Integer> list = Arrays.asList(-2, -1, 0, 1, 2); boolean foundOdd = false; for (Iterator<Integer> it = list.iterator(); it.hasNext(); foundOdd = foundOdd || isOdd(it.next()); } System.out.println(foundOdd);

private static boolean isOdd(int i) { return (i & 1) != 0; }

You Could Fix it Like This.


public class OddBehavior { public static void main(String[] args) { List<Integer> list = Arrays.asList(-2, -1, 0, 1, 2); boolean foundOdd = false; for (int i : list) foundOdd = foundOdd || isOdd(i); System.out.println(foundOdd); } private static boolean isOdd(int i) { return (i & 1) != 0; }

But This Is Even Better


public class OddBehavior { public static void main(String[] args) { List<Integer> list = Arrays.asList(-2, -1, 0, 1, 2); System.out.println(containsOdd(list)); } private static boolean containsOdd(List<Integer> list) { for (int i : list) if (isOdd(i)) return true; return false; } private static boolean isOdd(int i) { return (i & 1) != 0; }

The Moral
Use for-each wherever possible
> Nicer and safer than explicit iterator or

index usage

If you must use an iterator, make sure you call next() exactly once Conditional operators evaluate their right operand only if necessary to determine result
> This is almost always what you want

38. Set List


public class SetList { public static void main(String[] args) { Set<Integer> set = new LinkedHashSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); } System.out.println(set + " " + list);

What Does It Print?

(a) [-3, -2, -1] [-3, -2, -1] (b) [-3, -2, -1] [-2, 0, 2] (c) Throws exception (d) None of the above

public class SetList { public static void main(String[] args) { Set<Integer> set = new LinkedHashSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); } System.out.println(set + " " + list);

What Does It Print?


(a) [-3, -2, -1] [-3, -2, -1] (b) [-3, -2, -1] [-2, 0, 2] (c) Throws exception (d) None of the above

Autoboxing + overloading = confusion

Another Look
public class SetList { public static void main(String[] args) { Set<Integer> set = new LinkedHashSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove(i); // List.remove(int) } System.out.println(set + " " + list);

How Do You Fix It?


public class SetList { public static void main(String[] args) { Set<Integer> set = new LinkedHashSet<Integer>(); List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { set.remove(i); list.remove((Integer) i); } System.out.println(set + " " + list);

The Moral
Avoid ambiguous overloadings Harder to avoid in release 5.0
> Autoboxing, varargs, generics

Design new APIs with this in mind


> Old rules no longer suffice

Luckily, few existing APIs were compromised


> Beware List<Integer>

39. Powers of Ten


public enum PowerOfTen { ONE(1), TEN(10), HUNDRED(100) { @Override public String toString() { return Integer.toString(val); } }; private final int val; PowerOfTen(int val) { this.val = val; } @Override public String toString() { return name().toLowerCase(); } public static void main(String[] args) { System.out.println(ONE + " " + TEN + " " + HUNDRED); }

What Does It Print?

(a) ONE TEN HUNDRED (b) one ten hundred

public enum PowerOfTen { (c) one ten 100 ONE(1), TEN(10), HUNDRED(100) { (d) None of the above @Override public String toString() { return Integer.toString(val); } }; private final int val; PowerOfTen(int val) { this.val = val; } @Override public String toString() { return name().toLowerCase(); } public static void main(String[] args) { System.out.println(ONE + " " + TEN + " " + HUNDRED); }

What Does It Print?


(a) ONE TEN HUNDRED (b) one ten hundred (c) one ten 100 (d) None of the above: Wont compile
Non-static variable val cant be referenced from static context return Integer.toString(val); ^

Private members are never inherited

Another Look
public enum PowerOfTen { ONE(1), TEN(10), HUNDRED(100) { // Creates static anonymous class @Override public String toString() { return Integer.toString(val); } }; private final int val; PowerOfTen(int val) { this.val = val; } @Override public String toString() { return name().toLowerCase(); } public static void main(String[] args) { System.out.println(ONE + " " + TEN + " " + HUNDRED); }

How Do You Fix It?


public enum PowerOfTen { ONE(1), TEN(10), HUNDRED(100) { @Override public String toString() { return Integer.toString(super.val); } }; private final int val; PowerOfTen(int val) { this.val = val; } @Override public String toString() { return name().toLowerCase(); } public static void main(String[] args) { System.out.println(ONE + " " + TEN + " " + HUNDRED); }

The Moral
Nest-mates can use each others private members But private members are never inherited Constant-specific enum bodies define static anonymous classes Compiler diagnostics can be confusing

40. Testy Behavior


import java.lang.reflect.*; @interface Test { } public class Testy { @Test public static void test() { return; } @Test public static void test2() { new RuntimeException(); } public static void main(String[] args) throws Exception { for (Method m : Testy.class.getDeclaredMethods()) { if (m.isAnnotationPresent(Test.class)) { try { m.invoke(null); System.out.print("Pass "); } catch (Throwable ex) { System.out.print("Fail "); } } } } }

What Does It Print?


import java.lang.reflect.*;

(a) Pass Fail (b) Pass Pass (c) It varies

(d) None of the above @interface Test { } public class Testy { @Test public static void test() { return; } @Test public static void test2() { new RuntimeException(); } public static void main(String[] args) throws Exception { for (Method m : Testy.class.getDeclaredMethods()) { if (m.isAnnotationPresent(Test.class)) { try { m.invoke(null); System.out.print("Pass "); } catch (Throwable ex) { System.out.print("Fail "); } } } } }

What Does It Print?


(a) Pass Fail (b) Pass Pass (c) It varies (d) None of the above: In fact, nothing!

The program contains two bugs, both subtle

Another Look
import java.lang.reflect.*; @interface Test { } // By default, annotations are discarded at runtime public class Testy { @Test public static void test() { return; } @Test public static void test2() { new RuntimeException(); } // Oops ! public static void main(String[] args) throws Exception { for (Method m : Testy.class.getDeclaredMethods()) { if (m.isAnnotationPresent(Test.class)) { try { m.invoke(null); System.out.print("Pass"); } catch (Throwable ex) { System.out.print("Fail "); } } } } }

How Do You Fix It?


import java.lang.reflect.*; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @interface Test { } public class Testy { @Test public static void test() { return; } @Test public static void test2() { throw new RuntimeException(); } public static void main(String[] args) throws Exception { for (Method m : Testy.class.getDeclaredMethods()) { if (m.isAnnotationPresent(Test.class)) { try { m.invoke(null); System.out.print("Pass "); } catch (Throwable ex) { System.out.print("Fail "); } } } } }

The Moral
By default, annotations are discarded at runtime
> If you need annotations at runtime, use

@Retention(RetentionPolicy.RUNTIME )
> If you want them omitted from class file,

use @Retention(RetentionPolicy.SOURCE)

No guarantee on order of reflected entities

41. What the Bleep?


public class Bleep { String name = "Bleep"; void setName(String name) { this.name = name; } void backgroundSetName() throws InterruptedException { Thread t = new Thread() { @Override public void run() { setName("Blat"); } }; t.start(); t.join(); System.out.println(name); } public static void main(String[] args) throws InterruptedException { new Bleep().backgroundSetName(); } }

What Does It Print?

(a) Bleep

(b) Blat public class Bleep { String name = "Bleep"; (c) It varies void setName(String name) { this.name = name; (d) None of the above } void backgroundSetName() throws InterruptedException { Thread t = new Thread() { @Override public void run() { setName("Blat"); } }; t.start(); t.join(); System.out.println(name); } public static void main(String[] args) throws InterruptedException { new Bleep().backgroundSetName(); } }

What Does It Print?


(a) Bleep (b) Blat (c) It varies (d) None of the above

Bleep.setName isnt getting called

Another Look
public class Bleep { String name = "Bleep"; void setName(String name) { // Does this look familiar? this.name = name; } void backgroundSetName() throws InterruptedException { Thread t = new Thread() { // Invokes Thread.setName (shadowing) @Override public void run() { setName("Blat"); } }; t.start(); t.join(); System.out.println(name); } public static void main(String[] args) throws InterruptedException { new Bleep().backgroundSetName(); } }

How Do You Fix It?


public class Bleep { String name = "Bleep"; void setName(String name) { this.name = name; } void backgroundSetName() throws InterruptedException { Thread t = new Thread(new Runnable() { public void run() { setName("Blat"); } }); t.start(); t.join(); System.out.println(name); } public static void main(String[] args) throws InterruptedException { new Bleep().backgroundSetName(); } }

The Moral
Dont extend Thread
> Use new Thread(Runnable) instead

Often the Executor Framework is better still


> Much more flexible > See java.util.concurrent for more

information

Beware of shadowing

42. Beyond Compare


public class BeyondCompare { public static void main(String[] args) { Object o = new Integer(3); System.out.println(new Double(3).compareTo(o) == 0); } }

What Does It Print?


public class BeyondCompare { public static void main(String[] args) { Object o = new Integer(3); System.out.println(new Double(3).compareTo(o) == 0); } }

(a) true (b) false (c) Throws exception (d) None of the above

What Does It Print?


(a) true (b) false (c) Throws exception (d) None of the above: Wont compile (it did in 1.4)
compareTo(Double) in Double cannot be applied to (Object) System.out.println(new Double(3).compareTo(o) == 0); ^

The Comparable interface was generified in 5.0

Another Look
public class BeyondCompare { public static void main(String[] args) { Object o = new Integer(3); System.out.println(new Double(3).compareTo(o) == 0); } } // Interface Comparable was generified in release 5.0 public interface Comparable<T> { int compareTo(T t); // Was Object } public class Double extends Number implements Comparable<Double>

How Do You Fix It?


// Preserves 1.4 semantics public class BeyondCompare { public static void main(String[] args) { Object o = new Integer(3); System.out.println( new Double(3).compareTo((Double) o) == 0); } } // Fixes the underlying problem public class BeyondCompare { public static void main(String[] args) { Double d = 3.0; System.out.println(Double.valueOf(3).compareTo(d) == 0); } }

The Moral
Binary compatibility is preserved at all costs Source compatibility broken for good cause (rare)
Comparable<T> alerts you to errors at compile time

Take compiler diagnostics seriously


> Often there is an underlying problem

43. Fib ONacci


public class Fibonacci { private static final int LENGTH = 7; public static void main(String[] args) { int[] fib = new int[LENGTH]; fib[0] = fib[1] = 1; // First 2 Fibonacci numbers for (int i = 2; i < LENGTH; i++) fib[i] = fib[i - 2] + fib[i - 1]; System.out.println(Arrays.asList(fib)); } }

What Does It Print?


public class Fibonacci { private static final int LENGTH = 7; public static void main(String[] args) { int[] fib = new int[LENGTH]; fib[0] = fib[1] = 1; // First 2 Fibonacci numbers for (int i = 2; i < LENGTH; i++) fib[i] = fib[i - 2] + fib[i - 1]; System.out.println(Arrays.asList(fib)); } }

(a) [1, 1, 2, 3, 5, 8, 13] (b) Throws exception (c) It varies (d) None of the above

What Does It Print?


(a) [1, 1, 2, 3, 5, 8, 13] (b) Throws exception (c) It varies: Depends on hashcode [[I@ad3ba4] (d) None of the above

Arrays.asList only works on arrays of object refs

Another Look
public class Fibonacci { private static final int LENGTH = 7; public static void main(String[] args) { int[] fib = new int[LENGTH]; fib[0] = fib[1] = 1; // First 2 Fibonacci numbers for (int i = 2; i < LENGTH; i++) fib[i] = fib[i - 2] + fib[i - 1]; // Idiom only works for arrays of object references System.out.println(Arrays.asList(fib)); } }

How Do You Fix It?


public class Fibonacci { private static final int LENGTH = 7; public static void main(String[] args) { int[] fib = new int[LENGTH]; fib[0] = fib[1] = 1; // First 2 Fibonacci numbers for (int i = 2; i < LENGTH; i++) fib[i] = fib[i - 2] + fib[i - 1]; System.out.println(Arrays.toString(fib)); } }

The Moral
Use varargs sparingly in your APIs
> It can hide errors and cause confusion > This program wouldn't compile under 1.4

Arrays.asList printing idiom is

obsolete

> use Arrays.toString instead > Prettier, safer, and more powerful

A full complement of array utilities added in 5.0


equals, hashCode, toString for all array

44. Parsing Is Such Sweet Sorrow


public class Parsing { /** * Returns Integer corresponding to s, or null if s is null. * @throws NumberFormatException if s is nonnull and * doesn't represent a valid integer */ public static Integer parseInt(String s) { return (s == null) ? (Integer) null : Integer.parseInt(s); } public static void main(String[] args) { System.out.println(parseInt("-1") + " " + parseInt(null) + " " + parseInt("1")); }

What Does It Print?

public class Parsing { /** * Returns Integer corresponding to s, or null if s is null. * @throws NumberFormatException if s is nonnull and * doesn't represent a valid integer */ public static Integer parseInt(String s) { return (s == null) ? (Integer) null : Integer.parseInt(s); } public static void main(String[] args) { System.out.println(parseInt("-1") + " " + parseInt(null) + " " + parseInt("1")); }

(a) -1 null 1 (b) -1 0 1 (c) Throws exception (d) None of the above

What Does It Print?


(a) -1 null 1 (b) -1 0 1 (c) Throws exception: NullPointerException (d) None of the above

Program attempts to auto-unbox null

Another Look
public class Parsing { /** * Returns Integer corresponding to s, or null if s is null. * @throws NumberFormatException if s is nonnull and * doesn't represent a valid integer. */ public static Integer parseInt(String s) { return (s == null) ? // Mixed-type computation: Integer and int (Integer) null : Integer.parseInt(s); } public static void main(String[] args) { System.out.println(parseInt("-1") + " " + parseInt(null) + " " + parseInt("1")); } }

How Do You Fix It?


public class Parsing { /** * Returns Integer corresponding to s, or null if s is null. * @throws NumberFormatException if s is nonnull and * doesn't represent a valid integer. */ public static Integer parseInt(String s) { return (s == null) ? null : Integer.valueOf(s); } public static void main(String[] args) { System.out.println(parseInt("-1") + " " + parseInt(null) + " " + parseInt("1")); } }

The Moral
Mixed-type computations are confusing Especially true for ?: expressions Avoid null where possible Auto-unboxing and null are a dangerous mix

Resources and Summary

Resources
Send more puzzles

puzzlers@javapuzzles.com

Conclusion

Java platform is simple and elegant

But it has a few sharp corners avoid them! Avoid name reuse: overloading, hiding, shadowing

Keep programs simple

If you aren't sure what a program does, it probably doesn't do what you want it to

You might also like