27-JavaGotchas

Download Report

Transcript 27-JavaGotchas

Java Gotcha's
By Rick Mercer with help from the book:
Java™ Puzzlers: Traps, Pitfalls, and Corner Cases
Joshua Bloch and Neal Gafter
1




Every programming language has its
quirks.
Are you a code sleuth?
Have you ever spent days chasing a bug
caused by a trap or pitfall in Java or its
libraries?
Here are some diabolical puzzles
2
Oddity
// Does this method return true if n is odd?
public boolean isOdd(int n) {
return n % 2 == 1;
}
3
Ö

Yes, but it's wrong when n is negative
// Try this
return n % 2 != 0;
// or use bitwise AND operation (faster)
return (n & 1) == 1;
// n & 1 ands the rightmost bit with 1
//
if n is 25, n & 1 is 1
//
00011001 & 00000001 is 00000001
// Expression is 0 unless n is odd
//
if n is 24, n & 1 is 0
//
00011000 & 00000001 is 00000000
4
Simple Arithmetic
@Test
public void simpleArithmetic() {
// Does this assertion pass?
assertEquals(444, 123 + 32l);
}
5
Ö



Eyes Deceive
It is 123 + 32L, which is 155
Use L instead of l for Long
6
The laughs are on me
@Test
public void simpleChars() {
// Which, if any, of these two assertions pass?
assertEquals("Ha", "H" + "a"); // a.
assertEquals("Ha", 'H' + 'a'); // b.
}
7
Ö

Answer

Only a.
java.lang.AssertionError:
expected:<Ha> but was:<169>
8
Operator Precedence
@Test
public void stringIntern() {
String pig = "length: 10";
String dog = "length: " + pig.length();
// Which, if any, of these two assertions pass?
assertEquals("Animals equal: false",
"Animals equal: " + pig == dog);
assertEquals("Animals equal: true",
"Animals equal: " + pig == dog);
}
9
Ö

Neither. Precedence rules have + evaluating before
==. The actual value (2nd arg to assert) is
"Animals equal: " + pig == dog
which evaluates to
"Animals equal: pig" == dog
which as an argument evaluates to false
•
assertEquals uses the equals method
when the type do not match, equals returns false
"string".equals(true) is false
•
These asssertions pass
•
•
assertEquals("Animals equal: false", "Animals equal: " + (pig == dog));
assertEquals("Animals equal: true", "Animals equal: " + (pig.equals(dog)));
10
From a 227 Student
// Part of escape obstacle course in findExit
if(escape = false)
findExit(r-1, c);
if(escape = false)
findExit(r, c+1);

// row above
// col to the right
Can this recursive solution ever work?
11
Ö

No


The boolean expressions are ALWAYS false
An assignment statement evaluates to the value of the right
value (expression to the right of =)
 What is the value of booleanVar = true
12
From a 127B Student
@Test
public void testRecursion() {
assertEquals(6, sumInts(3));
}
int sumInts(int n) {
if(n <= 1)
return n;
else
return n * sumInts(n--);
}

Can this recursive solution ever work?
13
Ö

No



n is not decremented until after the function call
f(n--) results in a StackOverflowError
Use f(n-1)
14
Output from this program?
\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
15
Ö

Answer:
Hello World
 \u0070 in hexadecimal is 112 in decimal or the
character 'p'



Unicode not very readable
Suggestion" avoid Unicode till you need it
System.out.println(123.45 + " \u20ac" );
 123.45 €
16
assertEquals('@', '\u0040');
assertEquals('A', '\u0041');
assertEquals('B', '\u0042');
assertEquals('`', '\u0060');
assertEquals('a', '\u0061');
assertEquals('b', '\u0062');
assertEquals('€', '\u20ac');
17
Is “true” true?
@Test
public void trueOr() {
// Does this assertion pass?
assertEquals("Compare 5 to 4", "true", 5 > 4);
}
18
Ö

Answer



No
"true" is not true
However, JUnit show this:
java.lang.AssertionError:
Compare 5 to 4 expected:<true> but was:<true>
19
Output?
@Test
public void whoopsForgotToBreak() {
int choice = 2;
switch (choice) {
case 1:
System.out.println("one");
case 2:
System.out.println("two");
case 3:
System.out.println("three");
}
}
20
Ö
two
three

Add breaks
int choice = 2;
switch (choice) {
case 1:
System.out.println("one");
break;
case 2:
System.out.println("two");
break;
case 3:
System.out.println("three");
break;
}
21
BTW:

Java 7 allows String in a switch statement
String choice = "2";
switch (choice) {
case "1":
System.out.println("one");
break;
case "2":
System.out.println("two");
break;
case "3":
System.out.println("three");
break;
}
22
Java plus plus
@Test
public void thatDarnedPostIncrement() {
int j = 0;
for (int i = 0; i < 10; i++)
j = j++;
// Does this assertion pass?
assertEquals(10, j);
}
23
Ö

Answer: No
j = j++; is postfix increment operator
 j is 0 after the loop
When you use a postfix operator as part of a
larger expression, the expression's value is
returned before the postfix operator is processed




the assignment completes before the increment
Use
++j; instead
24
Output?
int j = 0;
int k = 0;
System.out.println(j++);
System.out.println(++k);
System.out.println(j);
//? _____
//? _____
//? _____
int[] x = { 5, 4, 3 };
int i = 0;
System.out.println(i + " " + x[i++]); //? _____
System.out.println(i + " " + x[i]);
//? _____
System.out.println(i + " " + x[++i]); //? _____
25
O
0
1
1
With array
0 5
1 4
1 3
26
Is there any Output?
public class Huh {
public static void main(String[] args) {
new B();
}
}
class B {
int j;
String s;
{
System.out.println("Hello world " + j + " " + s);
}
}
27
Ö

Answer: Yes
Hello world 0 null

This is an initializer, a method with no
heading;

{ }
28
Add to 0 three times
@Test
public void testBigInt() {
BigInteger five = new BigInteger("5");
BigInteger fifty = new BigInteger("50");
BigInteger fiveHundred = new BigInteger("500");
BigInteger total = BigInteger.ZERO;
total.add(five);
total.add(fifty);
total.add(fiveHundred);
// Does this assertion pass?
assertEquals(555, total);
}
29
Ö



No
BigInteger, like String is immutable
This will pass
BigInteger total = BigInteger.ZERO;
total = total.add(five);
total = total.add(fifty);
total = total.add(fiveHundred);
// Does this assertion pass
assertEquals(555, total);
30
No Warning
@Test
public void testHashMap() {
HashMap<String, BigInteger> hm =
new HashMap<String, BigInteger>();
hm.put("a", new BigInteger("123456"));
hm.put("b", new BigInteger("1234567"));
hm.put("c", new BigInteger("1234567"));
hm.put("a", new BigInteger("654321"));
BigInteger aBigInt = hm.get("a");
// Does this assertion pass?
assertEquals(123456, aBigInt.intValue());
}
31
Ö

No, the first mapping was destroyed
// Return old value if the key exists
// return null if there was no mapping to the key
hm.put("a", new BigInteger("123456"));
BigInteger bi = hm.put("a", new BigInteger("9999"));
// put returned the old value mapped to the key "a"
assertEquals(123456, bi.intValue());
32
Output?
int n = 0;
try {
n = n / 0;
} catch (Exception e) {
System.out.println("A");
} finally {
System.out.println("B");
}
System.out.println("C");
33
Ö
A
B
C

Finally blocks always execute unless
System.exit(0) is encountered first
34
Output?
int n = 0;
try {
n = n / 999;
} catch (Exception e) {
System.out.println("A");
} finally {
System.out.println("B");
}
System.out.println("C");
35
Ö
B
C
36
Output
int n = 0;
try {
n = n / 999;
} catch (Exception e) {
System.out.println("A");
System.exit(0);
} finally {
System.out.println("B");
}
System.out.println("C");
37
Ö
B
C
38
Output
int n = 0;
try {
n = n / 0;
} catch (Exception e) {
System.out.println("A");
System.exit(0);
} finally {
System.out.println("B");
}
System.out.println("C");
39
Ö
A
40
String data = new String("123"); String moreData = new String("123"); System.out.println(data==moreData);
To intern or not to intern
Which assertion(s) pass?
a)
b)
c)
d)
1 and 2
1 only
2 only
Neither
@Test
public void stringIntern() {
String s1 = "UofA";
String s2 = new String("UofA");
assertTrue(s1.equals(s2)); // 1
assertTrue(s1 == s2);
// 2
}
41
Ö
b) 1 only
•
•
•
== compares reference values, with new, a new string is
created. Without new, Java tries to find the characters in the
string pool where all instances of String are stored. If found,
Java returns a reference to the existing instance
In Java, String is a Flyweight
• to save memory
Both of these assertions pass:
String s1 = "UofA";
String s2 = "UofA";
assertTrue(s1.equals(s2)); // 1
assertTrue(s1 == s2);
// 2
42
To intern or not to intern
@Test
public void
Integer a
Integer b
Integer c
Integer d
Integer e
Integer f
testIntegerInterns() {
= -128;
= -128;
= 127;
= 127;
= 345;
= 345;
// Which, if
assertTrue(a
assertTrue(c
assertTrue(e
assertTrue(e
}
any, of these assertions fail?
== b); // a.
== d); // b.
>= f); // c.
== f); // d.
43
Ö

d only


== compares reference values so you would think
all 3 fail, but....
java.sun.com/docs/books/jls/download/langspec3.0.pdf explicitly states that wrappers for values in
the range -128 to 127 will be interned by any JVM.
If you use the int literal, you get a reference to that
existing instance

Flyweight: Do not create a new Integer(1), just
return a reference to that instance from the existing
pool of Integers -128..127
 to save memory
44
Are Doubles interned?
@Test
public void testEqualEquals() {
Double a = 4.2;
Double b = 4.2;
// Which, if any, of these assertions fail?
assertTrue(a >= b);
// a.
assertTrue(a <= b);
// b.
assertTrue(a.equals(b)); // c.
assertTrue(a == b);
// d.
assertTrue(a.compareTo(b) == 0);
// e.
assertTrue(a.compareTo(4.2) == 0); // f.
}
45
Ö
assertTrue(a == b); // d. fails


The == compares reference values, not the
numeric values
Why does a == b evaluate to false?

There is no Double pool
a and b refer to two different objects with the
same value
 == compares references, not the 4.2s

46