Abstract: In his latest book, Jim Waldo describes several Java features that he believes make Java "good". A nice easy read, and I even learned a few new things from it.
Welcome to the 185th issue of The Java(tm) Specialists' Newsletter. I mentioned in my last newsletter that I had lost my driver's license for two months. Turns out that I was driving 83km/h in a 90 zone, which the Greek police incorrectly thought was a 50 zone. However, I know that it is easier to simply not drive for two months than to argue my case with the Greek police :-) Initially it was frustrating not being able to drive, but now I am getting used to it. I walked to the beach with my son and dog the other morning and we went for a long swim out. Our canine is a natural swimmer, and if it wasn't for the rope that I used to pull him back, would have probably ended up in Kalathas.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
Some years ago, a friend of mine managed to get my name onto a reviewer list at a publishing house, which means that they send me free books about Java that they think I would find interesting. Lately, it has been sad how few new or interesting books have come my way. We are all waiting for Java 7 to be officially released, which will hopefully happen at JavaOne. This should cause a whole flurry of books to be written. Until then, we are stuck with the not always accurate memoirs of Java experts facing retirement.
When I received Java: The Good Parts by Jim Waldo [ISBN 0596803737] , I was excited at the prospect of reading a good book on Java. After all, it has "good" in the title, so it must be? An initial curtive glance showed some interesting points, which raised my expectations even further. Jim Waldo is a distinguished engineer at Sun, so perhaps I could learn something new?
Waldo captured the Good Parts of Java quite nicely. Here they are: The Type System, Exceptions, Packages, Garbage Collection, The Java Virtual Machine, Javadoc, Collections, Remote Method Invocation and Object Serialization, Concurrency and The Developer Ecology. Yes, that does look like a list of Good Parts of Java. What is also good is the price, how often do you get a technical book for under $20? Here again is the link to the book on Amazon. [ISBN 0596803737]
When I first read this chapter, I was not happy with the class hierarchy that Waldo defined for different roles that a baseball player might have. Later in the book, however, Waldo corrected the class structure and ended up with a nice design. Don't let the first examples put you off.
When talking about the type system, one thing that should
have been added is that every object in Java has a pointer
back to its type. We can call getClass()
on any
object and that will give us back the implementation class.
This is useful as it gives us type safety, but also means
that every object in Java carries around this memory that in
many cases is just wasted. In a 32-bit system, this
represents 4 bytes per object. In a 64-bit system, it would
use up 8 bytes each.
Waldo gives us an example of how to use checked exceptions. If we don't have enough values to produce decent baseball statistics, he says we should throw a NotEnoughAtBatsException. However, he does not provide a way to find out whether we had enough data points to calculate the statistics. This is a design flaw in my opinion.
Jim Waldo makes a small mistake on pages 35-36, where he suggests that it is possible to have unreachable catch statements in Java. For example, he argues that something like this could compile and could hide exceptions:
public class ExceptionOrder { public static void main(String[] args) { try { throw new NullPointerException(); } catch (Exception e) { System.out.println("An ordinary exception was caught"); } catch (RuntimeException e) { // this will fail to compile System.out.println("A runtime exception was caught"); } } }
Of course this is not possible and has not been possible ever
in Java. The javac
compiler will not compile a
class with such obvious dead code.
A fairly good chapter with just one little error. Waldo says that if an "interface has only package visibility, then the methods in that interface will also have only package visibility". Interface methods are always public and here is some code to prove it:
interface PackageInterface { void foo(); public void bar(); } public interface PublicInterface { void foo(); public void bar(); } import java.lang.reflect.*; public class InterfaceTest { public static void main(String[] args) { showMethods(PackageInterface.class); showMethods(PublicInterface.class); } private static void showMethods(Class<?> clazz) { System.out.println("Methods for " + clazz.getName()); for (Method m: clazz.getMethods()) { String modifier = Modifier.isPublic(m.getModifiers()) ? "public" : "package"; System.out.printf("%s %s%n", modifier, m.getName()); } } }
The output shows that methods from interfaces are always public, irregardless of what the accessibility of the interface is:
Methods for PackageInterface public foo public bar Methods for PublicInterface public foo public bar
Waldo makes some excellent points about how useful garbage collections is. He also points out that it is bad to write finalize() methods, but misses an important point about concurrency. Since the finalize() method is called from a separate thread, we have to make our class completely thread safe.
He also makes a very common mistake on page 56, which I have heard many times before and may also have repeated myself. His argument is that if we let a reference to an object escape from the finalize() method, that the object will never be garbage collected. It is easy to write code that seems to prove that point, but it is not so. An object whose reference escapes during finalize() does not get finalized again, but it certainly gets collected eventually.
The code to prove this is a bit tricky. First off, we create a simple LRU Cache, using the LinkedHashMap. By specifying "true" in the constructor, we sort the elements by access order instead of insertion order. We also limit the size to hold the last 1000 items by overriding the removeEldestEntry() method.
import java.util.*; public class ResurrectingTheDead { private static final Map<Zombie, Zombie> lruCache = new LinkedHashMap<Zombie, Zombie>(1000, 0.75f, true) { protected boolean removeEldestEntry( Map.Entry<Zombie, Zombie> eldest) { return size() > 1000; } }; public static void main(String[] args) throws Exception { while (true) { // We create a bunch of zombies, which will take at least // 24 bytes each on a 64 bit machine for (int i = 0; i < 100 * 1000; i++) { new Zombie(); } System.out.printf("UsedMem before gc: %dmb%n", memoryUsedInMB()); // We make sure that the objects are all collected for (int i = 0; i < 10; i++) { System.gc(); Thread.sleep(10); } // This should be 0 or close to that System.out.printf("UsedMem after gc: %dmb%n%n", memoryUsedInMB()); } } private static long memoryUsedInMB() { return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024; } private static class Zombie { // Since finalized is being accessed from multiple threads, // we need to make it volatile private volatile boolean finalized = false; protected void finalize() throws Throwable { if (finalized) System.err.println("Finalized twice"); finalized = true; lruCache.put(this, this); // we let 'this' reference escape super.finalize(); } } }
You might need to adjust the values to show similar results on your machine. I have a fairly new MacBook Pro:
UsedMem before gc: 10mb UsedMem after gc: 6mb UsedMem before gc: 16mb UsedMem after gc: 1mb UsedMem before gc: 9mb UsedMem after gc: 0mb UsedMem before gc: 9mb UsedMem after gc: 0mb UsedMem before gc: 9mb UsedMem after gc: 0mb
However, if we remove the explicit calls to System.gc(), then the finalize thread lags behind too far and cannot clean up as fast as we create new objects. It is likely that you would then see an OutOfMemoryError:
UsedMem before gc: 10mb UsedMem before gc: 17mb UsedMem before gc: 26mb ... UsedMem before gc: 111mb UsedMem before gc: 119mb Exception in thread "main" OutOfMemoryError: Java heap space at java.lang.ref.Finalizer.register(Finalizer.java:72) at java.lang.Object.<init>(Object.java:20)
I have always been a fan of Javadoc and Waldo did not disappoint me. In fact, I learned something new :-) When we implement an interface, the javadocs are automatically copied over into our class if we leave them out. But even better is to use the {@inheritDoc} tag, because then we can override some of the element documentations and add implementation details. We agree on most things in this chapter.
Some of the examples given in this chapter are terrible. For example, the FormatBattingAvg() method on page 113 demonstrates exactly why I was not happy with throwing unnecessary exceptions.
Waldo claims that the UnsupportedOperationException was introduced specifically for the collection classes. After a bit of research into Java 1.1 and 1.2 source code, plus looking at the javadoc for UnsupportedOperationException, it seems that Waldo is correct. I have learned something new through his book.
Of course concurrency is one of the good parts of Java,
except when we make serious mistakes. In this chapter, Waldo
forgets to add volatile to a shared boolean in his
RosterRetriever class, which means that isDone() might never
return true. He also should have rather used the
ExecutorService
, which comes out-of-the-box with
a Future
that we can do to retrieve the result
later on.
This seems to indicate that Waldo's detailed knowledge of Java concurrency is slightly dated, especially as he recommends Doug Lea's old book [ISBN 0201310090] , instead of the much better Java Concurrency in Practice by Brian Goetz. Lea's book is good, but expensive and almost 11 years old. Goetz's book is the definitive guide on the subject of Java concurrency and I do not think we will see another threading book for Java for a long time.
At under $20 on Amazon [ISBN 0596803737] , I can recommend you get it for a nice summer read. The book is thin enough to read in a few sittings. The writing style is funny and has kept me reading it from cover to cover. There are mistakes, but the only reason I found them is because I bothered to read the book. My book shelf is filled with books that have never been opened.
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.