java pitfalls and good-to-knows

25
Java Pitfalls and Good-to-Knows Miquel Martin [email protected] Benjamin Hebgen [email protected]

Upload: miquel-martin

Post on 11-Nov-2014

3.719 views

Category:

Technology


3 download

DESCRIPTION

This slide set covers some Java tidbits you might not encounter on your day-to-day java development. Perhaps some of them will improve your coding and save you some debugging!

TRANSCRIPT

Page 1: Java Pitfalls and Good-to-Knows

Java Pitfalls and Good-to-Knows

Miquel Martin – [email protected] Benjamin Hebgen – [email protected]

Page 2: Java Pitfalls and Good-to-Knows

What’s this?

Photo credits:

The splashing coffe cup at the title by 96dpi on flicker: http://www.flickr.com/photos/96dpi/

Coffee beans on the watermark by wiedmaier on flicker: http://www.flickr.com/photos/wiedmaier/

Padlock on the ReadWriteLock slide by darwinbell on flicker: http://www.flickr.com/photos/darwinbell/

Tree in the dune by suburbanbloke on flickr: http://www.flickr.com/photos/suburbanbloke/ 2

This slide set covers some Java tidbits you might not encounter on your day-to-day java development. Perhaps some of them will improve your coding and save you some debugging!

Page 3: Java Pitfalls and Good-to-Knows

PermGen and Strings The Java Garbage Collector works

on generations. The permanent one hosts internalized Strings and classes.

When the compiler finds a literal String in your code, it allocates it (if not yet there) in the string pool at the PermGen heap. All instances of the same String will point to the same PermGen instance.

Too many literal Strings (bad design) or thinks like classloader leaks (http://goo.gl/LIodj) can lead to “Out of MemoryError: PermGen space”.

3

Young Generation: New objects

Tenured Generation: Multi Garbage Collection survivors

Permanent Generation: Class objects, Strings. Won’t be Garbage Collected

Page 4: Java Pitfalls and Good-to-Knows

StringBuilders and StringBuffers String numberList = "";

for(int i = 0; i < 1000; i++){

numberList+= i + ", ";

}

Strings are immutable.

The example on the right will: 1. Create the numberList String 2. Create a newNumberList with the

numberList, I and a “, “ 3. Assign newNumberList to numberList 4. Repeat 1000 times 2 & 3

This is slow, fills up your heap and therefore forces more costly garbage collections

Use instead a mutable StringBuilder. If you have multiple threads accessing it, use the synchronized version, StringBuffer.

4

StringBuilder builder= new StringBuilder();

for(int i = 0; i < 1000; i++){

builder.append(i+", ");

}

String numberList = builder.toString();

Page 5: Java Pitfalls and Good-to-Knows

From numbers to Strings int i = 42;

String str = i; //Error, i not a String // Case1: All time favorite (and slowest) String str = "" + i; String str = i + ""; // Case2: the fastest one String str = Integer.toString(myNumber); // Case3: Also fast (calls Case 2) String str = String.valueOf(myNumber);

How do you quickly convert a number to a String? Case 1: easy but slow, a new String is created that

concatenates the literal “” and i. Case 2: a new String is directly created for the

number. This is 2x faster than Case 1 Case 3: it internally calls Case 2, and has roughly

the same performance.

Exception: in “The number is “ + i, all three cases are similar since String concatenation must happen anyway, but Case 2 and 3 need to explicitly create an additional String.

This also works on: short, float, double, long, etc..

Take home lesson: ignore this and do whatever’s

more readable, unless performance is really critical

5

Page 6: Java Pitfalls and Good-to-Knows

Comparing floating point numbers

//This is true (uses floats) boolean floatResult = 0.1f + 0.1f + 0.1f == 0.3f; //This is false (uses doubles) boolean doubleResult = 0.1 + 0.1 + 0.1 == 0.3; //Also false (also doubles) boolean doubleResult = 0.1d + 0.1d + 0.1d == 0.3d;

Floating point numbers are encoded using IEEE754-2008. Not all real numbers can be represented (e.g. 0.2)

Extra caution is needed when comparing floats (see examples)

Also, portability issues due to evolution:

Java ≤ 1.2: intermediate calculation steps done using single or double precision

Java > 1.2: to decrease rounding errors the highest platform supported precision is used for intermediate results.

Note: the strictfp keyword forces Java 1.2 behavior

6

float f = 25

In Hexadecimal:

0100 0001 1100 1000 0000 0000 0000 00002

Bit 0: the sign, 0 is positive

Bits 1 to 8: Exponent: 1000 00112 is 13110

and then: 131-127 = 4

Significand: bit ?: 1x1/1 = 1x1 = 1 bit 9: 1x1/2 = 1x0.5 = 0.5 bit 10: 0x1/4 = 0x0.25 = 0 bit 11: 0x1/8 = 0x0.125 = 0 bit 11: 1x1/16 = 0x0.0625 = 0.0625

. . Total = 1 + 0.5 + 0.0625 = 1.5625

Result: + 1.5625 x 24 = 25

Page 7: Java Pitfalls and Good-to-Knows

Volatile variables package operators;

class Test extends Thread {

boolean done = false;

public void run() {

while (!done) {

}

System.out.println("Thread terminated.");

}

public static void main(String[] args) throws

InterruptedException {

Test t = new Test();

t.start();

Thread.sleep(1000);

t.done = true;

System.out.println("done");

}

}

Depending on the JVM, threads may work on copies of variables and never re-check the original reference.

This does not happen if the variable is marked volatile, or the variable access is synchronized. Both tell the JVM that multi-thread access is likely and to check the original reference.

The code on the right will never finish unless (1) is volatile. The behavior can change between JVMs. Also, volatile does not solve concurrent modification issues. Don’t rely on volatile unless you know exactly what you’re doing.

7

1

Page 8: Java Pitfalls and Good-to-Knows

Breaking out of the right loop

mytag: for(int i =0; i < 10; i++){

for(j =0; j < 10; j++){

if(done) {

break mytag;

}

}

}

The break keyword takes a label as a parameter and breaks out of the scope tagged for it

This works also on

switch, for, while and do-while

both for

continue and break

8

Page 9: Java Pitfalls and Good-to-Knows

Preventing inheritance, overriding and instantiating Controlling how your class is used

comes in handy: singletons, factories, utility classes, etc…

The final keyword applied to a: Class: prevents extending method: prevents overriding primitive field: prevents changing

the value Object field: prevents changing

the reference (you can still change the object)

A private default constructor will additionally prevent external instantiation

A final Class with only one private constructor cannot be extended or overridden: it will never be externally instantiated

9

// A Utility class has only static methods

public class UtilityClass {

// Private constructor prevents

// external class instantiation private TestClass() { } public static int getInstance() {

return new TestClass(); } }

// Final class cannot be extended

public final class FinalClass { // Final primitive cannot be modified

final boolean something = false;

// Reference to final object cannot

//be modified final StringBuilder builder= new StringBuilder();

// Final method cannot be overriden

public final void someMethod() { } }

Page 10: Java Pitfalls and Good-to-Knows

Static code blocks public class StaticBlocks { private static int number = 1; static { System.out.println(number); } static { number = 2; } static { System.out.println(number); } static { number = increaseNumber(number); } static { System.out.println(number); } public static int increaseNumber(int i) { return i + 1; } public static void main(String[] args) { System.out.println(number); //Aggregatted output: 1, 2, 3, 3 } }

Static blocks let you execute something at class loading time (e.g. assign a value to a static field)

Static blocks and static field assignment is done once at class loading

The execution order is the same as the order in the code

10

Page 11: Java Pitfalls and Good-to-Knows

Finally we finalize the final. try {

System.out.println("In try");

throw new Exception();

} catch (Exception e) {

System.out.println("In catch");

} finally {

System.out.println("In finally");

}

// Outputs: In try, In catch, In finally

BufferedWriter writer = null;

try {

writer = Files.newBufferedWriter(file,

charset);

writer.write(s, 0, s.length());

} catch (IOException x) {

System.err.println("Error happened");

} finally {

if (writer != null) writer.close();

}

final is already covered

finalize will be called on an instance when (if!) it is garbage collected. You have no real control of when

the GC will pass. Plus it’s bad practice and often ignored by JVMs

Do not use it to free resources. It’s not the C++ destructor,

finally goes after a try…catch block:

First, run the code in the try If an exception is thrown, run the

appropriate code in catch Then, no matter what, run the code

in finally. Even after a return in catch or try.

It’s worth using finally even if no exceptions are expected. They might be thrown in a future code change.

11

Page 12: Java Pitfalls and Good-to-Knows

Keywords you don’t see so often • assert: evaluate a boolean expression and throw an AssertionException if false. Useful to

write your assumptions in the code, and get notified if you were wrong

• continue: skip to the next iteration of a loop

• strictfp: ensures consistent floating point operations on different platforms

• transient: keep the field from being serialized when using standard java serialization

mechanisms

• volatile: prevents threads from working with a thread local copy of a variable when not

using synchronized access

• native: denotes that the method is provided by an external non-java library using JNI

• goto: it’s a reserved keyword but a valid keyword. The compiler will throw a syntax error

• const: same as goto

12

Page 13: Java Pitfalls and Good-to-Knows

Protective copies

class Test {

private List<String> internalStuff = new

ArrayList<String>();

public void addStuff(String stuff) {

//Do internal logic

internalStuff.add(stuff);

}

public void removeStuff(String stuff) {

//Do internal logic

internalStuff.remove(stuff);

}

public List<String> getAllStuff(){

//Dangerous: return internalStuff;

//Better:

return new ArrayList<String>(internalStuff);

}

}

In this code, some internal logic occurs on adding/removing stuff.

If getAllStuff returns internalStuff, the caller can add/remove items without going through removeStuff

If this is (or could be) an issue, create a protective copy of internalStuff first

13

Page 14: Java Pitfalls and Good-to-Knows

Shallow comparison and Arrays String[] a1 = new String[] { "0", "1", "2" }; String[] a2 = new String[] { "0", "1", "2" }; System.out.println(a1 == a2); // false System.out.println(a1.equals(a2)); // false System.out.println(Arrays.equals(a1, a2)); // true List<String> list1 = new ArrayList<String>(3); List<String> list2 = new ArrayList<String>(3); list1.add("0"); list2.add("0"); list1.add("1"); list2.add("1"); list1.add("2"); list2.add("2"); System.out.println(list1 == list2); // false System.out.println(list1.equals(list2)); // true if the elements in the list properly

implement equals

Comparison types:

Shallow: uses == and compares object references

Deep: uses equals and is as good as your equals implementation

Notable exception: equals is odd for arrays (1) and will perform a shallow comparison. Use Arrays.equals instead

14

1

Page 15: Java Pitfalls and Good-to-Knows

Unchecked Exceptions

Checked VS Unchecked exceptions Unlike Unchecked exceptions,

Checked exceptions need to be caught or declared

Pros of unchecked exceptions:

Clearer code

You can still treat them like checked exceptions if you want

Cons: Easy to miss JsonParseException in Gson

NumberFormatException from new Integer(“3,4”) in some locales

15

Throwable

Exception Error

? extends Error

Runtime Exception

? extends Runtime

Exception

? extends Exception

Page 16: Java Pitfalls and Good-to-Knows

Testing with Hamcrest Matchers public class RegexMatcher extends

BaseMatcher<String> { private final String regex; public RegexMatcher(String regex) { this.regex = regex; } @Override public boolean matches(Object s) { return ((String) s).matches(regex); } @Override public void describeTo(Description description)

{ description.appendText("matches " + regex); } public static RegexMatcher matches(String regex)

{ return new RegexMatcher(regex); } } //Allows you to do: assertThat(actual, matches(regex));

In JUnit, assertTrue, assertEquals, etc… perform specific assertions

For arbitrary assertions, you can use a Hamcrest Matcher and run it with assertThat

16

Page 17: Java Pitfalls and Good-to-Knows

Type erasure public void doSomething(List<String>

list) {} public void doSomething(List<Integer>

list) { } // Compiler error: both methods can’t

be in the same class, since they have the same type erasure

The Java generics implementation does not change the compiler much, instead, it pre-processes away the generics

At compile time, type parameters are replaced with their bounds

Types are “erased” in the compiled bytecode

Famous pitfall: after type erasure, List<String> is the same as List<Integer>

There’s plenty more interesting gotchas in generics, check: http://goo.gl/0AeYW

17

Page 18: Java Pitfalls and Good-to-Knows

Read/Write Locks Case: you don’t care how many

threads read your object, as long as no one is modifying it, and then, only one at a time.

Solution: replace your synchronized methods with ReadWriteLocks

Beware of the details:

in balanced read/write scenarios, the overhead of a ReadWriteLock will likely degrade performance.

Thread starvation and fairness are an issue. Check ReentrantLocks

A good discussionL http://goo.gl/zRjvL

18

Page 19: Java Pitfalls and Good-to-Knows

Synchronized Collections

List<String> unsynchronizedList = new

ArrayList<String>();

List<String> synchronizedList =

Collections.synchronizedList(unsynchro

nizedList);

Map<String, String> unsynchronizedMap =

new LinkedHashMap<String, String>();

Map<String, String> synchronizedMap =

Collections.synchronizedMap(unsynchron

izedMap);

You’ll still find online that:

A Hashtable is like a HashMap but synchronized (there’s more to it, like null values)

A Vector is like an ArrayList but synchronized (it isn’t)

This hasn’t been true since Java 1.3 introduced the Synchronized Collections

Note that Read/Write locks are Java >=1.5 so there’s plenty room for optimizing if you need to.

Notable exception: ConcurrentHashMap is (probably) your high-performance map for multi-threaded access

19

Page 20: Java Pitfalls and Good-to-Knows

Legacy Classes Legacy classes are not (yet) deprecated but their use is discouraged. They still pop up in plenty of tutorials and snippets.

Vector and Dictionary/Hashtable should not be used as synchronized List and Map. Use Collections.synchronizedList and Collections.synchronizedMap instead. Also consider ConcurrentHashMap.

Properties extends Hashtable with file writing capabilities, but there’s no replacement.

Stack is a revamp of Vector to work like a LinkedList. Use that instead.

StringTokenizer is better implemented by String.split and also handles encoding better

Enumeration is very close to an Iterator but does less (e.g. remove)

Most discouraged classes are parallel implementations of existing classes with a minor (often mis-implemented) delta.

20

Page 21: Java Pitfalls and Good-to-Knows

equals, hashCode and Comparable // HashMap put public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); // Locate the bucket by hashCode int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // Check for equality only in the bucket if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }

Different collections use different mechanisms to sort out objects, for example:

HashMap uses hashCode

TreeSet uses Comparable

If hashCode and Comparable are not consistent with equals, expect inconsistencies! Check the contract.

21

Page 22: Java Pitfalls and Good-to-Knows

Java 7’s NIO.2 Files.copy(source, target,

StandardCopyOption.REPLACE_EXISTING); WatchService

watchService = new WatchService() { ... }; path.register(watchService, ENTRY_CREATE,

ENTRY_DELETE, ENTRY_MODIFY); Files.getPosixFilePermissions(path,

LinkOption.NOFOLLOW_LINKS); Files.isExecutable(path); Files.isReadable(path); Files.isWritable(path); Files.readAllLines(path,

Charset.forName("utf-8")); Files.write(path, "mytext".getBytes(Charset.

forName("utf-8")), StandardOpenOption.CREATE);

Java 7’s new file I/O makes file system interactions much easier

Implemented in java.nio.file.Files and java.nio.file.Path

It provides common file operations like copy or readAllLines

You can get notifications on Path modifications

Support for permission and owner handling and links

Fine grained exception handling

22

Page 23: Java Pitfalls and Good-to-Knows

Where to store resources String winPath = System.getProperty("

user.home") + File.separator + "Application Data" + File.separator + "myApp";

String linuxPath = System.getProperty("user.home") + File.separator + ".myApp";

TestClass.class.getResourceAsStream("myresource.xml")

//This will find your class

MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath();

Best practice for user specific configuration options per OS:

Storing in your classpath (e.g. in your jar) using Class.getResourceAsStream

If you really really want, there is a way to find the path to your class, allowing for storage relative to the installation folder

23

Page 24: Java Pitfalls and Good-to-Knows

(Avoid) Reinventing the wheel

There are a lot of 3rd party libraries for Java. Avoid reinventing the wheel! Also beware of very alpha libraries!

You will only use stuff you know exists. Consider browsing through the documentation of libraries like:

Apache Commons: for anything from logging to collections and much more http://commons.apache.org/

Google’s Guava: for a ton of convenience classes and very interesting containers http://code.google.com/p/guava-libraries/

OPS4J: for all sort of OSGi related helpers http://team.ops4j.org

24

Page 25: Java Pitfalls and Good-to-Knows

25

Thanks and Happy Coding!