Running on Java 24-ea+25-3155 (Preview)
Home of The JavaSpecialists' Newsletter

185Book Review: Java: The Good Parts

Author: Dr. Heinz M. KabutzDate: 2010-07-11Java Version: 6Category: Book Review
 

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.

Book Review: 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 [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]

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 [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.

Should You Buy Java: The Good Parts?

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

 

Comments

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)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '24 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...