|
The Java Specialists' Newsletter
Issue 185 2010-07-11
Category:
Book Review
Java version: Java 6 Book Review: Java: The Good Partsby Dr. Heinz M. KabutzAbstract: 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.
Our Java Specialist
Club is now well under way, with close to 200
members. I keep on getting emails from excited members who
are thrilled with the amount of value they are getting for
what they now see as a small monthly investment. Here
is one I received this morning: "These learning webinars
are very good. Really. There are many subtleties in learning
and applying the patterns... These webinars are very
clarifying." Members have access to all the previous webinars as
recordings and can discuss interesting Java questions on our
forum. Furthermore, on completion of the
Design Patterns Webinars, they will be awarded with a
certificate from JavaSpecialists.EU.
Talking of the Java Specialist Club, we are redoing the
webinar Working with Terrible Code at a time that is more
convenient to the Americas, on Thursday the 15th July at 7pm
GMT. It is a free webinar for all readers of The Java
Specialists' Newsletter. Please register
here: https://www1.gotomeeting.com/register/319482160.
Looking forward to seeing lots of you there :-)
Would you like to really understand Java concurrency? Join us for an
in-depth study of how threading works in Java. During the course,
you will learn how to write correct and fast multi-threaded Java code.
Please
click here if you would like to learn more. Java: The Good Parts
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 , 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.
The Type System
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.
Exceptions
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.
Packages
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
Garbage Collection
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)
Javadoc
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.
Collections
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.
Concurrency
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 ,
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.
Should You Buy Java: The Good Parts?
At under $20 on Amazon , 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
P.S. Please register for our free webinar on how to work
with terrible code: https://www1.gotomeeting.com/register/319482160
Book Review Articles
Related Java Course
Discuss at The Java Specialist Club
|