Java Pitfalls and Good-to-Knows


Published on

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!

Published in: Technology, News & Politics
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Java Pitfalls and Good-to-Knows

  1. 1. Java Pitfalls and Good-to-KnowsMiquel Martin – contact@miquelmartin.orgBenjamin Hebgen –
  2. 2. What’s this? 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!Photo credits:The splashing coffe cup at the title by 96dpi on flicker: beans on the watermark by wiedmaier on flicker: on the ReadWriteLock slide by darwinbell on flicker: 2Tree in the dune by suburbanbloke on flickr:
  3. 3. PermGen and Strings The Java Garbage Collector works on generations. The permanent one hosts internalized Strings and classes. Permanent Generation: When the compiler finds a literal Class objects, Strings. Won’t String in your code, it allocates it be Garbage Collected (if not yet there) in the string pool Tenured Generation: at the PermGen heap. All instances Multi Garbage Collection of the same String will point to the survivors same PermGen instance. Young Generation: New objects Too many literal Strings (bad design) or thinks like classloader leaks ( can lead to “Out of MemoryError: PermGen space”. 3
  4. 4. StringBuilders and StringBuffers Strings are immutable. String numberList = ""; for(int i = 0; i < 1000; i++){ The example on the right will: numberList+= i + ", "; 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 StringBuilder builder= new StringBuilder(); you have multiple threads accessing for(int i = 0; i < 1000; i++){ it, use the synchronized version, builder.append(i+", "); } StringBuffer. String numberList = builder.toString(); 4
  5. 5. From numbers to Strings How do you quickly convert a number to a String? int i = 42; String str = i; //Error, i not a String Case 1: easy but slow, a new String is created that concatenates the literal “” and i. // Case1: All time favorite (and slowest) Case 2: a new String is directly created for the String str = "" + i; number. This is 2x faster than Case 1 String str = i + ""; Case 3: it internally calls Case 2, and has roughly the same performance. // Case2: the fastest one String str = Integer.toString(myNumber); 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 // Case3: Also fast (calls Case 2) an additional String. String str = String.valueOf(myNumber); 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
  6. 6. Comparing floating point numbers Floating point numbers are encoded float f = 25 In Hexadecimal: using IEEE754-2008. Not all real 0100 0001 1100 1000 0000 0000 0000 00002 numbers can be represented (e.g. 0.2) Significand: bit ?: 1x1/1 = 1x1 = 1 Extra caution is needed when bit 9: 1x1/2 = 1x0.5 = 0.5 bit 10: 0x1/4 = 0x0.25 = 0 comparing floats (see examples) bit 11: 0x1/8 = 0x0.125 = 0 bit 11: 1x1/16 = 0x0.0625 = 0.0625 Also, portability issues due to . . evolution: Total = 1 + 0.5 + 0.0625 = 1.5625  Java ≤ 1.2: intermediate Bits 1 to 8: Exponent: 1000 00112 is 13110 calculation steps done using and then: 131-127 = 4 single or double precision Bit 0: the sign, 0 is positive Result: + 1.5625 x 24 = 25  Java > 1.2: to decrease rounding //This is true (uses floats) errors the highest platform boolean floatResult = supported precision is used for 0.1f + 0.1f + 0.1f == 0.3f; //This is false (uses doubles) intermediate results. boolean doubleResult = 0.1 + 0.1 + 0.1 == 0.3; Note: the strictfp keyword forces Java //Also false (also doubles) boolean doubleResult = 1.2 behavior 0.1d + 0.1d + 0.1d == 0.3d; 6
  7. 7. Volatile variables Depending on the JVM, threads package operators; may work on copies of variables class Test extends Thread { boolean done = false; and never re-check the original public void run() { 1 reference. while (!done) { This does not happen if the } variable is marked volatile, or the System.out.println("Thread terminated."); variable access is synchronized. } Both tell the JVM that multi- thread access is likely and to check public static void main(String[] args) throws InterruptedException { the original reference. Test t = new Test(); The code on the right will never t.start(); finish unless (1) is volatile. The Thread.sleep(1000); behavior can change between t.done = true; JVMs. Also, volatile does not solve System.out.println("done"); concurrent modification issues. } } Don’t rely on volatile unless you know exactly what you’re doing. 7
  8. 8. Breaking out of the right loop The break keyword takes a label as a mytag: for(int i =0; i < 10; i++){ parameter and breaks out of the for(j =0; j < 10; j++){ if(done) { scope tagged for it break mytag; } } } This works also on switch, for, while and do-while both for continue and break 8
  9. 9. Preventing inheritance, overriding andinstantiating Controlling how your class is used // A Utility class has only static methods comes in handy: singletons, factories, public class UtilityClass { utility classes, etc… // Private constructor prevents // external class instantiation private TestClass() { The final keyword applied to a: }  Class: prevents extending public static int getInstance() {  method: prevents overriding return new TestClass();  primitive field: prevents changing } } the value  Object field: prevents changing // Final class cannot be extended the reference (you can still public final class FinalClass { change the object) // Final primitive cannot be modified final boolean something = false; A private default constructor will // Reference to final object cannot additionally prevent external //be modified final StringBuilder builder= new StringBuilder(); instantiation A final Class with only one private // Final method cannot be overriden constructor cannot be extended or public final void someMethod() { } overridden: it will never be externally } instantiated 9
  10. 10. Static code blocks Static blocks let you execute public class StaticBlocks { private static int number = 1; static { something at class loading time System.out.println(number); } (e.g. assign a value to a static static { number = 2; field) } Static blocks and static field static { System.out.println(number); } assignment is done once at static { class loading } number = increaseNumber(number); The execution order is the same static { System.out.println(number); as the order in the code } 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 } } 10
  11. 11. Finally we finalize the final. final is already covered try { System.out.println("In try"); throw new Exception(); finalize will be called on an instance } catch (Exception e) { when (if!) it is garbage collected. System.out.println("In catch");  You have no real control of when } finally { the GC will pass. Plus it’s bad System.out.println("In finally"); practice and often ignored by JVMs }  Do not use it to free resources. // Outputs: In try, In catch, In finally  It’s not the C++ destructor, finally goes after a try…catch block: BufferedWriter writer = null;  First, run the code in the try try {  If an exception is thrown, run the writer = Files.newBufferedWriter(file, appropriate code in catch charset);  Then, no matter what, run the code writer.write(s, 0, s.length()); in finally. Even after a return in } catch (IOException x) { catch or try. System.err.println("Error happened"); It’s worth using finally even if no } finally { exceptions are expected. They might be if (writer != null) writer.close(); thrown in a future code change. } 11
  12. 12. 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
  13. 13. Protective copies In this code, some internal logic class Test { occurs on adding/removing private List<String> internalStuff = new ArrayList<String>(); stuff. public void addStuff(String stuff) { //Do internal logic If getAllStuff returns internalStuff.add(stuff); internalStuff, the caller can } public void removeStuff(String stuff) { add/remove items without //Do internal logic going through removeStuff internalStuff.remove(stuff); } If this is (or could be) an issue, public List<String> getAllStuff(){ //Dangerous: return internalStuff; create a protective copy of //Better: internalStuff first return new ArrayList<String>(internalStuff); } } 13
  14. 14. Shallow comparison and Arrays Comparison types: String[] a1 = new String[] { "0", "1", "2" }; String[] a2 = new String[] { "0", "1", "2" };  Shallow: uses == and System.out.println(a1 == a2); // false compares object System.out.println(a1.equals(a2)); // false 1 System.out.println(Arrays.equals(a1, a2)); // true references  Deep: uses equals and is List<String> list1 = new ArrayList<String>(3); List<String> list2 = new ArrayList<String>(3); as good as your equals list1.add("0"); implementation list2.add("0"); list1.add("1"); list2.add("1"); list1.add("2"); list2.add("2"); Notable exception: equals is System.out.println(list1 == list2); // false odd for arrays (1) and will System.out.println(list1.equals(list2)); perform a shallow // true if the elements in the list properly implement equals comparison. Use Arrays.equals instead 14
  15. 15. Checked VS Unchecked exceptions Unlike Unchecked exceptions, Throwable Checked exceptions need to be caught or declared Pros of unchecked exceptions: Exception Error  Clearer code  You can still treat them like ? extends Runtime ? extends Exception checked exceptions if you Exception Error want ? extends Runtime Cons: Easy to miss Exception Unchecked Exceptions  JsonParseException in Gson  NumberFormatException from new Integer(“3,4”) in some locales 15
  16. 16. Testing with Hamcrest Matchers In JUnit, assertTrue, public class RegexMatcher extends BaseMatcher<String> { assertEquals, etc… perform private final String regex; specific assertions public RegexMatcher(String regex) { this.regex = regex; } For arbitrary assertions, you @Override can use a Hamcrest Matcher public boolean matches(Object s) { return ((String) s).matches(regex); and run it with assertThat } @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)); 16
  17. 17. Type erasure The Java generics implementation public void doSomething(List<String> does not change the compiler list) {} much, instead, it pre-processes public void doSomething(List<Integer> away the generics list) { } At compile time, type parameters // Compiler error: both methods can’t are replaced with their bounds be in the same class, since they have the same type erasure 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: 17
  18. 18. 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 18
  19. 19. Synchronized Collections You’ll still find online that: List<String> unsynchronizedList = new  A Hashtable is like a HashMap but ArrayList<String>(); synchronized (there’s more to it, List<String> synchronizedList = like null values) Collections.synchronizedList(unsynchro  A Vector is like an ArrayList but nizedList); synchronized (it isn’t) This hasn’t been true since Java Map<String, String> unsynchronizedMap = 1.3 introduced the Synchronized new LinkedHashMap<String, String>(); Collections Map<String, String> synchronizedMap = Note that Read/Write locks are Collections.synchronizedMap(unsynchron Java >=1.5 so there’s plenty room izedMap); for optimizing if you need to. Notable exception: ConcurrentHashMap is (probably) your high-performance map for multi-threaded access 19
  20. 20. Legacy ClassesLegacy classes are not (yet) deprecated but their use isdiscouraged. They still pop up in plenty of tutorials andsnippets. 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
  21. 21. equals, hashCode and Comparable // HashMap put  Different collections use public V put(K key, V value) { if (key == null) different mechanisms to sort return putForNullKey(value); int hash = hash(key.hashCode()); out objects, for example: // Locate the bucket by hashCode  HashMap uses hashCode int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e =  TreeSet uses Comparable { Object k;  If hashCode and Comparable // Check for equality only in the bucket if (e.hash == hash && ((k = e.key) == key || are not consistent with equals, key.equals(k))) { V oldValue = e.value; expect inconsistencies! Check e.value = value; e.recordAccess(this); the contract. } return oldValue; } modCount++; addEntry(hash, key, value, i); return null; } 21
  22. 22. Java 7’s NIO.2Java 7’s new file I/O makes file system Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);interactions much easier WatchService Implemented in java.nio.file.Files and watchService = new WatchService() { ... java.nio.file.Path }; path.register(watchService, ENTRY_CREATE, It provides common file operations ENTRY_DELETE, ENTRY_MODIFY); like copy or readAllLines Files.getPosixFilePermissions(path, You can get notifications on Path LinkOption.NOFOLLOW_LINKS); modifications Files.isExecutable(path); Files.isReadable(path); Support for permission and owner Files.isWritable(path); handling and links Files.readAllLines(path, Charset.forName("utf-8")); Fine grained exception handling Files.write(path, "mytext".getBytes(Charset. forName("utf-8")), StandardOpenOption.CREATE); 22
  23. 23. Where to store resources Best practice for user specific String winPath = System.getProperty(" user.home") + File.separator + configuration options per OS: "Application Data" + File.separator + "myApp"; String linuxPath = System.getProperty ("user.home") + File.separator + ".myApp"; Storing in your classpath (e.g. in your TestClass.class.getResourceAsStream(" myresource.xml") jar) using Class.getResourceAsStream If you really really want, there is a way //This will find your class to find the path to your class, allowing MyClass.class.getProtectionDomain().g etCodeSource().getLocation().getP for storage relative to the installation ath(); folder 23
  24. 24. (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  Google’s Guava: for a ton of convenience classes and very interesting containers  OPS4J: for all sort of OSGi related helpers 24
  25. 25. Thanks and Happy Coding! 25