Book Review: The Well-Grounded Java Developer
Java-related books are perhaps the most widely available books in terms of diverse selections of any programming language. Even so, most Java books seem to fall into one of two categories: they are either introductory/general level books or they are highly focused specific books. These books all have their place and many of them have been beneficial to me in my career, but I'm always on the look out for that rare combination of the two: the book that is relatively general in terms of Java coverage, but is more advanced or looks at the topic quite differently than an introductory or typical general Java book. Books such as Effective Java and Java: The Good Parts come to mind as books that strive to hit this sweet spot. In this book review, I look at soon-to-be-released (in print, with electronic version available already) The Well-Grounded Java Developer, a book whose topic selection has interested me since first hearing about it in early 2011.

The initial paragraph of the "About This Book" section summarizes the authors' intent:
Welcome to The Well-Grounded Java Developer. This book is aimed at turning you into a Java developer for the modern age, reigniting your passion for both the language and platform. Along the way, you’ll discover new Java 7 features, ensure that you’re familiar with essential modern software techniques (such as dependency injection, testdriven development, and continuous integration), and start to explore the brave new world of non-Java languages on the JVM.
The authors of The Well-Grounded Java Developer, Ben Evans and Martijn Verburg, are instrumental players in the London Java Community, a member of the Java SE/EE Executive Committee in the Java Community Process. Both also speak at Java-related conferences with Martijn being well-known as theDiabolical Developer and Ben Evans as "the voice of reason?"
The title of the book and the selection of topics are what appealed to me most about the advertised content of The Well-Grounded Java Developer when I first saw its Table of Contents in the early MEAP edition. The chosen topics seem appropriate for experienced Java developers who are too busy developing solutions for their customers to spend time trying out features of Java 7, delving deeply into the intricacies of Java and the JVM, or playing with the major alternative JVM languages. I have worked with many solid Java developers who are great at Java, but don't have the time or inclination to play with the latest versions of Java or with JVM languages other than Java until or unless they need to for their job role. This book is a perfect quick introduction to these topics for such developers. A single book provides the highlights of Java SE 7 along with introduction material related to the three most popular non-Java JVM languages. It also throws in in-depth coverage of some advanced topics for making good Java developers better. In addition, the book includes information on modern Java development tools.
The Well-Grounded Java Developer is divided into four multi-chapter parts along with five appendices. Part 1 ("Developing with Java 7") introduces new features of Java SE 7 with focus on Project Coin(Chapter 1) and NIO.2 (Chapter 2).
Part 1 : Java 7
Chapter 1 ("Introducing Java 7") briefly discusses Java the language and Java the platform along with other general characteristics of the Java programming language (open source now, compiled or interpreted nature, etc.). The remainder of the first chapter covers various Project Coin featuresincluding switching on String, enhanced numeric literal syntax, improved exception handling, try-with-resources (automatic resource management), diamond operator syntax, and simplified variable arguments.
Chapter 2 ("New I/O") provides brief overview and history of NIO in Java before delving into details of Java 7's NIO.2. The chapter introduces NIO.2 via the all-important Path interface and looks at how this new interface enables improved file input/output (with Files class and other handy new files handling mechanisms).
Part 2 : Vital Techniques
Part 2 ("Vital Techniques") of The Well-Grounded Java Developer continues covering new Java 7 features, but the focus is shifted from the first part. In the first part, the focus was on the features new to Java 7. In the second part, other features new to Java 7 are covered, but in this part they are covered as a means to a different end than simply introducing new Java 7 features. The intent of this second part is to provide more in-depth coverage of "vital techniques" that Java developers should know. As the introduction to this part states, it is about "exploring vital programming knowledge and techniques with Java." These "vital techniques" are dependency injection (Chapter 3), concurrency (Chapter 4), class files and bytecode (Chapter 5), and performance tuning (Chapter 6).
Although there are great books devoted to some of these topics covered in Part 2 (Java Concurrency in Practice and Java Performance for example), many Java developers don't have the time, interest, or inclination to purchase and read these books. These chapters help to get a Java developer going with these ideas and more comprehensive books can be referenced when more comfortable with the subject or when more details are needed.
Chapter 3 ("Dependency Injection") provides an introduction to dependency injection (DI) andcontrasts it with the encompassing concept of inversion of control (IoC). The chapter introduces JSR 330 ("Dependency Injection for Java") and discusses using Google Guice as an implementation of JSR 330.
The introduction to Chapter 4 ("Modern Concurrency") includes this description of the intent of the chapter:
The aim of this chapter is to make you aware of the underlying platform mechanisms that explain why Java’s concurrency works the way it does. We’ll also cover enough general concurrency theory to give you the vocabulary to understand the issues involved, and to teach you about both the necessity and the difficulty involved in getting concurrency right.
Chapter 5 ("Class Files and Bytecode") is one of the book's chapters that I think can be of most value to the highest percentage of intermediate to advanced Java developers. Classloading is one of the most frustrating issues in Java development and the fifth chapter attacks that topic head-on. A feature new to Java 7, method handles, are then covered. Method handles are described in this chapter as the "new Java 7 way to achieve the same ends [as the Java Reflection API], with much cleaner code."
When I reviewed the book Java Reflection in Action, I noted that one of its few weaknesses was its age (2004/JDK 1.4) and I concluded that "the most glaring omission from a book on Java reflection is Java 7's introduction of method handles, but few Java books in print today cover these." The Well-Grounded Java Developer is a welcome exception to this statement with an entire section (5.2) devoted to the topic.
I've been a big fan of the javap tool ("Java Class File Disassembler") that comes with the HotSpot SDK, but one of the things that has made it more difficult to learn to use that tool effectively is lack of easily accessible documentation on it. Section 5.3 ("Examining Class Files") of the fifth chapter goes a long way toward remedying that deficiency. Besides introducing
javap
, the ambitious 5.3 section also covers reading bytecode. This chapter is dense with detail, but this is welcome detail and a great introduction to the subject.
The final section of Chapter 5 (Section 5.5 on "invokedynamic") introduces Java 7's invokedynamic. The introduction to the section notes where this subject likely likes in terms of interest to general Java developers:
[invokedynamic] isn’t a feature that will necessarily be used directly by every working developer, because it involves a very advanced use case. This feature is for frameworks developers and non-Java languages at present.
Chapter 6 ("Understanding Performance Tuning") introduces Java and JVM performance tuning by reminding the reader that performance tuning is often not necessary and depends on measurement with realistic loads. The chapter provides definitions of performance-related terminology and introduces "a pragmatic approach to performance analysis." This sixth chapter also discusses the garbage collection in more details than most Java developers are probably aware of and introduces some of the Oracle (HotSpot) JVM options that can be used.
Chapter 6 also introduces another Oracle JVM command-line tool (jmap) ["Memory Map"] and provides descriptive text on how to read jmap's output (similar to approach used with javap coverage in Chapter 5).
Part 3 : Alternative JVM Languages
Part 3 ("Polyglot Programming on the JVM") moves beyond the Java programming language and focuses on alternative languages running on the JVM. The authors point out that learning about these alternative JVM languages can be useful to Java developers because "many of the new ideas that will be needed in the future are present in other JVM languages today." I'll also add that, at least in the case of Groovy, I've found it useful already for things where Java is not as strong (scripting).
Chapter 7 ("Alternative JVM Languages") introduces this third part of the book focused on alternative JVM languages. This chapter covers why one would be interested in an alternative JVM language before delving into how alternative JVM languages can be used to support functional programming concepts on the JVM. The chapter outlines different ways to classify non-Java JVM languages such as interpreted versus compiled, statically typed versus dynamically typed, and imperative versus functional. The chapter also contrasts completely new languages designed for the JVM (Groovy, Scala,Clojure) with "reimplementation" languages (pre-existing languages ported to the JVM such as Jython,JRuby, and Rhino).
Chapter 7 provides information on how to decide which alternative JVM language to choose for different situations. The authors quickly introduce the non-Java JVM languages that each get a chapter's focus (Groovy, Scala, and Clojure) and explain why they selected these languages upon which to focus.
We’ve picked three languages that we see having the greatest potential longevity and influence. These are the languages on the JVM (Groovy, Scala, and Clojure) that already have well-established mind share among polyglot programmers.
After Chapter 7 kicks off Part 3 on polygot programming on the JVM, the next three chapters of Part 3 each focus on a specific alternative JVM language: Chapter 8 ("Groovy: Java’s dynamic friend"), Chapter 9 ("Scala: powerful and concise"), and Chapter 10 ("Clojure: safer programming"). Each of these three chapters provides an overview of the respective alternative JVM languages. Of course, a single chapter is not sufficient to comprehensively introduce a programming language, so these chapters focus on the strengths of each covered language. They also cover things such as Java interoperability (for Groovy and Clojure) and use contrasting of the languages with Java to illustrate language features.
Part 4 : Applying Alternative Language and New Java Techniques and Tools
Part 4 ("Crafting the Polyglot Project") applies information covered earlier in the book to "some of the most common and important techniques in modern software development." Three of the four chapters in this part look at modern software development techniques (test-driven development, building/continuous integration, and rapid web development). The concluding chapter of the fourth part looks at what the future holds for Java.
Chapter 11 ("Test-Driven Development") introduces TDD, JUnit, Mockito, and ScalaTest. This chapter also looks at different ways to implement "test doubles" (dummy, stub, fake, mock). Chapter 12 ("Build and Continuous Integration") introduces tools Maven 3, Jenkins, FindBugs, Checkstyle, and Leiningen(for Clojure projects).
Chapter 13 ("Rapid web development") covers issues with developing web applications using Java. It looks at several different popular Java web frameworks before focusing specifically on Grails (Groovy) and Compojure (Clojure).
Chapter 14 ("Staying Well-Grounded") begins with the sentence, "To stay ahead of the curve, the well-grounded Java developer should always be aware of what’s coming around the corner." This chapter then looks at the authors' perspective on the future of Java. The authors cover Java 8 with specific focus on the two most significant features of that version: lambda expressions and Jigsaw modularity. The chapter also looks at JVM changes making polygot programming easier and more productive and at improved concurrency support. It ends by looking at other JVM trends.
Appendices : Background / Supporting Information
Besides the four parts with 14 chapters, The Well-Grounded Java Developer also includes five appendices: Appendix A ("Java7developer - source code installation"), Appendix B ("Glob pattern syntax and examples"), Appendix C ("Installing alternative JVM languages"), Appendix D ("Downloading and installing Jenkins"), and Appendix E ("Java7developer - the Maven POM"). These appendices provide example code related to the book as well as other useful reference information related to topics covered in the book.
The Book's Audience
The Well-Grounded Java Developer hits its goal of being aimed at and primarily beneficial for the Java developer who wants to be a "well-grounded Java developer." Newer Java developers can learn much about basic tools, techniques, and approaches in Java. Moderately experienced Java developers are likely to pick up new tools and techniques or to gain a better and deeper understanding of principles about the Java language and about the JVM.
Most experienced Java developers will likely find much to learn from this book, especially if they are too busy developing applications to spend the time trying out three alternative JVM languages along with the new features of Java in Java 7 and Java 8. I know several Java developers who I consider very experienced and even they would benefit from some of the low-level details this book provides. For example, the sections on
javap
and jmap
are as good as introductions and concise reference as I've seen for those respective Oracle JVM tools.
Although I think nearly all Java developers can learn new things, new approaches, and new techniques from The Well-Grounded Java Developer, I think the book is particularly well-suited for the Java developer who feels comfortable with Java, but has not had a lot of time in recent years to come up to speed on the popular alternative JVM languages or the features of Java 7 and 8 (closures and modularity). This book can provide a quick "tune-up" to bring these experienced Java developers up to speed on the latest happenings.
Disclaimer
It is trendy currently to talk about how "transparent" one is. In an effort to fit in with this trend, I must acknowledge here factors that might be misconstrued as affecting my judgment. First, the authors have kindly included a reference to my blog post on Groovy JSON support in The Well-Grounded Java Developer. This is a book that I'm very happy to have my name and my blog associated with and that may prove my opinion of this book more than anything else.
A second potential conflict of interest that one might misconstrue to affect my judgment is authorMartijn Verburg's (@karianna) tweet-out of my Twitter handle (@DustinMarx). That single tweet increased the number of people "following" me on Twitter by several times my previous number. However, I had planned to write this review and had the general gist of it in mind well before this Tweet.
Manning Early Access Program (MEAP)
I enjoyed having access to portions of The Well-Grounded Java Developer earlier than its formal publication via the Manning Early Access Program. This is an especially valuable program when the forthcoming book is on subjects that are very new (such as Java 7).
The Downsides
The Well-Grounded Java Developer's primary downsides are related to the fact that it cannot be everything to everyone (nothing can!). The book is great at helping Java developers kick-start their efforts at becoming well-grounded. However, as the authors acknowledge multiple times in the book, many of the subjects will require additional reading of other sources for more details on subjects introduced in this book. Also, the book cannot possibly cover every "new" thing coming to the world of Java. For example, JavaFX is not mentioned in the book, though the current Java.net poll may end up indicating that JavaFX may not have yet earned enough community-wide respect and acceptance to warrant such a mention.
Conclusion
I was excited about the prospect of The Well-Grounded Java Developer when I initially read about it and it has delivered to a level matching even those high expectations. This is that rare book I'm always on the lookout for that stretches me as a general Java developer. Many highly-focused and specific Java-related books do this for me in one area of Java, but this book helped me to learn and realize things in several different aspects of Java and the JVM.
I have used Java for many years and was a fairly early adopter of Java 7. I have also been a huge fan of Groovy for a while now and I read and write blogs frequently, but I still found numerous details and insights in this book that I had not thought about previously. I work with many great Java developers who spend far less time reading blogs, working with alternative JVM languages, or trying out new features of Java and I'd expect them to gain even more useful insights from reading The Well-Grounded Java Developer. As Dr. Heinz Kabutz wrote in the book's forward, "Studying this book will help you become a well-grounded Java developer. Not only that, it will give you tips on how to stay well-grounded." This book comes from a great idea and is an example of where a delivered product meets the lofty expectations of the original idea.
Java 7's Support for Suppressed Exceptions
A new constructor and two new methods were added to the Throwable class (parent of Exception andError classes) in JDK 7. The new constructor and two new methods were added to support "suppressed exceptions" (not to be confused with the bad practice of swallowing or ignoring exceptions). In this post, I look at why these methods were introduced and how they are used in JDK 7. I throw in a short discussion on how suppressed exceptions are different than chained exceptions.
Suppressed exceptions play a significant role in the execution of the new-to-Java 7 try-with-resources statement (also known as Automatic Resource Management [ARM]). Providing API support for this new resource management capability appears to have been the primary driver for the new constructor and methods on the
Throwable
class that provide access to suppressed exceptions, but the APIs support suppressed exceptions being used outside of the try-with-resources statement.
Perhaps the most common use case for encountering suppressed exceptions is when a try-with-resources statement (which is the one type of
try
that does not require a catch
or finally
clause according to Java Language Specification Java SE 7 Edition Section 14.20) encounters an exception within the try
block and then encounters another exception in implicitly trying to close the related resource.
To illustrate this case, I make up my own "resource" that can be used in a try-with-resource statement because it implements the java.lang.AutoCloseable interface. This "naughty resource" intentionally throws an exception when the code using it attempts to use it and then continues its bad form by throwing another exception when the overridden close method (the only one prescribed by theAutoCloseable interface) is called. The code listing for
NaughtyResource.javaNaughtyResource
is shown next.- package dustin.examples;
- /**
- * Resource that throws exceptions both in its use and its closure and is only
- * intended for use in demonstrating Java 7's suppressed exceptions APIs. This
- * is not a well-behaved class.
- *
- * @author Dustin
- */
- public class NaughtyResource implements AutoCloseable
- {
- /**
- * Method that intentionally throws an exception.
- *
- * @throws RuntimeException Thrown no matter how you call me.
- */
- public void doNothingGood()
- {
- throw new RuntimeException("Nothing good can come of this.");
- }
- /**
- * The overridden closure method from AutoCloseable interface.
- *
- * @throws Exception Exception that might be thrown during closure of this
- * resource.
- */
- @Override
- public void close() throws Exception
- {
- throw new UnsupportedOperationException("Not supported yet.");
- }
- }
With a naughty resource now available, it's time to use the naughty resource and demonstrate the suppressed exceptions API. The next image depicts what happens if one tries to use this resource without catching the Exception thrown implicitly by the close method and without declaring the method as throwing it.
This is an error message provided by javac as shown in the next screen snapshot.
I have shown the two previous screen snapshots to emphasize the implicit call to
close
that is performed on the resource. The error message shown in the NetBeans editor and in the console clearly states this is the case.
The next code listing contains the first version of the
SuppressedExceptions.java (Version 1)SuppressedExceptions
class that compiles.- package dustin.examples;
- /**
- * Demonstrate JDK 7's Suppressed Exceptions API support.
- *
- * @author Dustin
- */
- public class SuppressedExceptions
- {
- /**
- * Executable function demonstrating suppressed exceptions.
- *
- * @param arguments The command line arguments; none expected.
- */
- public static void main(String[] arguments) throws Exception
- {
- try (NaughtyResource naughty = new NaughtyResource())
- {
- naughty.doNothingGood();
- }
- }
- }
Although two exceptions are really encountered when the above code is executed (one from within the
try
block on the call to NaughtyResource.doNothingGood()
and one on the implicitly called close
method), only one percolates to the top and is shown when the application is run. This is demonstrated in the next screen snapshot.
As the last image proves, only the exception encountered in try
SuppressedExceptions.java (Version 2)try
block of the try-with-resources statement is shown. This is where the ability to ask a Throwable
(in this case an Exception
) about its suppressed exceptions comes in handy. To demonstrate this, version 2 of theSuppressedExceptions
class (which uses Throwable.getSuppressed()) is shown next.- package dustin.examples;
- import static java.lang.System.err;
- /**
- * Demonstrate JDK 7's Suppressed Exceptions API support.
- *
- * @author Dustin
- */
- public class SuppressedExceptions
- {
- /**
- * Method that uses NaughtyResource with try-with-resource statement.
- *
- * @throws Exception Expected exception for try-with-resource used on the
- * NaughtyResource.
- */
- public static void performTryWithResource() throws Exception
- {
- try (NaughtyResource naughty = new NaughtyResource())
- {
- naughty.doNothingGood();
- }
- }
- /**
- * Executable function demonstrating suppressed exceptions.
- *
- * @param arguments The command line arguments; none expected.
- */
- public static void main(String[] arguments)
- {
- try
- {
- performTryWithResource();
- }
- catch (Exception ex)
- {
- err.println("Exception encountered: " + ex.toString());
- final Throwable[] suppressedExceptions = ex.getSuppressed();
- final int numSuppressed = suppressedExceptions.length;
- if (numSuppressed > 0)
- {
- err.println("\tThere are " + numSuppressed + " suppressed exceptions:");
- for (final Throwable exception : suppressedExceptions)
- {
- err.println("\t\t" + exception.toString());
- }
- }
- }
- }
- }
The above application will print out any suppressed exceptions. In this case, the exception encountered upon attempted closure of
NaughtyResource
is now shown as one of the suppressed exceptions. This is demonstrated in the next screen snapshot.
As documented in the Java Tutorial, we see that the only thrown exception is the exception encountered within the
try
block of the try-with-resources statement and the second exception encountered during resource closure is "suppressed" (but still associated and available from the actually thrown exception).
One doesn't need to use try-with-resources to work with suppressed exceptions. The next code listing adapts
SuppressedExceptions.java (Version 3)SuppressedExceptions
to use a more traditional try
-finally
.- package dustin.examples;
- import static java.lang.System.err;
- /**
- * Demonstrate JDK 7's Suppressed Exceptions API support.
- *
- * @author Dustin
- */
- public class SuppressedExceptions
- {
- /**
- * Method that uses NaughtyResource with JDK 7 try-with-resource statement.
- *
- * @throws Exception Expected exception for try-with-resource used on the
- * NaughtyResource.
- */
- public static void performTryWithResource() throws Exception
- {
- try (NaughtyResource naughty = new NaughtyResource())
- {
- naughty.doNothingGood();
- }
- }
- /**
- * Method that uses NaughtyResource with traditional try-finally statement.
- *
- * @throws Exception Exception thrown during use of NaughtyResource.
- */
- public static void performTryFinally() throws Exception
- {
- final NaughtyResource naughty = new NaughtyResource();
- try
- {
- naughty.doNothingGood();
- }
- finally
- {
- naughty.close();
- }
- }
- /**
- * Executable function demonstrating suppressed exceptions.
- *
- * @param arguments The command line arguments; none expected.
- */
- public static void main(String[] arguments)
- {
- try
- {
- // performTryWithResource();
- performTryFinally();
- }
- catch (Exception ex)
- {
- err.println("Exception encountered: " + ex.toString());
- final Throwable[] suppressedExceptions = ex.getSuppressed();
- final int numSuppressed = suppressedExceptions.length;
- if (numSuppressed > 0)
- {
- err.println("\tThere are " + numSuppressed + " suppressed exceptions:");
- for (final Throwable exception : suppressedExceptions)
- {
- err.println("\t\t" + exception.toString());
- }
- }
- else
- {
- err.println("\tNo Suppressed Exceptions.");
- }
- }
- }
- }
The above code calls a method that uses
try
-finally
and the behavior is different than that for the try-with-resources example as shown in the next screen snapshot.
The
try
-finally
shows the exception encountered on attempted resource closure rather than the exception encountered in use of the resource (opposite of try-with-resources shown above). An additional difference is that the other exception is not available even as a suppressed exception in thetry
-finally
case. This is an example of the well-advertised "lost exception" issue of a potentially "trivial" exception (closing the resource) hiding a potentially more significant exception (actually using the resource).
If I want to see both exceptions thrown during use and closure of
SuppressedExceptions.java (Version 4)NaughtyResource
in conjunction withtry
-finally
, I can make use of Throwable.addSuppressed(Throwable) as shown in the next code listing. In that listing, the try
and finally clauses are enhanced to capture the exception thrown during use of the NaughtyResource
and to add that caught exception to the actually-thrown exception as a suppressed exception.- package dustin.examples;
- import static java.lang.System.err;
- /**
- * Demonstrate JDK 7's Suppressed Exceptions API support.
- *
- * @author Dustin
- */
- public class SuppressedExceptions
- {
- /**
- * Method that uses NaughtyResource with JDK 7 try-with-resource statement.
- *
- * @throws Exception Expected exception for try-with-resource used on the
- * NaughtyResource.
- */
- public static void performTryWithResource() throws Exception
- {
- try (NaughtyResource naughty = new NaughtyResource())
- {
- naughty.doNothingGood();
- }
- }
- /**
- * Method that uses NaughtyResource with traditional try-finally statement.
- *
- * @throws Exception Exception thrown during use of NaughtyResource.
- */
- public static void performTryFinally() throws Exception
- {
- final NaughtyResource naughty = new NaughtyResource();
- Throwable throwable = null;
- try
- {
- naughty.doNothingGood();
- }
- catch (Exception usingEx)
- {
- throwable = usingEx;
- }
- finally
- {
- try
- {
- naughty.close();
- }
- catch (Exception closingEx)
- {
- if (throwable != null)
- {
- closingEx.addSuppressed(throwable);
- throw closingEx;
- }
- }
- }
- }
- /**
- * Executable function demonstrating suppressed exceptions.
- *
- * @param arguments The command line arguments; none expected.
- */
- public static void main(String[] arguments)
- {
- try
- {
- // performTryWithResource();
- performTryFinally();
- }
- catch (Exception ex)
- {
- err.println("Exception encountered: " + ex.toString());
- final Throwable[] suppressedExceptions = ex.getSuppressed();
- final int numSuppressed = suppressedExceptions.length;
- if (numSuppressed > 0)
- {
- err.println("\tThere are " + numSuppressed + " suppressed exceptions:");
- for (final Throwable exception : suppressedExceptions)
- {
- err.println("\t\t" + exception.toString());
- }
- }
- else
- {
- err.println("\tNo Suppressed Exceptions.");
- }
- }
- }
- }
The output of the modified class is shown next. Note that the exception resulting from using the resource itself is now associated with the thrown exception as a suppressed exception. Although not shown here,
Throwable
also now provides a constructor that allows specification via boolean
argument of whether suppressed exceptions are allowed (enabled) or not allowed (disallowed) for the newly instantiated Throwable
.
Another perspective from which to look at suppressed exceptions is that of full stack traces. The following three images are screen snapshots of full stack traces resulting from use of try-with-resources (explicitly shows suppresed exception), use of traditional
Suppressed Exceptions Versus Chained Exceptionstry
-finally
statement without explicitly adding suppressed exceptions (and so no suppressed exceptions in full stack trace), and use of traditional try
-finally
with suppressed exceptions explicitly added (and thus do appear in full stack trace). I've added a red line to each image to separate show where the full stack trace ends and have circled the explicit reference to the suppressed exception where applicable.
Suppressed exceptions are not the same as chained exceptions. Chained exceptions were introduced with JDK 1.4 and were intended to make it possible to easily track causal relationships between exceptions. Typically, chained exceptions resulted from associating a newly thrown exception with the exception that was caught and caused the throwing of a new exception. For example, an unchecked exception might be thrown that "wraps" a checked exception that was caught and they could be chained together.
Suppressed exceptions were introduced with JDK 7 and are less about causal relationships and more about representing possibly related but not necessarily causal multiple exceptional conditions in a single thrown exception. In my naughty resource example above, the naughty resource's exception upon its sole method being called was NOT the cause of it thrown an exception upon invocation of its
close
method. Because of this, it makes more sense for the two exceptions to be associated (via the suppressed exception mechanism) than to force one to appear to be the cause of the other in a chained relationship.
It may be easiest to quickly understand the difference between chained exceptions and suppressed exceptions by comparing
NetBeans Recommends try-with-resourceThrowable
's methods for accessing each type. For chained exceptions, one invokes a particular Exception
's (Throwable
's) method. This method returns a single instance of the causing Throwable
. That returned Throwable
can be asked for its cause and the process is repeated up the chain of causing Throwables
. The important observation is that each given Throwable
has a single causing Throwable
. This can be contrasted with the ability of a Throwable
to provide multiple suppressed Throwable
s (in an array) when its getSuppressed() method is called.
It's worth noting here that NetBeans warns about the use of
Conclusiontry
-finally
and suggests replacing it with try-with-resources as discussed in my post Seven NetBeans Hints for Modernizing Java Code and in the screen snapshot shown next.
I believe it's obvious why NetBeans recommends that the developer change a
try
-finally
for resource handling to a try-with-resources statement. It is often preferable to know first what operation on the resource caused an exception, but it is nice to be able to access the exception on closure as well if desired. If I had to choose, I'd typically be more interested in the resource's problem during execution because the closure problem might be a derivative of that. However, having both is even better. The traditional try
-finally
only lists the exception upon closure without additional effort to relay both. The try-with-resource statement is not only more concise; it's also more useful thanks to its built-in support for inclusion of suppressed exceptions.JDK 7 Additions to Currency Class
The java.util.Currency class has been available since JDK 1.4, but JDK 7 has provided some useful new methods at the class and instance level. I look at these new methods in this post.
The
Currency
class represents worldwide currencies as "identified by their ISO 4217 currency codes" available at the International Organization for Standardization site. Although one can override the represented currencies available in an instance of the JVM by placing an appropriate properties file in<JAVA_HOME>/lib/currency.properties
, most of us are likely to use the currencies provided out-of-the-box with the JVM. It is easy to find out what these available currencies are with the new-to-JDK 7 class-level (static) function getAvailableCurrencies().
I like it when the JDK provides access to runtime JVM-specific details. The addition of
groovy -e "Currency.availableCurrencies.each{println it}"
Currency.getAvailableCurrencies()
brings this support to Currency
similar to what is available for locales [Locale.getAvailableLocales()], time zones [TimeZone.getAvailableIDs()], characters sets[CharSet.availableCharsets()], and available JVM scripting engines[ScriptEngineManager.getEngineFactories()]. It is easy to invoke the static function with a single line Groovy script called directly with the -e option:
There are three new instance-level methods added to
Currency
in JDK 7. Two of them [getDisplayName() and getDisplayName(Locale)] provide a String-based name of the currency instance (one for default locale and one for a provided locale). The third new method [getNumericCode()] provides an int
representing the numeric code for that currency instance.
The next code listing is of a simple Java application that runs through all available currencies using the method added to JDK 7 and then prints the numeric code for each currency along with the two versions of the currency display name as provided by new JDK 7 methods.
Example Demonstrating JDK 7 Methods on Currency- package dustin.examples;
- import static java.lang.System.out;
- import java.util.Currency;
- import java.util.Locale;
- import java.util.Set;
- /**
- * Demonstrate methods added to java.util.Currency in JDK 7.
- *
- * @author Dustin
- */
- public class Main
- {
- /**
- * Main executable function for demonstrating Currency methods added in JDK 7.
- *
- * @param arguments The command line arguments; none expected.
- */
- public static void main(String[] arguments)
- {
- final Set<Currency> currencies = Currency.getAvailableCurrencies();
- for (final Currency currency : currencies)
- {
- out.println(
- "Currency: "
- + currency.getDisplayName() + "/"
- + currency.getDisplayName(Locale.ITALIAN) + "/"
- + currency.getNumericCode());
- }
- }
- }
When the above is executed on the JVM I'm using, the output appears as follows:
Currency: Venezuelan Bolívar/Bolívar venezuelano forte/937
Currency: Colombian Peso/Peso Colombiano/170
Currency: French Franc/Franco Francese/250
Currency: Kyrgystani Som/Som Kirghiso/417
Currency: Euro/Euro/978
Currency: Maltese Lira/Lira Maltese/470
Currency: Georgian Lari/Lari Georgiano/981
Currency: Afghan Afghani (1927-2002)/Afgani (1927-2002)/4
Currency: US Dollar (Next day)/Dollaro Statunitense (Next day)/997
Currency: Mozambican Metical/Metical del Mozambico/943
Currency: Dutch Guilder/Fiorino Olandese/528
Currency: Panamanian Balboa/Balboa di Panama/590
Currency: Qatari Rial/Rial del Qatar/634
Currency: Bahamian Dollar/Dollaro delle Bahamas/44
Currency: Djiboutian Franc/Franco Gibutiano/262
Currency: Serbian Dinar (2002-2006)/Serbian Dinar (2002-2006)/891
Currency: New Zealand Dollar/Dollaro Neozelandese/554
Currency: Syrian Pound/Sterlina Siriana/760
Currency: Malagasy Franc/Franco Malgascio/450
Currency: Venezuelan Bolívar (1871-2008)/Bolivar Venezuelano/862
Currency: Saudi Riyal/Ryal Saudita/682
Currency: Rwandan Franc/Franco Ruandese/646
Currency: Yemeni Rial/Rial dello Yemen/886
Currency: Barbadian Dollar/Dollaro di Barbados/52
Currency: Sierra Leonean Leone/Leone della Sierra Leone/694
Currency: Sri Lankan Rupee/Rupia di Sri Lanka/144
Currency: Chilean Peso/Peso Cileno/152
Currency: Bermudan Dollar/Dollaro delle Bermuda/60
Currency: Omani Rial/Rial Omanita/512
Currency: Myanma Kyat/Kyat di Myanmar/104
Currency: Gambian Dalasi/Dalasi del Gambia/270
Currency: Guatemalan Quetzal/Quetzal Guatemalteco/320
Currency: CFP Franc/Franco CFP/953
Currency: Turkmenistani Manat (1993-2009)/Manat Turkmeno/795
Currency: Romanian Leu/Romanian Leu/946
Currency: Singapore Dollar/Dollaro di Singapore/702
Currency: Jamaican Dollar/Dollaro Giamaicano/388
Currency: Bolivian Boliviano/Boliviano/68
Currency: Eritrean Nakfa/Nakfa Eritreo/232
Currency: Turkmenistani Manat/Turkmenistani Manat/934
Currency: Mongolian Tugrik/Tugrik Mongolo/496
Currency: Indonesian Rupiah/Rupia Indonesiana/360
Currency: ZWN/ZWN/942
Currency: Luxembourgian Franc/Franco del Lussemburgo/442
Currency: Russian Ruble (1991-1998)/Rublo della CSI/810
Currency: São Tomé and Príncipe Dobra/Dobra di Sao Tomé e Principe/678
Currency: Ukrainian Hryvnia/Hrivna Ucraina/980
Currency: Argentine Peso/Peso Argentino/32
Currency: Bahraini Dinar/Dinaro del Bahraini/48
Currency: Belize Dollar/Dollaro Belize/84
Currency: Russian Ruble/Rublo Russo/643
Currency: Laotian Kip/Kip Laotiano/418
Currency: Maldivian Rufiyaa/Rufiyaa delle Maldive/462
Currency: Libyan Dinar/Dinaro Libico/434
Currency: Sudanese Dinar (1992-2007)/Dinaro Sudanese/736
Currency: Danish Krone/Corona Danese/208
Currency: Zimbabwean Dollar (2008)/Zimbabwean Dollar (2008)/935
Currency: Spanish Peseta/Peseta Spagnola/724
Currency: Macanese Pataca/Pataca di Macao/446
Currency: Surinamese Guilder/Fiorino del Suriname/740
Currency: Samoan Tala/Tala della Samoa Occidentale/882
Currency: Swazi Lilangeni/Lilangeni dello Swaziland/748
Currency: Tanzanian Shilling/Scellino della Tanzania/834
Currency: Portuguese Escudo/Escudo Portoghese/620
Currency: XUA/XUA/965
Currency: Armenian Dram/Dram Armeno/51
Currency: Greek Drachma/Dracma Greca/300
Currency: Guinean Franc/Franco della Guinea/324
Currency: Nicaraguan Córdoba/Córdoba oro nicaraguense/558
Currency: Gibraltar Pound/Sterlina di Gibilterra/292
Currency: Bolivian Mvdol/Mvdol Boliviano/984
Currency: Bulgarian Lev/Nuovo Lev Bulgaro/975
Currency: Belgian Franc/Franco Belga/56
Currency: Kazakhstani Tenge/Tenge Kazaco/398
Currency: Solomon Islands Dollar/Dollaro delle Isole Solomon/90
Currency: Costa Rican Colón/Colón Costaricano/188
Currency: Swiss Franc/Franco Svizzero/756
Currency: Turkish Lira/Turkish Lira/949
Currency: Australian Dollar/Dollaro Australiano/36
Currency: CFA Franc BCEAO/Franco CFA BCEAO/952
Currency: Uzbekistan Som/Sum dell’Uzbekistan/860
Currency: European Unit of Account (XBD)/Unità di acconto europea (XBD)/958
Currency: Chinese Yuan/Renmimbi Cinese/156
Currency: Malawian Kwacha/Kwacha Malawiano/454
Currency: Bhutanese Ngultrum/Ngultrum Butanese/64
Currency: Zimbabwean Dollar (2009)/Zimbabwean Dollar (2009)/932
Currency: Special Drawing Rights/Diritti Speciali di Incasso/960
Currency: Italian Lira/Lira Italiana/380
Currency: Moroccan Dirham/Dirham Marocchino/504
Currency: Chilean Unit of Account (UF)/Unidades de Fomento Chilene/990
Currency: Lesotho Loti/Loti del Lesotho/426
Currency: Nigerian Naira/Naira Nigeriana/566
Currency: Guinea-Bissau Peso/Peso della Guinea-Bissau/624
Currency: Sudanese Pound/Sterlina sudanese/938
Currency: Lebanese Pound/Sterlina Libanese/422
Currency: Mauritian Rupee/Rupia Mauriziana/480
Currency: Testing Currency Code/Testing Currency Code/963
Currency: Latvian Lats/Lat Lettone/428
Currency: Japanese Yen/Yen Giapponese/392
Currency: Turkish Lira (1922-2005)/Lira Turca/792
Currency: Norwegian Krone/Corona Norvegese/578
Currency: Indian Rupee/Rupia Indiana/356
Currency: Hungarian Forint/Fiorino Ungherese/348
Currency: Tunisian Dinar/Dinaro Tunisino/788
Currency: Congolese Franc/Franco Congolese/976
Currency: Andorran Peseta/Peseta Andorrana/20
Currency: Palladium/Palladio/964
Currency: Liberian Dollar/Dollaro Liberiano/430
Currency: Netherlands Antillean Guilder/Fiorino delle Antille Olandesi/532
Currency: US Dollar (Same day)/Dollaro Statunitense (Same day)/998
Currency: Somali Shilling/Scellino Somalo/706
Currency: Mexican Peso/Peso Messicano/484
Currency: Zimbabwean Dollar (1980-2008)/Dollaro dello Zimbabwe/716
Currency: British Pound Sterling/Sterlina Inglese/826
Currency: Albanian Lek/Lek Albanese/8
Currency: Jordanian Dinar/Dinaro Giordano/400
Currency: East Caribbean Dollar/Dollaro dei Caraibi Orientali/951
Currency: Cuban Convertible Peso/Cuban Convertible Peso/931
Currency: Gold/Oro/959
Currency: Czech Republic Koruna/Corona Ceca/203
Currency: US Dollar/Dollaro Statunitense/840
Currency: Cuban Peso/Peso Cubano/192
Currency: Comorian Franc/Franco Comoriano/174
Currency: Pakistani Rupee/Rupia del Pakistan/586
Currency: Fijian Dollar/Dollaro delle Figi/242
Currency: French UIC-Franc/Franco UIC Francese/0
Currency: Vietnamese Dong/Dong Vietnamita/704
Currency: Honduran Lempira/Lempira Hoduregno/340
Currency: Brunei Dollar/Dollaro del Brunei/96
Currency: Finnish Markka/Markka Finlandese/246
Currency: European Monetary Unit/Unità monetaria europea/956
Currency: United Arab Emirates Dirham/Dirham degli Emirati Arabi Uniti/784
Currency: Algerian Dinar/Dinaro Algerino/12
Currency: South African Rand/Rand Sudafricano/710
Currency: Burundian Franc/Franco del Burundi/108
Currency: Bangladeshi Taka/Taka Bangladese/50
Currency: Paraguayan Guarani/Guarani del Paraguay/600
Currency: XSU/XSU/994
Currency: Peruvian Nuevo Sol/Sol Nuevo Peruviano/604
Currency: Ghanaian Cedi (1979-2007)/Cedi del Ghana/288
Currency: North Korean Won/Won Nordcoreano/408
Currency: Slovenian Tolar/Tallero Sloveno/705
Currency: Philippine Peso/Peso delle Filippine/608
Currency: Mauritanian Ouguiya/Ouguiya della Mauritania/478
Currency: Unknown Currency/Nessuna valuta/999
Currency: Azerbaijani Manat (1993-2006)/Manat Azero (1993-2006)/31
Currency: Cayman Islands Dollar/Dollaro delle Isole Cayman/136
Currency: Croatian Kuna/Kuna Croata/191
Currency: Botswanan Pula/Pula del Botswana/72
Currency: Saint Helena Pound/Sterlina di Sant’Elena/654
Currency: AYM/AYM/945
Currency: Ugandan Shilling/Scellino Ugandese/800
Currency: Austrian Schilling/Scellino Austriaco/40
Currency: Malaysian Ringgit/Ringgit della Malesia/458
Currency: French Gold Franc/Franco Oro Francese/0
Currency: Irish Pound/Sterlina irlandese/372
Currency: Vanuatu Vatu/Vatu di Vanuatu/548
Currency: Serbian Dinar/Dinaro serbo/941
Currency: Iranian Rial/Rial Iraniano/364
Currency: Timorese Escudo/Escudo di Timor/626
Currency: Mexican Investment Unit/Unidad de Inversion (UDI) Messicana/979
Currency: Iraqi Dinar/Dinaro Iracheno/368
Currency: Romanian Leu (1952-2006)/Leu della Romania/946
Currency: Moldovan Leu/Leu Moldavo/498
Currency: Slovak Koruna/Corona Slovacca/703
Currency: Israeli New Sheqel/Nuovo sheqel israeliano/376
Currency: Polish Zloty/Zloty Polacco/985
Currency: Seychellois Rupee/Rupia delle Seychelles/690
Currency: European Unit of Account (XBC)/Unità di acconto europea (XBC)/957
Currency: Yugoslavian New Dinar (1994-2002)/Dinaro Noviy Yugoslavo/891
Currency: Macedonian Denar/Dinaro Macedone/807
Currency: Belarusian Ruble/Rublo Bielorussia/974
Currency: Egyptian Pound/Sterlina Egiziana/818
Currency: Hong Kong Dollar/Dollaro di Hong Kong/344
Currency: Guyanaese Dollar/Dollaro della Guyana/328
Currency: Bosnia-Herzegovina Convertible Mark/Marco Conv. Bosnia-Erzegovina/977
Currency: Thai Baht/Baht Tailandese/764
Currency: Kuwaiti Dinar/Dinaro Kuwaitiano/414
Currency: Kenyan Shilling/Scellino Keniota/404
Currency: Icelandic Króna/Corona Islandese/352
Currency: Falkland Islands Pound/Sterlina delle Falkland/238
Currency: Angolan Kwanza/Kwanza Angolano/973
Currency: Bulgarian Hard Lev/Lev Bulgaro/100
Currency: Malagasy Ariary/Ariary Malgascio/969
Currency: Surinamese Dollar/Dollaro surinamese/968
Currency: Dominican Peso/Peso Dominicano/214
Currency: Tongan Pa?anga/Pa?anga di Tonga/776
Currency: Papua New Guinean Kina/Kina della Papua Nuova Guinea/598
Currency: Cape Verdean Escudo/Escudo del Capo Verde/132
Currency: Namibian Dollar/Dollaro Namibiano/516
Currency: Uruguayan Peso/Peso Uruguayo uruguaiano/858
Currency: Ethiopian Birr/Birr Etiopico/230
Currency: Trinidad and Tobago Dollar/Dollaro di Trinidad e Tobago/780
Currency: Salvadoran Colón/Colón Salvadoregno/222
Currency: Brazilian Real/Real Brasiliano/986
Currency: Silver/Argento/961
Currency: Tajikistani Somoni/Somoni del Tajikistan/972
Currency: European Composite Unit/Unità composita europea/955
Currency: Lithuanian Litas/Lita Lituana/440
Currency: Estonian Kroon/Corona dell’Estonia/233
Currency: Platinum/Platinum/962
Currency: Haitian Gourde/Gourde Haitiano/332
Currency: Belarusian New Ruble (1994-1999)/Nuovo Rublo Bielorussia (1994-1999)/112
Currency: South Korean Won/Won Sudcoreano/410
Currency: Azerbaijani Manat/Azerbaijani Manat/944
Currency: Nepalese Rupee/Rupia Nepalese/524
Currency: Cambodian Riel/Riel Cambogiano/116
Currency: Zambian Kwacha/Kwacha dello Zambia/894
Currency: New Taiwan Dollar/Nuovo dollaro taiwanese/901
Currency: CFA Franc BEAC/Franco CFA BEAC/950
Currency: Cypriot Pound/Sterlina Cipriota/196
Currency: Afghan Afghani/Afgani/971
Currency: Aruban Florin/Fiorino di Aruba/533
Currency: Canadian Dollar/Dollaro Canadese/124
Currency: Mozambican Metical (1980-2006)/Mozambican Metical (1980-2006)/508
Currency: Ghanaian Cedi/Cedi ghanese/936
Currency: German Mark/Marco Tedesco/276
Currency: Swedish Krona/Corona Svedese/752
This blog post has briefly looked at the new class-level method and three new instance-level methods added to the
Currency
class.Seven NetBeans Hints for Modernizing Java Code
In the post Seven Indispensable NetBeans Java Hints, I talked about using NetBeans hints in general and then focused on seven hints in particular. The seven hints emphasized in that post are listed next:
- Suspicious Method Call
- Comparing Strings Using == or != AND String Constructor
- Overridable Method Call in Constructor
- .equals Incompatible Types
- Incorrect Column Index in ResultSet
- Cast Incompatible with instanceof
- Generate .equals or .hashCode Method
In this post, I look focus on a different set of seven NetBeans hints that may not be as "indispensable" as the first set, but which I have found to be very useful in moving existing code and my own mindset about writing code in Java into the era of JDK 7. NetBeans 6.9 began the introduction of numerous new hints that I've become quite fond of for moving forward into improved performance and safety in JDK 7.
Suggesting EnumMap and EnumSet
Two of the hints that were introduced with NetBeans 6.9 in the "Performance" category are "Map replaceable with EnumMap" (described in NetBeans Java Hints as "Finds instantiations of Maps that can be replaced with EnumMap") and "Set replaceable with EnumSet" (described as "Finds instantiations of Sets that can be replaced with EnumSet"). I have posted before regarding the advantages of using EnumMap and EnumSet, so I find these to be two very useful hints when working with existing code. Although the Enum and associated
EnumMap
and EnumSet
have all been around since J2SE 5, there may be code bases where these were not used because the code is older or because developers did not think of them when creating their maps or sets.
The next series of snapshots demonstrate in a static fashion how these hints identify potential situations where
JDK 7 Upgrade HintsEnumSet
and EnumMap
are potentially more efficient. The first image shows an example of the hint to use EnumSet
. The second through fourth images demonstrate the hint for usingEnumMap
along with selecting the action to apply an EnumMap
and the result of that action.
NetBeans 7.x offers several hints related to Java 7 syntax and language support in the "JDK 1.5 and later" category of hints. These hints provide more examples of where NetBeans hints can help bring existing Java code bases into a newer and more current version of the JDK. One important thing to note here is that NetBeans will only identify these hints if the source version associated with the NetBeans project is JDK 7 (1.7).
Two of the NetBeans hints related to JDK 7 are related to catching exceptions. Both the "Join catch sections using multicatch" hint ("Join catch sections using multicatch") and the "Use specific catch" hint ("Converts catch (Throwable) or catch (Exception) to multicatch catching the exceptions thrown by the try body.") were introduced with NetBeans 7.0.
The three screen snapshots that follow show an extremely convoluted piece of code that does demonstrate the "Join catch section using multicatch" hint. The three images show display of the hint, choosing to apply the hint, and the result of applying the hint.
Another exception handling hint in NetBeans related to JDK 7 is the "Use specific catch hint." The idea of catching a more specific exception than Exception (or the even more general Throwable) is not new to JDK 7, but this is a JDK 7-dependent hint because it places multiple specific checked exceptions in a JDK 7 multicatch when it does the conversion. In fact, the hint doesn't apply if there is only one known checked exception that could be more specific than
Exception
or Throwable
. Runtime exceptions (unchecked exceptions) are not considered for obvious reasons (they're not checked after all!) and only the presence of multiple checked exceptions in the try
clause will lead to this hint.
All of this is depicted in the following screen snapshots. The first screen snapshot depicts the hint appearing because two checked exceptions are possible in the
try
block. The second image proves that the presence of one checked exception is insufficient for the hint to appear (one of them is commented out). The third image shows both checked exceptions applicable again and how to select the action to take place. The fourth image depicts the results of accepting the hint's recommended action: the general Exception
is changed to a multicatch with the two specific checked exceptions that might be encountered.
NetBeans 6.9 introduced the "Use switch over Strings where possible." hint ("Marks cascades of ifs which can be converted to switch over Strings."). This allows developers to more readily recognize a lengthy series of conditionals based on Strings that can be refactored to use JDK 7's support forswitching on Strings. The next three screen snapshots demonstrate the providing of this hint, the ability to apply the hint, and the result of the application of the hint.
JDK 7 introduced the diamond syntax to make use of generics a little more concise. NetBeans 7.1 has introduced the hint "Can Use Diamond" (described as "Warns about places where the diamond operator in JDK 7 can be used instead of explicit type parameters") to help migrate code to use of this more concise syntax. The next three screen snapshots show how the hint appears, the action that can be taken by clicking on it, and the result of that action being taken.
The "Convert to try-with-resources" hint ("Converts try finally block to try-with-resources") introduced with NetBeans 7.0 helps developers identify situations in which the handy and safer approach of using the new try-with-resources can be applied.
The next four images depict use of the "Convert to try-with-resources" hint in NetBeans. The first screen snapshot shows that if a resource is first checked in a conditional for non-null status, the hint does not appear. The second image shows that removing the condition on the resource leads to the hint appearing. The third screen snapshot shows the prompt to apply the action associated with the hint and the fourth image shows the results of applying the hint. The example in these snapshots is adapted from the example provided in the Java Tutorial page The try-with-resources Statement. NetBeans converts the
Upgrade to NetBeans 7.1 for Latest and Greatest Hint Supporttry
-finally (no catch
in this case) example provided in that tutorial to essentially the try-with-resources example shown in the same tutorial.
I used NetBeans 7.1 and NetBeans 7.1.1 for the screen snapshots shown in this post. Although I have not covered it here, an interesting hints-related feature of NetBeans 7.1 is the ability to create custom hints.
Conclusion
This blog post has covered seven NetBeans hints that aid developers in taking advantage of newer features of Java (JDK 7 in particular). Enabling these hints (including associating the NetBeans project with JDK 7) enables developers to quickly identify pieces of legacy code that can be modernized when the compiler is upgraded to JDK 7 and can also help developers learn to write new code using these new constructs and features. The hints covered in this post are:
- Map replaceable with EnumMap
- Set replaceable with EnumSet
- Join catch sections using multicatch
- Use specific catch
- Use switch over Strings where possible
- Can Use Diamond
- Convert to try-with-resources
No comments:
Post a Comment