There are a number of great quotes in the book which I will keep handy when I need to find a good code quote. Especially great were quotes to use with junior engineers.
The book covers a lot of history about decisions made in the Java language in the beginning, and how those decisions have evolved over time. The historical design decisions are still relevant today, and in some cases make the evolution of the platform more difficult. The idea that we must be able to run 1.0 Java code is rooted deeply in the language. The decision has caused a lot of legacy designs and constructs to remain in the code. Arguably, good, or bad depending on implementer, or implementation.
I was very happy to have been given the opportunity to read the book, and would give it 4/5 stars. It is a guide for senior developers to help mentor junior developers, and provides junior developers with answers to questions about why something was done a certain way in Java.
Details
The book is divided into eleven chapters: Introduction, Type System, Exceptions, Packages, Garbage Collection, JVM, Javadoc, Collections, RMI and Object Serialization, Concurrency, and Developer Ecology. These are in the author's opinion some of "good" parts of Java.The introduction is more of a list of credentials for the author, and why we should consider him a subject matter expert. Jim certainly has the credentials to cover the topic being part of the early team on Java at Sun.
Chapter Two - Type System
The Type System is our first introduction to why the author feels this is a good part. The explanation has some historical perspective and is backed up with code. The real gem for me occurs under the topic A Real Problem where Jim has an interesting and insightful set of remarks. Jim explains a change in the class loading mechanism that was a response to a potential security issue which was discovered early on in Applet security. The issue was sharing of static variables across applets in a shared VM space. The fix was to ensure each applet was loaded by its own class loader. Also the run time type of an object would now be determined by a combination of its compile time type, and the class loader.This now means that there is a difference between the compile time type of an Object and its run time type. It usually does not matter if you are not loading objects over the network, or not using multiple class loaders. If you do so, there can be consequences which should be considered. This is a sage piece of advice.
As noted, you will not notice most of the time if the Object is loaded by a common class loader. If they are loaded by different class loaders you will get a message telling you that the declared type is not the same as the run time type and are incompatible even though the source indicates they are the same.
Chapter Three - Exceptions
This chapter covers exceptions and starts with a great quote:Those who object to the exception mechanism either don't understand it, don't realize what it can do, or are simply lazy twits who don't care about writing reliable code.This is a great quote. Sometimes you feel like it was a bad idea, but it forces you think about what your code could do. We think of code with rose tinted glasses, and this allows us to get some clarity.
There is another quote on page 34 about some code which is not abstracted, and checks for exceptions after every operation.
This is the mistake of a puppy programmer. When you encounter such code, you should correct the writer, perhaps by rolling up a newspaper and swatting the offender.That is one to keep in your hip pocket for use later.
On page 36, Jim points out a common form of exception handling which I have seen far too often and should result in the aforementioned swatting with a paper.
} catch (Exception ignored) { // Catch and swallow. }
I have seen too much of this, but this pattern is common in a try, catch, finally where you are closing a resource. This changes with Java 7 and try with resources.
The closing part of the chapter refers to the Dark Side. The discussion is focused on the evil of
RuntimeException
and why developers should avoid it. If you have been doing any development over time you will come across code you have written and is now deployed which has an exception condition you did not anticipate. Usually it occurs where we may have been a little lax on unit testing, and had to meet a deadline. When it occurs, we will be tempted to make the exception a RuntimeException
so that we don't have to change our method signature. That is the temptation of the Dark Side. This is summed up in the following quote:... If you actually design your code to throw only subclasses of RuntimeException, then you have gone beyond simple evil. You have now become a corrupter of others, and should feel the appropriate shame and be subject to the appropriate ridicule.Enough said.
Chapter Four - Packages
I started laughing and remembering my early days as a Java programmer. Page 41 has a quote on using the unnamed package (default).Following that witty comment is one of the best explanations on why you should not use wildcard imports. I will quote the author once again since the explanation is succinct.
The unnamed package is a form of namespace limbo where code written by confused, obstinate, or lazy programmers is placed until they evolve to a higher life form.
The more general form of the import statement is quite popular, but should be avoided if you can. By importing more than you need, you are polluting the namespace of your own code, and make it more likely that you will clash with some name that is defined in the other package.
Later there is an important discussion on examining the quantity of imports in a class. The more classes that are imported from another namespace may indicate an interconnection between the class, and the other namespace. This may indicate a design flaw, increased complexity, or lack of abstraction.
Chapter Five - Garbage Collection
There is a great explanation on the use of references in Java as opposed to pointers in C/C++. The primary reason we use references is because of garbage collection. The explanation is really well done, and gives you insight really why this is such a good part of Java. I actually found this chapter very interesting, and the historical perspective on what decisions were made and how it works today make the chapter a must read.Later there is a discussion on finalizers and why you should avoid them as a programmer. The basic point is that the
finalize()
method may never be called. If you rely on the finalizer to cleanup your code, you may leave your data in an inconsistent state.Chapter Six - Java Virtual Machine
The JVM is probably the most important part of Java. Its design and flexibility have allowed us to use it for more than just Java. Today the JVM allows us to run languages like Ruby (JRuby), and Scala on the same VM as Java. It allows us to combine these languages in a new "ployglot" programming style. The chapter is a discussion of the great flexibility of the JVM.Chapter Seven - Javadoc
At first I was not sure why the author felt this was one of the good parts of Java. After reading his discussion on the subject I have become convinced he is correct.I think that the
{@inheritDoc}
tag requires special mention. This is particularly important if you have a class which is implementing an interface, or extending a class. If the documentation is complete on the interface, or parent class, this allows you to inherit the documentation and allows the developer to be more productive without having to copy the previous comments to their implementation. It also allows you to add additional comments to the existing comments.The author makes a very good recommendation: write the documentation for your interfaces first. The implementation classes simply need to inherit them.
Finally, the author concludes that Javadoc comment review is just as important as code review. I agree generally with the author, but I have found that the code changes over time, and often the Javadocs do not. So I take a position to not believe the Javadocs per se, and choose to use the force and read the source.
Chapter Eight - Collections
If there is any reason to buy this book, this is it. The explanation of the Collections API is very complete, and the historical reasons for how it was constructed, and the evolution over time is a walk down memory lane. The discussion about parametrized types, and the evolution of generics in Collections is very interesting, and informative. I have a new found appreciation of the difficulty, and the decisions made on how to incorporate them.Additional information can be found in Java Generics and Collections by Maurice Naftalin and Philip Wadler (O'Reilly & Associates).
Chapter Nine - Remote Method Invocation and Object Serialization
Here is where I depart with the author. Jim was involved with the development of RMI. His love for the technology is apparent. Without a doubt, it is a valuable technology, but it is often dealt with on a higher level of abstraction today. Most Java developers could not write an RMI application without taking a trip to Google.If you are looking at RMI, the author recommends using a security manager. Here is his quote on why:
Because RMI makes use of the ability of the JVM to dynamically load code, and because you don't want to load code without the protection of a security manager, programs using RMI should always be run with a security manager.
Object serialization is an important part of Java development today. This is particularly true of applications which are running on an application container. I was surprised to discover something about serialization I had not considered before. When serializing an object, it makes an object graph of all the classes that are contained in the object so that when it is de-serialized, it matches state of the application as completely as possible. There are two key items to remember.
- If two
Object
s serialized have a reference to a third commonObject
, when the objects are de-serialized. There will be twoObject
s created which represent the common third object. This could result in strange errors. - All static variables are reset to the default values on the de-serialized object. Static variables are considered
transient
by serialization.
Serializable
. This means you must keep the fact that static variables can not be assumed to be consistent since they are transient.Chapter Ten - Concurrency
This is a boon to Java and an Achilles Heel. The author talks about a simple implementation, but refers you to other resources for more details on the subject.The best book which should be on all developer's bookshelves is: Java Concurrency in Practice by Brian Goetz (Addison-Wesley). It is a must have for anyone doing concurrent programming. Also consider Java Threads by Scott Oaks and Henry Wong. A good book too on the subject.
One interesting note is mentioning the
Timer
object to schedule Thread
processing. I would go further and recommend that the reader look at the Java Concurrency API instead. If you need anything more sophisticated than basic scheduling, it is the only logical choice.Atomic data types are mentioned along with an example. This is something that most programmers, I believe, forget about. These are thread-safe data types which should always be considered in concurrent programming. Thanks for the reminder, simple explanation, and example.
No comments:
Post a Comment