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!
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Java Pitfalls and Good-to-Knows
1. Java Pitfalls and Good-to-Knows
Miquel Martin – contact@miquelmartin.org
Benjamin Hebgen – benjamin.hebgen@gmx.de
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: 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/
2
Tree in the dune by suburbanbloke on flickr: http://www.flickr.com/photos/suburbanbloke/
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 (http://goo.gl/LIodj) can lead
to “Out of MemoryError: PermGen
space”.
3
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. 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. 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. 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. 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. Preventing inheritance, overriding and
instantiating
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. 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. 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. 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. 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. 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. 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. 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. 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:
http://goo.gl/0AeYW
17
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 http://goo.gl/zRjvL
18
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. 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
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 e.next) {
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. Java 7’s NIO.2
Java 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. 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. (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