You are on page 1of 453

Collected Java Practices

Collected Java Practices

Consolidated Topics
This compilation is intended for offline use, and for printing. It presents all topics on a single web page.
The content of the topics on javapractices.com is updated continually. Generated on Fri Sep 20 12:39:58
ADT 2013. See javapractices.com for the latest updates, and for further information.
Servlets and JSPs | Swing | Exceptions | Input-Output | Databases | Collections | Overriding Object Methods | Common Tasks |
Common Design Patterns | Common Practices | Inheritance | More Common Practices | Assertions | Constructors | Serialization |
Threads

Servlets and JSPs


A Web App Framework - WEB4J
Beware multilingual submit buttons
Beware of custom cookies
Beware of URL rewriting
Consider Controllers for redirects
Emit flexible URLs
Forward versus redirect
Loss of data during restart
Measure web app performance
Parse parameters into domain objects
Prefer JSTL tags
Prevent self-linking
Repel invalid requests
Send trouble-ticket emails
Session-scope objects often Serializable
Thread-safety in web apps
Use Cascading Style Sheets
Use Model-View-Controller framework
Validate email addresses
Web usability guidelines

Always maintain HttpSessions


Beware of common hacks
Beware of doubly escaped ampersands
Choose form validation style carefully
Do not synchronize doGet, doPost
Escape special characters
JSPs should contain only presentation logic
Manage sessions closely
Minimize site response time
Pre-populate forms
Prefer UTF-8 in all layers
Refactor large Controllers
Reuse login page for errors
Serve binary content
Struts seems mediocre
Understand details of FORM tags
Use extensions for fine-grained security
Use templates to standardize layout
Validate generated HTML
Wrap file upload requests

Swing
A Swing App - StocksMonitor
Align menu items
Consider JGoodies Forms for layouts
Don't subclass JDialog or JFrame
Indicate table sort
Launch other applications
Look and Feel guidelines
Observers and listeners

Actions
Change theme
Don't bury dialogs
Filter table rows
Input dialogs
Layout Managers
Minimal Swing application
Preferences dialogs
1

Collected Java Practices

Provide an uncaught exception handler


Sort table rows
Standardized dialogs
Swing threads
Timers
Using preferences
Verify input with regular expressions

Render table cells


Splash screen
Swing in general
Swing utility tasks
Using JavaHelp
Verify input with Model Objects

Exceptions
Avoid @throws in javadoc
Be specific in throws clause
Checked versus unchecked exceptions
Exceptions and control flow
Javadoc all exceptions
Stack trace as String

Avoid empty catch blocks


Beware of unknown root causes
Exception translation
Finally and catch
Pass all pertinent data to exceptions
Use template for repeated try-catch

Input-Output
Always close streams
Console input
Reading and writing binary files
Reading and writing text files

Buffering usually appropriate


Copy a file
Reading and writing Serializable objects

Databases
Business identifiers as String
Consider data layer tools
Consider wrapper classes for optional data
Data exception wrapping
Don't perform basic SQL tasks in code
Keep SQL out of code
Reduce database code duplication
Simplify database operations
Use template for transactions

Connection pools
Consider using standard SQL
Data access objects
Data is king
Encapsulate connections
Prefer PreparedStatement
Remember the basics of database design
Try pseudo-persistence for mock ups

Collections
Choosing the right Collection
Iterate without an index
Two ways of using Iterator
Use interface references to Collections

Encapsulate collections
Prefer Collections over older classes
Use for-each liberally
Use standard Collections

Overriding Object Methods


Avoid clone
Implementing equals
Implementing toString

Implementing compareTo
Implementing hashCode
Never rely on finalize

Common Tasks
2

Collected Java Practices

Arrays as String
Command line operations
Copy an array
Examine bytecode
Generate random numbers
Get size of object in memory
Internationalization
Measure application performance
Open file in native directory
Pattern-match lines of a file
Recovering resources
Replace a substring
Send an email

Clipboard copy and paste


Compare and sort Strings
Determine if Strings are equal
Fetch web page and header
Get database connection
Implement code tables
Logging messages
Modernize old code
Parse text
Read jar version information
Recursive file listing
Representing money
Time execution speed

Common Design Patterns


Abstract Factory
Factory methods
Lazy initialization
Plugin Factory
Singleton
Type-Safe Enumerations

Command objects
Immutable objects
Model Objects
Private constructor
Template method
Wrapper (Decorator)

Common Practices
Avoid basic style errors
Beware of instanceof operator
Construct classes from the outside in
Don't declare local variables before use
Interface for constants
Minimize ripple effects
Nest classes with care
Separate public and private members
Tag or marker interfaces
Use @Override liberally
Use javadoc liberally
Use System.exit with care
Validate method arguments

Avoid raw types


Class for constants
Do not break portability
Fields should usually be private
Know the core libraries
Naming conventions
Output parameters
String concatenation does not scale
Uncommon classes need explicit imports
Use final liberally
Use static imports rarely
Use Version Control tools
Wisdom, not rules

Inheritance
Consider composition instead of subclassing
Overridable methods need special care

Designing for subclassing


Remember styles of inheritance

More Common Practices


Avoid null if possible
Beware of DecimalFormat

Beware of Byte Order Marks


Beware of floating point numbers
3

Collected Java Practices

Clarifying method
Compile regular expressions once
Consider code generators
Conventional name for return value
Design by Contract
Extra space in argument list
Include 'from', exclude 'to'
Overloading can be tricky
Passwords never in clear text
Quote dynamic text when logging
Structs are occasionally useful
Try alternatives to ResourceBundle
Use a testing framework (JUnit)
Use boxing with care
Validate state with class invariants

Coding conventions
Conditional compile
Consider immutable forms for dates
Defensive copying
Don't use tab characters
Generating unique IDs
Multiple return statements
Package by feature, not layer
Prefer empty items to null ones
Self-encapsulate fields
Test using main method
Use a fake system clock
Use Ant for build scripts
Use enums to restrict arguments
Validation belongs in a Model Object

Assertions
Assert is for private arguments only
Assertions in general

Assert use cases


Force enabling of assertions

Constructors
Avoid JavaBeans style of construction
Construct Object using class name
Constructors shouldn't call overridables
Copy constructors
Initializing fields to 0-false-null is redundant

Beware of mistaken field redeclares


Constructors in general
Constructors shouldn't start threads
Don't pass 'this' out of a constructor

Serialization
Implementing Serializable
Some classes need readResolve

Serialization and subclassing

Threads
Always shut down an ExecutorService
Data integrity first
Dump thread information
Launch thread is just another user thread
Perform N tasks in parallel
Query host for the number of processors
Remember the types of intrinsic lock
Stop threads through cooperation
Synchronized is implementation detail
Use finally to unlock

Avoid ThreadGroup
Document thread safety
Handle InterruptedException
Objects communicating across threads
Prefer modern libraries for concurrency
Read-write locks
Schedule periodic tasks
Synchronize access to mutable fields
Thread priorities are not portable

Collected Java Practices

A Web App Framework - WEB4J


WEB4J is a Java web application framework. It was built by the author of javapractices.com, out of
frustration with other tools, which seem overly complex. WEB4J follows a philosophy of deep simplicity
and minimalism.
WEB4J is free and open source, with a BSD License. You can download it here. The web4j.com website
provides good documentation, including some extensive examples of web apps built with WEB4J.
See Also :
A Swing App - StocksMonitor
Struts seems mediocre

Always maintain HttpSessions


If you are using sessions, you must ensure that they are properly maintained.
Cookies are the usual mechanism for implementing sessions. However, the user's browser may have
cookies disabled. In that case, you may want to fall back on URL rewriting, to ensure sessions are still
maintained. (See this topic for a reason why you may not want to do this.) URL rewriting inserts a
session identifier into the URLs found in web pages.
If you decide to use URL rewriting, then you will need to ensure that it's applied to every link (and form's
action attribute) in every page that can form part of a session. If you mistakenly emit a link which has
no URL rewriting, then your users will lose their session information if they happen to click on that link.
To allow for this, an application should define a policy for rewriting URLs. For example, one might
decide to always use the JSTL tags <c:url> and <c:redirect> (or similar tools) to emit all links in an
application. These tags will automatically rewrite URLs when needed. (Another option might be a servlet
filter to automatically scan the output for links.)
Hard-coding "raw" links is always risky, since it can never allow for URL rewriting.
See Also :
Emit flexible URLs
Beware of custom cookies
Prefer JSTL tags
Manage sessions closely
Beware of URL rewriting

Beware multilingual submit buttons


When multipleSUBMIT controls are used in an HTML form, it's natural to use this style:
<input type='submit' name='operation' value='Add'>
<input type='submit' name='operation' value='Change'>
5

Collected Java Practices

<input type='submit' name='operation' value='Delete'>

Here, the server detects the desired operation by simply examining the value of theoperation parameter.
However, this style doesn't work well when the application is multilingual, since thevalue attribute
varies according to language.
A simple workaround for this problem is to use a different name for eachSUBMIT control:
<input type='submit' name='operation_Add' value='Add'>
<input type='submit' name='operation_Change' value='Change'>
<input type='submit' name='operation_Delete' value='Delete'>

Here, the server will detect the desired operation by looking for the presence of certain parameter names,
and not by examining the value of a singleoperation parameter.
An application might define a naming convention to ease the extraction of the desired operation. Here's
an example method where request parameters whose names start withoperation_ are treated as
operation indicators. This method may be placed in a base action class, or in a utility class:
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import java.util.logging.*;
public final class SubmitOperation {
/**
* Determine the desired operation.
*
* Returns the name of the single submitted operation_X parameter, without
* the leading 'operation_'. For example, if a single parameter named
* operation_Add is present in aRequest, then this method will return
* 'Add'.
*
* This method depends on two conventions :
* <ul>
* <li>a naming convention, whereby the names of request parameters which
* denote an operation are in the style : 'operation_blah', 'operation_XYZ',
* and so on. That is, the parameter name starts with 'operation_'
* <li>at most one operation_X parameter can appear in aRequest
* (it may have none; in that case this method will return null).
* </ul>
*
* This method is needed only when the app is multilingual, and
* multiple SUBMIT buttons appear on a single page.
*/
String getOperation(HttpServletRequest aRequest){
String result = null;
String OPERATION = "operation_";
List<String> operationParamNames = new ArrayList<>();
Enumeration<String> allParamNamesEnum = aRequest.getParameterNames();
while (allParamNamesEnum.hasMoreElements()) {
String paramName = allParamNamesEnum.nextElement();
if (paramName.startsWith(OPERATION)) {
operationParamNames.add(paramName);
}
}
if (! operationParamNames.isEmpty()) {
if (operationParamNames.size() > 1) {
throw new IllegalArgumentException(
"Expecting at most one parameter starting with '" + OPERATION +
"'. Actually found : " + operationParamNames
);
}
String operationParamName = operationParamNames.get(0);
result = operationParamName.substring(OPERATION.length());
}
fLogger.fine("Operation : " + result);
return result;
}
// PRIVATE
private static final Logger fLogger = Logger.getLogger(
6

Collected Java Practices

SubmitOperation.class.getName()
);
}

See Also :
Understand details of FORM tags

Beware of common hacks


SQL Injection Attacks
SQL Injection attacks use crafted, unusual user input to cause undesired SQL statements to be executed.
An application is vulnerable to such attacks whenever it builds SQL statements directly out of raw user
input. If an application builds SQL statements out of user input by using PreparedStatement correctly,
then it will be protected from SQL injection attacks. If, on the other hand, it uses Statement to
dynamically build SQL, or uses PreparedStatement incorrectly then it will not be protected from such
attacks.

Cross-Site Scripting (XSS) Attacks


HTML allows for scripting. Scripting needs some extra care, since it allows all kinds of code to execute
on the client, where it has access to the user's private environment. If you do not exercise care, it is very
easy to create web applications that allow users to enter malicious scripts as regular data, into forms or
URLs. When such malicious input is later fetched from the database and rendered in a view, it becomes
available for execution by the browser.
Note that such scripts will execute in any browser that renders the data, not just for the browser of the
hacker who happened to enter the script. When the malicious script is executing in an innocent victim's
browser, it gets access to sensitive information, and sends it back to the hacker. This is called a CrossSite Scripting (XSS) attack.
To prevent XSS attacks, ask yourself two questions:
can this form control accept a script as input?
if so, how will the script be disabled when the user input is ultimately rendered in a page?
Let's take this mock form as an example ( POST ing this form does nothing):
Movie Title :
Movie Decade :

2000

Submit

When the user POSTs this form, the data should always be validated as strongly as possible by the
server. For instance, the Movie Decade should be checked against a fixed set of values, to ensure that
7

Collected Java Practices

form has not been altered by a malicious user. If the Movie Decade is indeed checked in such a manner,
then it's not possible for the user to enter an arbitrary script as a value for the decade. If such strong
checks are performed, then no special precautions against XSS attacks are needed.
The Movie Title is a different story, however. Since it's free-form user input, it's not possible to tightly
constrain the Movie Title . The server cannot perform tight validation checks on its content, since there
is no 'whitelist' of allowed values.
In this case, the user may indeed enter a script. To prevent the script from being activated when rendered
in a page as regular data, special care must be taken: any special characters in free-form user input must
be escaped. The Open Web App Security Project recommends that you escape these 12 characters :
Character Encoding
<

&lt;

>

&gt;

&

&amp;

"

&quot;

'

&#039;

&#040;

&#041;

&#035;

&#037;

&#059;

&#043;

&#045;

The escaping can be performed in various places :


in the view, when the data is ultimately rendered
in the Model Object
in the database
The WEB4J tool, for example, recommends performing the escaping in the Model Object, by using its
SafeText class to model free-form user input instead of String.
In any case, remember that data should not be escaped more than once.
Note as well that JSTL's <c:out> tag performs escaping only for XML (5 characters), and not for HTML
(12 characters). In addition, note that the JSP Expression Language performs no escaping at all.

Cross-Site Request Forgery (CSRF) Attacks


The fundamental idea in a Cross-Site Request Forgery (CSRF) attack is that of hijacking a victim's
session to perform a malicious task. Since the session becomes available to the hacker, the hacker can
perform tasks that would otherwise not be possible.
For example, consider a simple logging-off operation. On many sites, logging off is implemented with a
simple link ( GET ), and not a POSTed form . A hacker can log you off such a site simply by sending you an
8

Collected Java Practices

email. Here's the scenario:


you legitimately log in to the target site
the hacker sends you an email containing a bogus IMG tag whose 'src' attribute points to the logoff link
you receive the email and open it in some client (while still logged in to the target site)
your email client automatically parses the email content
when the IMG tag is encountered by your email client, it extracts the src target and sends an HTTP
GET request over the network, ostensibly to fetch the 'image'
bingo! you are now logged out, just by opening an email
(This is why many email clients suppress the display of images.)
Defending against CSRF attacks isn't as simple as defending against XSS attacks. Usually, the defenses
take this form:
for operations that have side-effects of any kind (database edits, logging off), use a form with
method='POST'

for operations without any side-effects (listings, reports, search operations), use either a form with
method='GET' or a link
for each user log-in, create a random, hard-to-guess token, and store it in the session. This token
(also called a nonce) is injected as a hidden parameter into each form served by your application.
For each POST ed form, the server verifies that the hidden parameter is present, and has the expected
value for the current session. All of this has the intent of answering a simple question: 'Did this
POSTed form really come originally from the legitimate server, or from some unknown third
party?'
specify the content-type of the response as an HTTP header. When using form tokens, the
content-type lets tools know when you are serving HTML - if your are serving plain text or
XML (which doesn't contain a form), then there's no need to inject form tokens.
Your web app framework should assist you in defending against CSRF attacks. With servlets, it's
common to provide a Filter to generate and validate the special form token.
See Also :
Manage sessions closely
Beware of doubly escaped ampersands
Prefer PreparedStatement

Beware of custom cookies


Cookies are meant to store user-related data on the browser.
The recommended method of handling user data is not with a Cookie, but with an HttpSession:
safer than using cookies directly - data related to the user is placed in 'session scope'. Session
scope exists on the server, not in the browser, and is a much more secure way of handling sensitive
data. In addition, when implementing a session with cookies, the container will always generate
cookie values that are difficult to guess, making it difficult for hackers to steal someone else's
session.
higher level of abstraction - each session is implemented using either cookies or URL rewriting,
but the details are hidden from the caller.
9

Collected Java Practices

independence of browser settings - if the user's browser has cookies disabled, then the session
will be implemented using URL rewriting, as a backup, if desired.
If you decide to use a Cookie directly, then care should be exercised that:
it doesn't represent a security risk by exposing sensitive user data
the case of disabled cookies is acceptable in some way
Note as well that the Open Web App Security Project says that 'Remember Me' cookies are a security
risk.
See Also :
Manage sessions closely

Beware of doubly escaped ampersands


It's not uncommon to see web pages with text showing incorrectly as&quot;blah&quot; instead
of"blah". (A simple search for the text'&quot;' will return numerous examples of such errors.)
This is caused by overzealous escaping of special characters. The ampersand character'&' , in particular,
is doubly special: it is both a special character, and forms part of the escape mechanism itself, as
in'&gt;' and'&quot;' .
If text containing any special character is escaped twice, then the above mentioned problem occurs. A
simple example shows why :
original form:"blah" (including quotes)
escape once to form:&quot;blah&quot; (still renders as"blah")
escape a second time to form:&amp;quot;blah&amp;quot; (renders as&quot;blah&quot;). Note
the doubly escaped ampersand.
The most common cause of such double escaping occurs by:
first escaping when storing the original user input into the database
escaping a second time when rendering the same item in the presentation layer, using a tool such as
<c:out> in JSTL
Since the escaping is related to HTML - that is, to the presentation layer - it's recommended that the
database not store data is its escaped form.
See Also :
Escape special characters
Beware of common hacks

Beware of URL rewriting


Sessions can be implemented with two underlying mechanisms -- cookies and URL rewriting. URL
10

Collected Java Practices

rewriting involves placing a session id in the URL, as in


http://www.javapractices.com/topic/TopicAction.do;jsessionid=863F3D316?Id=191

According to the Open Web App Security Project, URL rewriting has significant security risks. The
general idea is that since the session id appears in the URL, it may be easily seen by third parties:
end users often copy and paste such links without knowing the attached session id compromises
their security
server log files usually record the 'Referer' header, which will record session ids in the log
Third-party access to session id's simply means that private user information is wide open to attack. Thus,
many argue that URL rewriting is a dangerous practice, and should be avoided. If cookies are used
instead, then the session id does not appear in the URL.
It's possible that some web sites may use cookies to track user browsing patterns. As a result, some users
turn off cookies in an attempt to protect their privacy. However, given the seriousness of the above
security issue, many would argue that turning off cookies is actually much worse for user privacy. That is,
the risk of compromising personal data through session hijacking seems to far outweigh concerns about
tracking personal browsing patterns.
Options for managing URL rewriting include :
disabling them at the server level.
disabling them at the application level. An attractive option is a Servlet filter. The filter wraps the
response object with an alternate version, which changes response.encodeURL(String) and
related methods into no-operations. (The WEB4J tool includes such a filter.)
In the case of public web sites, you will need to decide if requiring browsers to keep cookies enabled is
acceptable in each case.
See Also :
Emit flexible URLs
Always maintain HttpSessions
Manage sessions closely

Choose form validation style carefully


In web applications, validation of user input can be done with two different tools:
scripting code executed by the browser (javascript, for example)
regular java code executed by the server, as part of the web application
Server validation:
cannot be disabled by a change in browser preferences
executes only after the SUBMIT button is pressed
validates all fields of the form as one unit
if an error is detected, a new web page is displayed, with an appropriate error message
Browser validation:

11

Collected Java Practices

can be disabled by a change in browser preferences


can be initiated in two ways : after the SUBMIT button is pressed, or after an input field has changed
if executing after SUBMIT is pressed, the form's data is POST -ed to the server only if validation is
successful
if an error is detected, a pop-up dialog box is displayed
Another important distinction between the two styles deals not with their behavior, but with their
testability. Validation code on the server can always be tested economically using tools such as JUnit.
During the entire life of an application, such tests can be quickly repeated using automated means,
helping to identify possible errors caused by code changes. Although tools such as HttpUnit can test
javascript behavior in web applications, such tests do not seem to be as simple to implement as JUnit
tests on a Model Object.
Browser validation seems most useful for :
low bandwidth environments
forms with a large number of fields, each validated as it is input
If using scripting, should validation be done after SUBMIT is pressed, or as each field is changed? If there
are no dependencies between fields, there is not much difference between the two styles. However, if
there are dependencies between fields, which is quite common, then the validate-after- SUBMIT style is
likely to be simpler to implement.
Many prefer to treat server validation as mandatory and browser validation as optional, either as a
performance optimization or as the preferred user experience. If both are implemented, then this will
usually represent significant code repetition.
See Also :
Use a testing framework (JUnit)

Consider Controllers for redirects


HTTP provides redirection headers. A main use case for these headers is to instruct browsers that a page
has moved to a new URL, either temporarily or permanently. When a page is moved, any links to the old
URL will be broken, unless a redirect is provided.
Here's a snippet which performs a permanent redirection, using an HttpServletResponse object:
aResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
aResponse.setHeader("Location", "www.javapractices.com/source/SourceAction.do");

In the case of a temporary redirection, another style can be used instead, if desired:
aResponse.sendRedirect("www.javapractices.com/source/SourceAction.do");

Using a Controller servlet to perform redirections is often an attractive option.


Example
The Servlet API allows for welcome-file entries in web.xml. These items handle requests which specify
only a directory, instead of a specific file or action.
A common requirement is to serve welcome files using a servlet instead of a static page. It's true that a
12

Collected Java Practices

static page can indeed perform a redirect. However, that style seems to be slower. As well, it forces the
quick display of an intermediate page before the "real" one, which seems to be a less pleasing experience
for the user.
The javapractices.com site uses a redirection Controller to serve its home page. Here are the relevant
entries in its web.xml:
<servlet>
<servlet-name>RedirectWelcomeFile</servlet-name>
<description>
Redirects directory requests to the home page Action.
</description>
<servlet-class>hirondelle.jp.redirect.WelcomeFileController</servlet-class>
<init-param>
<param-name>Destination</param-name>
<param-value>http://www.javapractices.com/home/HomeAction.do</param-value>
<description>
The URL of the home page, as an Action.
</description>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>RedirectWelcomeFile</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
<!-- the welcome-file points to the servlet, not to a file -->
<!-- note the slash does not appear here -->
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>

The actual Controller class is fairly simple. Note that it reads in the Destination setting from web.xml :
package hirondelle.jp.redirect;
import
import
import
import
import
import
import
import

hirondelle.web4j.util.Util;
java.io.IOException;
java.util.logging.Logger;
javax.servlet.ServletConfig;
javax.servlet.ServletException;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;

/**
* Redirect requests for the home page to a specific Action.
*
* <P>Configuration in web.xml :<br>
* This Controller servlet is mapped to '/welcome', and a corresponding welcome-file
entry
* is listed as 'welcome', without the leading slash.
*/
public final class WelcomeFileController extends HttpServlet {
@Override public void init(ServletConfig aConfig) throws ServletException {
super.init(aConfig);
fLogger.config("WelcomeFile Controller - starting.");
fDestination = aConfig.getInitParameter("Destination");
if ( ! Util.textHasContent(fDestination) ) {
fLogger.severe("Destination URL needed, but not configured in web.xml.");
}
}
@Override public void destroy(){
fLogger.config("WelcomeFile Controller - ending.");
}
@Override protected void doGet(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
fLogger.fine("Redirecting directory request to new location : " + fDestination);
aResponse.setContentType("text/html");
aResponse.sendRedirect(fDestination);
13

Collected Java Practices

}
// PRIVATE //
private String fDestination;
private static final Logger fLogger = Util.getLogger(WelcomeFileController.class);
}

See Also :
Forward versus redirect

Do not synchronize doGet, doPost


The servlet specification strongly recommends that doGet , doPost, and all other service methods should
not be declared as synchronized.
Declaring such methods as synchronized will often decrease performance significantly. It is not
necessary to declare these methods assynchronized at all, since this is taken care of by the servlet
container itself.

Emit flexible URLs


The URLs emitted by a web application should respond flexibly to changes in deployment details, and to
the possible use of session IDs by the container:
relative URLs are usually preferred, as they are insensitive to deployment details.
JSTL's <c:url> and <c:redirect> tags are helpful, since they prepend the application context, and
they also transparently rewrite the URL if necessary, by adding a session ID. Note that <c:url> is
used only with relative URLs.
Other items to note :
URL rewriting is considered by many to be a security risk.
The <c:url> tag doesn't escape ampersand characters. This means, unfortunately, that the URL
generated by <c:url> doesn't always form a valid HTML HREF attribute. When the URL
generated by <c:url> contains more than one request parameter (as in Blah.do?X=Y&A=B), some
means should be used to properly escape the '&' character.

See Also :
Always maintain HttpSessions
Prefer JSTL tags
Manage sessions closely
Beware of URL rewriting
14

Collected Java Practices

Escape special characters


Arbitrary text placed in an HTML tag often needs to be altered, to ensure that the resulting HTML
remains valid.
Problem characters can include:
<
>
"
'
\
&
These characters can be replaced with HTML character entities. For example, < can be replaced with
&lt; .
Query strings ( Blah=1&Name=Bob ) often need to be escaped as well. If the query string contains special
characters, it will need to be "URL encoded". (See the javadoc for the URLEncoder class for further
information.) This will ensure the query string conforms with valid HTTP.
There's often a second issue, however, with regard to query strings. If a query string is placed in an HREF
attribute, then even a URL encoded query string is often not of valid form. This is because URLEncoder
produces valid HTTP, but it doesn't in general produce text which is a valid HTML attribute - the
ampersand character needs to be replaced by the corresponding character entity &amp; .
Here is an example of a utility class which escapes special characters for HTML, XML, regular
expressions, and so on.
package hirondelle.web4j.util;
import
import
import
import
import
import

java.net.URLEncoder;
java.io.UnsupportedEncodingException;
java.text.CharacterIterator;
java.text.StringCharacterIterator;
java.util.regex.Pattern;
java.util.regex.Matcher;

import
import
import
import
import
import

hirondelle.web4j.security.SafeText;
hirondelle.web4j.ui.translate.Text;
hirondelle.web4j.ui.translate.Tooltips;
hirondelle.web4j.ui.translate.TextFlow;
hirondelle.web4j.ui.tag.Populate;
hirondelle.web4j.database.Report;

/**
Convenience methods for escaping special characters related to HTML, XML,
and regular expressions.
<P>To keep
characters
don't need
shouldn't

you safe by default, WEB4J goes to some effort to escape


in your data when appropriate, such that you <em>usually</em>
to think too much about escaping special characters. Thus, you
need to <em>directly</em> use the services of this class very often.

<P><span class='highlight'>For Model Objects containing free form user input,


it is highly recommended that you use {@link SafeText}, not <tt>String</tt></span>.
Free form user input is open to malicious use, such as
<a href='http://www.owasp.org/index.php/Cross_Site_Scripting'>Cross Site Scripting</a>
attacks.
Using <tt>SafeText</tt> will protect you from such attacks, by always escaping
special characters automatically in its <tt>toString()</tt> method.

15

Collected Java Practices

<P>The following WEB4J classes will automatically escape special characters


for you, when needed :
<ul>
<li>the {@link SafeText} class, used as a building block class for your
application's Model Objects, for modeling all free form user input
<li>the {@link Populate} tag used with forms
<li>the {@link Report} class used for creating quick reports
<li>the {@link Text}, {@link TextFlow}, and {@link Tooltips} custom tags used
for translation
</ul>
*/
public final class EscapeChars {
/**
Escape characters for text appearing in HTML markup.
<P>This method exists as a defence against Cross Site Scripting (XSS) hacks.
The idea is to neutralize control characters commonly used by scripts, such that
they will not be executed by the browser. This is done by replacing the control
characters with their escaped equivalents.
See {@link hirondelle.web4j.security.SafeText} as well.
<P>The following characters are replaced with corresponding
HTML character entities :
<table border='1' cellpadding='3' cellspacing='0'>
<tr><th> Character </th><th>Replacement</th></tr>
<tr><td> < </td><td> &lt; </td></tr>
<tr><td> > </td><td> &gt; </td></tr>
<tr><td> & </td><td> &amp; </td></tr>
<tr><td> " </td><td> &quot;</td></tr>
<tr><td> \t </td><td> &#009;</td></tr>
<tr><td> ! </td><td> &#033;</td></tr>
<tr><td> # </td><td> &#035;</td></tr>
<tr><td> $ </td><td> &#036;</td></tr>
<tr><td> % </td><td> &#037;</td></tr>
<tr><td> ' </td><td> &#039;</td></tr>
<tr><td> ( </td><td> &#040;</td></tr>
<tr><td> ) </td><td> &#041;</td></tr>
<tr><td> * </td><td> &#042;</td></tr>
<tr><td> + </td><td> &#043; </td></tr>
<tr><td> , </td><td> &#044; </td></tr>
<tr><td> - </td><td> &#045; </td></tr>
<tr><td> . </td><td> &#046; </td></tr>
<tr><td> / </td><td> &#047; </td></tr>
<tr><td> : </td><td> &#058;</td></tr>
<tr><td> ; </td><td> &#059;</td></tr>
<tr><td> = </td><td> &#061;</td></tr>
<tr><td> ? </td><td> &#063;</td></tr>
<tr><td> @ </td><td> &#064;</td></tr>
<tr><td> [ </td><td> &#091;</td></tr>
<tr><td> \ </td><td> &#092;</td></tr>
<tr><td> ] </td><td> &#093;</td></tr>
<tr><td> ^ </td><td> &#094;</td></tr>
<tr><td> _ </td><td> &#095;</td></tr>
<tr><td> ` </td><td> &#096;</td></tr>
<tr><td> { </td><td> &#123;</td></tr>
<tr><td> | </td><td> &#124;</td></tr>
<tr><td> } </td><td> &#125;</td></tr>
<tr><td> ~ </td><td> &#126;</td></tr>
</table>
<P>Note that JSTL's {@code <c:out>} escapes <em>only the first
five</em> of the above characters.
*/
public static String forHTML(String aText){
final StringBuilder result = new StringBuilder();
final StringCharacterIterator iterator = new StringCharacterIterator(aText);
char character = iterator.current();
while (character != CharacterIterator.DONE ){
if (character == '<') {
result.append("&lt;");
}
else if (character == '>') {
result.append("&gt;");
}
else if (character == '&') {
result.append("&amp;");
}
else if (character == '\"') {
result.append("&quot;");
16

Collected Java Practices

}
else if (character == '\t') {
addCharEntity(9, result);
}
else if (character == '!') {
addCharEntity(33, result);
}
else if (character == '#') {
addCharEntity(35, result);
}
else if (character == '$') {
addCharEntity(36, result);
}
else if (character == '%') {
addCharEntity(37, result);
}
else if (character == '\'') {
addCharEntity(39, result);
}
else if (character == '(') {
addCharEntity(40, result);
}
else if (character == ')') {
addCharEntity(41, result);
}
else if (character == '*') {
addCharEntity(42, result);
}
else if (character == '+') {
addCharEntity(43, result);
}
else if (character == ',') {
addCharEntity(44, result);
}
else if (character == '-') {
addCharEntity(45, result);
}
else if (character == '.') {
addCharEntity(46, result);
}
else if (character == '/') {
addCharEntity(47, result);
}
else if (character == ':') {
addCharEntity(58, result);
}
else if (character == ';') {
addCharEntity(59, result);
}
else if (character == '=') {
addCharEntity(61, result);
}
else if (character == '?') {
addCharEntity(63, result);
}
else if (character == '@') {
addCharEntity(64, result);
}
else if (character == '[') {
addCharEntity(91, result);
}
else if (character == '\\') {
addCharEntity(92, result);
}
else if (character == ']') {
addCharEntity(93, result);
}
else if (character == '^') {
addCharEntity(94, result);
}
else if (character == '_') {
addCharEntity(95, result);
}
else if (character == '`') {
addCharEntity(96, result);
}
else if (character == '{') {
addCharEntity(123, result);
17

Collected Java Practices

}
else if (character == '|') {
addCharEntity(124, result);
}
else if (character == '}') {
addCharEntity(125, result);
}
else if (character == '~') {
addCharEntity(126, result);
}
else {
//the char is not a special one
//add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}
/**
Escape all ampersand characters in a URL.
<P>Replaces all <tt>'&'</tt> characters with <tt>'&amp;'</tt>.
<P>An ampersand character may appear in the query string of a URL.
The ampersand character is indeed valid in a URL.
<em>However, URLs usually appear as an <tt>HREF</tt> attribute, and
such attributes have the additional constraint that ampersands
must be escaped.</em>
<P>The JSTL <c:url> tag does indeed perform proper URL encoding of
query parameters. But it does not, in general, produce text which
is valid as an <tt>HREF</tt> attribute, simply because it does
not escape the ampersand character. This is a nuisance when
multiple query parameters appear in the URL, since it requires a little
extra work.
*/
public static String forHrefAmpersand(String aURL){
return aURL.replace("&", "&amp;");
}
/**
Synonym for <tt>URLEncoder.encode(String, "UTF-8")</tt>.
<P>Used to ensure that HTTP query strings are in proper form, by escaping
special characters such as spaces.
<P>It is important to note that if a query string appears in an <tt>HREF</tt>
attribute, then there are two issues - ensuring the query string is valid HTTP
(it is URL-encoded), and ensuring it is valid HTML (ensuring the
ampersand is escaped).
*/
public static String forURL(String aURLFragment){
String result = null;
try {
result = URLEncoder.encode(aURLFragment, "UTF-8");
}
catch (UnsupportedEncodingException ex){
throw new RuntimeException("UTF-8 not supported", ex);
}
return result;
}
/**
Escape characters for text appearing as XML data, between tags.
<P>The following characters are replaced with corresponding character entities :
<table border='1' cellpadding='3' cellspacing='0'>
<tr><th> Character </th><th> Encoding </th></tr>
<tr><td> < </td><td> &lt; </td></tr>
<tr><td> > </td><td> &gt; </td></tr>
<tr><td> & </td><td> &amp; </td></tr>
<tr><td> " </td><td> &quot;</td></tr>
<tr><td> ' </td><td> &#039;</td></tr>
</table>
<P>Note that JSTL's {@code <c:out>} escapes the exact same set of
18

Collected Java Practices

characters as this method. <span class='highlight'>That is, {@code <c:out>}


is good for escaping to produce valid XML, but not for producing safe
HTML.</span>
*/
public static String forXML(String aText){
final StringBuilder result = new StringBuilder();
final StringCharacterIterator iterator = new StringCharacterIterator(aText);
char character = iterator.current();
while (character != CharacterIterator.DONE ){
if (character == '<') {
result.append("&lt;");
}
else if (character == '>') {
result.append("&gt;");
}
else if (character == '\"') {
result.append("&quot;");
}
else if (character == '\'') {
result.append("&#039;");
}
else if (character == '&') {
result.append("&amp;");
}
else {
//the char is not a special one
//add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}
/**
Escapes characters for text appearing as data in the
<a href='http://www.json.org/'>Javascript Object Notation</a>
(JSON) data interchange format.
<P>The following commonly used control characters are escaped :
<table border='1' cellpadding='3' cellspacing='0'>
<tr><th> Character </th><th> Escaped As </th></tr>
<tr><td> " </td><td> \" </td></tr>
<tr><td> \ </td><td> \\ </td></tr>
<tr><td> / </td><td> \/ </td></tr>
<tr><td> back space </td><td> \b </td></tr>
<tr><td> form feed </td><td> \f </td></tr>
<tr><td> line feed </td><td> \n </td></tr>
<tr><td> carriage return </td><td> \r </td></tr>
<tr><td> tab </td><td> \t </td></tr>
</table>
<P>See <a href='http://www.ietf.org/rfc/rfc4627.txt'>RFC 4627</a> for more
information.
*/
public static String forJSON(String aText){
final StringBuilder result = new StringBuilder();
StringCharacterIterator iterator = new StringCharacterIterator(aText);
char character = iterator.current();
while (character != StringCharacterIterator.DONE){
if( character == '\"' ){
result.append("\\\"");
}
else if(character == '\\'){
result.append("\\\\");
}
else if(character == '/'){
result.append("\\/");
}
else if(character == '\b'){
result.append("\\b");
}
else if(character == '\f'){
result.append("\\f");
}
else if(character == '\n'){
result.append("\\n");
}
else if(character == '\r'){
19

Collected Java Practices

result.append("\\r");
}
else if(character == '\t'){
result.append("\\t");
}
else {
//the char is not a special one
//add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}
/**
Return <tt>aText</tt> with all <tt>'<'</tt> and <tt>'>'</tt> characters
replaced by their escaped equivalents.
*/
public static String toDisableTags(String aText){
final StringBuilder result = new StringBuilder();
final StringCharacterIterator iterator = new StringCharacterIterator(aText);
char character = iterator.current();
while (character != CharacterIterator.DONE ){
if (character == '<') {
result.append("&lt;");
}
else if (character == '>') {
result.append("&gt;");
}
else {
//the char is not a special one
//add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}
/**
Replace characters having special meaning in regular expressions
with their escaped equivalents, preceded by a '\' character.
<P>The escaped characters include :
<ul>
<li>.
<li>\
<li>?, * , and +
<li>&
<li>:
<li>{ and }
<li>[ and ]
<li>( and )
<li>^ and $
</ul>
*/
public static String forRegex(String aRegexFragment){
final StringBuilder result = new StringBuilder();
final StringCharacterIterator iterator =
new StringCharacterIterator(aRegexFragment)
;
char character = iterator.current();
while (character != CharacterIterator.DONE ){
/*
All literals need to have backslashes doubled.
*/
if (character == '.') {
result.append("\\.");
}
else if (character == '\\') {
result.append("\\\\");
}
else if (character == '?') {
result.append("\\?");
}
else if (character == '*') {
20

Collected Java Practices

result.append("\\*");
}
else if (character == '+') {
result.append("\\+");
}
else if (character == '&') {
result.append("\\&");
}
else if (character == ':') {
result.append("\\:");
}
else if (character == '{') {
result.append("\\{");
}
else if (character == '}') {
result.append("\\}");
}
else if (character == '[') {
result.append("\\[");
}
else if (character == ']') {
result.append("\\]");
}
else if (character == '(') {
result.append("\\(");
}
else if (character == ')') {
result.append("\\)");
}
else if (character == '^') {
result.append("\\^");
}
else if (character == '$') {
result.append("\\$");
}
else {
//the char is not a special one
//add it to the result as is
result.append(character);
}
character = iterator.next();
}
return result.toString();
}
/**
Escape <tt>'$'</tt> and <tt>'\'</tt> characters in replacement strings.
<P>Synonym for <tt>Matcher.quoteReplacement(String)</tt>.
<P>The following methods use replacement strings which treat
<tt>'$'</tt> and <tt>'\'</tt> as special characters:
<ul>
<li><tt>String.replaceAll(String, String)</tt>
<li><tt>String.replaceFirst(String, String)</tt>
<li><tt>Matcher.appendReplacement(StringBuffer, String)</tt>
</ul>
<P>If replacement text can contain arbitrary characters, then you
will usually need to escape that text, to ensure special characters
are interpreted literally.
*/
public static String forReplacementString(String aInput){
return Matcher.quoteReplacement(aInput);
}
/**
Disable all <tt><SCRIPT></tt> tags in <tt>aText</tt>.
<P>Insensitive to case.
*/
public static String forScriptTagsOnly(String aText){
String result = null;
Matcher matcher = SCRIPT.matcher(aText);
result = matcher.replaceAll("&lt;SCRIPT>");
matcher = SCRIPT_END.matcher(result);
result = matcher.replaceAll("&lt;/SCRIPT>");
return result;
}
21

Collected Java Practices

// PRIVATE //
private EscapeChars(){
//empty - prevent construction
}
private static final Pattern SCRIPT = Pattern.compile(
"<SCRIPT>", Pattern.CASE_INSENSITIVE
);
private static final Pattern SCRIPT_END = Pattern.compile(
"</SCRIPT>", Pattern.CASE_INSENSITIVE
);
private static void addCharEntity(Integer aIdx, StringBuilder aBuilder){
String padding = "";
if( aIdx <= 9 ){
padding = "00";
}
else if( aIdx <= 99 ){
padding = "0";
}
else {
//no prefix
}
String number = padding + aIdx.toString();
aBuilder.append("&#" + number + ";");
}
}

See Also :
Replace a substring
Beware of doubly escaped ampersands

Forward versus redirect


A Controller (in this context, an implementation of HttpServlet) may perform either a forward or a
redirect operation at the end of processing a request. It's important to understand the difference between
these two cases, in particular with respect to browser reloads of web pages.
Forward
a forward is performed internally by the servlet
the browser is completely unaware that it has taken place, so its original URL remains intact
any browser reload of the resulting page will simple repeat the original request, with the original
URL
Redirect
a redirect is a two step process, where the web application instructs the browser to fetch a second
URL, which differs from the original
a browser reload of the second URL will not repeat the original request, but will rather fetch the
second URL
redirect is marginally slower than a forward, since it requires two browser requests, not one
objects placed in the original request scope are not available to the second request

22

Collected Java Practices

In general, a forward should be used if the operation can be safely repeated upon a browser reload of the
resulting web page; otherwise, redirect must be used. Typically, if the operation performs an edit on the
datastore, then a redirect, not a forward, is required. This is simply to avoid the possibility of
inadvertently duplicating an edit to the database.
More explicitly (in terms of common SQL operations) :
for SELECT operations, use a forward
for INSERT, UPDATE, or DELETE operations, use a redirect
In HTML, a <FORM> tag can either GET or POST its data. In this context, a GET corresponds to a SELECTthen-forward, and a POST corresponds to an edit-then-redirect.
It's strongly recommended that forms for the input of search criteria should use GET , while forms for
editing database records should use POST .
The most common symptom of not using forward/redirect properly is a warning message in a browser,
asking the user if they really wish to POST their form data a second time.
Example
This example is after the style of the WEB4J Controller class. The important methods of the Servlet API
are:
ServletRequest.getRequestDispatcher(String)
RequestDispatcher.forward(request, response)
HttpServletResponse.sendRedirect(String)

import
import
import
import
import
import
import

java.io.IOException;
javax.servlet.*;
javax.servlet.http.*;
hirondelle.web4j.action.Action;
hirondelle.web4j.action.ResponsePage;
hirondelle.web4j.request.RequestParser;
hirondelle.web4j.model.BadRequestException;

public class RedirectForward extends HttpServlet {


//..many items elided
@Override public final void doGet(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
processRequest(aRequest, aResponse);
}
@Override public final void doPost(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
processRequest(aRequest, aResponse);
}
/**
* Handle all HTTP <tt>GET</tt> and <tt>POST</tt> requests.
*/
protected void processRequest(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
RequestParser requestParser = RequestParser.getInstance(aRequest, aResponse);
try {
Action action = requestParser.getWebAction();
ResponsePage responsePage = action.execute();
if ( responsePage.getIsRedirect() ) {
redirect(responsePage, aResponse);
23

Collected Java Practices

}
else {
forward(responsePage, aRequest, aResponse);
}
}
catch (BadRequestException ex){
//..elided
//use Response.sendError()
}
catch (Throwable ex) {
//..elided
//use Response.sendError()
}
}
// PRIVATE //
private void redirect(
ResponsePage aDestinationPage, HttpServletResponse aResponse
) throws IOException {
String urlWithSessionID = aResponse.encodeRedirectURL(aDestinationPage.toString());
aResponse.sendRedirect( urlWithSessionID );
}
private void forward(
ResponsePage aResponsePage, HttpServletRequest aRequest, HttpServletResponse
aResponse
) throws ServletException, IOException {
RequestDispatcher dispatcher =
aRequest.getRequestDispatcher(aResponsePage.toString());
dispatcher.forward(aRequest, aResponse);
}
}

See Also :
Command objects
A Web App Framework - WEB4J
Consider Controllers for redirects

JSPs should contain only presentation logic


If an MVC framework is being followed, then most of the content of a JSP should simply present and
format the data (the "Model" objects of the MVC framework) being passed to the JSP by the Controller
(or its delegate).
Many feel that when using MVC, code in a JSP which performs a task not directly related to presentation
(such as business logic, validation, error handling, etc.) probably belongs elsewhere. For example, the
most natural home for validation code is the Model Object.
This is a specific example of a general guideline of lasting value - the idea of separating the "layers" of
the application : the user interface, the business logic or model, and the database. The underlying reason
for this separation is, ultimately, to allow people with different skills to make significant and effective
contributions to the development of an application. User interface designers should be concerned almost
entirely with presentation issues, while a database expert should be concerned almost entirely with
writing SQL.
(See as well the package by feature, not layer topic.)

24

Collected Java Practices

See Also :
Use Model-View-Controller framework
Package by feature, not layer
Validation belongs in a Model Object

Loss of data during restart


If a server restarts, then data stored in session scope may be lost. The behaviour of a web container in
this case is not defined by the servlet specification, and varies between implementations. Some web
containers provide "failover", in which session data is not lost during a restart, while others do not.
In those that do implement failover, a common requirement is that only Serializable objects can be
restored.
See Also :
Implementing Serializable
Session-scope objects often Serializable

Manage sessions closely


The Servlet API is, unfortunately, rather liberal in creating sessions. Various tools have default behaviors
which can implicitly create sessions in the background. It's very easy for an application to "accidentally"
create a session, even when one was not explicitly requested.
An an example, JSPs will often create a session if one doesn't already exist. This allows JSPs to use the
implicit session variable. As a second example, the request.getSession() method will also
automatically create a session if one doesn't already exist.
However, sessions should be managed with some care, for two main reasons:
sessions have various security risks associated with them.
sessions consume server resources.
So, the creation and destruction of sessions should likely be tightly controlled by the application.
Here's an example of a reasonable set of policies regarding sessions:
use a <%@ page session="false" %> directive at the top of every JSP that doesn't use a session
consider disabling URL rewriting altogether
create a new session only when the user logs in (and not before)
when the user logs out, invalidate the session and delete any related cookie
in web.xml, ensure session time out is set to value which isn't unnecessarily long
defend against Cross-Site Request Forgery attacks (which hijack existing sessions)

See Also :
25

Collected Java Practices

Emit flexible URLs


Always maintain HttpSessions
Beware of custom cookies
Beware of common hacks
Beware of URL rewriting

Measure web app performance


Web applications have no control over the frequency or timing of user requests. As well, the only way of
determining an application's performance is to actually measure it under various loads.
Many tools are available to help the programmer measure web application performance. Such tools are
configured by the developer to simulate the expected environment of an application, by sending it
multiple requests, through multiple independent threads. The tool then measures response times, and
collects statistics on application performance.
See Also :
Measure application performance
Minimize site response time

Minimize site response time


Response times of web sites are usually slow compared with that of desktop software. It's very helpful to
minimize web site response times, to increase user satisfaction.
Some helpful techniques:
minimize the number of graphics associated with the page
if a graphic is necessary, consider ways of reducing its size in bytes
seriously consider serving zipped content (all modern browsers are able to unzip pages on the fly)
remove unnecessary whitespace
during development, use the Firebug extension for Firefox (or a similar tool) to show file size and
download time for each element in the page

See Also :
Web usability guidelines
Measure web app performance

Parse parameters into domain objects


All HTTP request parameter names and values are entirely textual, and the Servlet Specification treats
them as simple Strings. The problem always arises in a web application of how to translate or parse such
26

Collected Java Practices

Strings into more meaningful objects in the problem domain. Typically, this means performing two kinds
of operations:
translating Strings into Integer, BigDecimal , and so on
grouping such translated items into complete Model Objects
These operations will vary depending on the framework you're using.
Example
This example is taken from an application built with the WEB4J framework.
/** Build a Model Object from request parameters. */
public final class RestoAction extends ActionTemplateListAndEdit {
public static final RequestParameter RESTO_ID =
RequestParameter.withLengthCheck("Id");
public static final RequestParameter NAME = RequestParameter.withLengthCheck("Name");
public static final RequestParameter LOCATION =
RequestParameter.withLengthCheck("Location");
public static final RequestParameter PRICE =
RequestParameter.withLengthCheck("Price");
public static final RequestParameter COMMENT =
RequestParameter.withLengthCheck("Comment");
protected void validateUserInput() {
try {
ModelFromRequest builder = new ModelFromRequest(getRequestParser());
fResto = builder.build(Resto.class, RESTO_ID, NAME, LOCATION, PRICE, COMMENT);
}
catch (ModelCtorException ex){
//displays error message to end user
addError(ex);
}
}
//..many items elided
//target Model Object, built from user input
private Resto fResto;
}

In this case, the parsing of raw HTTP request parameters is shared between these classes:
RequestParameter represents the raw underlying parameters accepted by the Action
RequestParser defines default policies for translating single request parameters into String, Date ,
Boolean , Integer , and BigDecimal objects, and Collection s thereof.
ModelFromRequest translates sets of request parameters into Model Objects. While a
RequestParser returns single "building block" objects ( Integer , Date , etc.), ModelFromRequest

goes a step further, and returns an entire Model Object, which more or less encapsulates a number
of "building block" objects.
Validation and parsing of HTTP request parameters are related topics. See the Repel invalid requests
topic for further information.
See Also :
Repel invalid requests
Command objects
Model Objects
A Web App Framework - WEB4J

27

Collected Java Practices

Pre-populate forms
Pre-populating form elements is a very common and important task in a web application. If prepopulation is implemented effectively, then a web app becomes both simpler and more robust.
Forms are used for two kinds of operations: editing the database, and defining search criteria.
When editing the database, there are two use cases for forms:
the "add" use case, where a new record is added to the datastore - here, input items are initially
blank, or have default initial values set in their static HTML.
the "change" use case, where an already existing record is to be edited by the user - here, input
items are not initially blank, but are dynamically pre-populated with the current values fetched
from the database.
In both cases, the user makes entries in the form, and then submits it for processing, using a POST . If an
error is detected, then there is a "return to sender" operation, and the HTML form is redisplayed, with all
its items dynamically pre-populated, showing their most recently entered values. Appropriate error
messages are also displayed.
(For forms used for input of search criteria, the behavior is similar to the "add" use case described above,
with the important difference that the form uses GET instead of POST .)
How can this behavior be implemented most effectively?
One method uses a single custom Populate tag, and is guided by the idea that adding pre-population to a
form should have an absolutely minimal affect on the markup used in the case of a regular, static HTML
form. This seems very effective, since
web page authors do not need to learn anything new, since forms remain almost exactly as they are
in the static case
it radically minimizes the effort needed to implement pre-population
it reuses request parameters which are already present and already in the form needed for ultimate
display
it couples HTML forms to the details of a domain object only through a simple naming convention,
which maps the names of input controls to the getXXX methods of the domain object
implementations of domain classes need to handle only the case of valid data, so they become
smaller and simpler, a class invariant is easy to define, and they can be immutable objects
An inferior style, commonly seen in frameworks such as Struts and Java Server Faces, replaces the
standard HTML tags such as <INPUT> and <SELECT> with a parallel collection of custom tags. Such a
style has these disadvantages:
the form needs radical alteration from the style used in a simple, static HTML form
any prototype written in HTML must be either discarded or altered radically
the web page author is forced to learn the details of multiple custom tags, which are different for
each framework
if the framework is changed, then another set of custom tags must be learned
pre-population is more of a programming task than a rendering task, but in this style the page
author is mainly responsible for its implementation
Example

28

Collected Java Practices

The web4j framework implements such a Populate tag. Here, a JSP uses the <w:populate> tag to wrap
the controls of a <FORM> tag that may need dynamic population. Note how the <w:populate> tag
simply wraps the body of the form, and represents an absolutely minimal change to the style used in a
simple, static HTML form.
<w:populate using="itemForEdit">
<form action='MemberAction.do' method="post">
<input name="Id" type="hidden">
<table align="center">
<tr>
<td><label>Name</label> *</td>
<td><input name="Name" type="text"></td>
</tr>
<tr>
<td><label>Is Active?</label></td>
<td><input name="Is Active" type="checkbox" value="true"></td>
</tr>
<tr>
<td><label>Disposition</label></td>
<td>
<select name="Disposition">
<option> </option>
<c:forEach var="item" items="${dispositions}">
<option value="${item.id}">${item}</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<td align="center" colspan=2>
<input type="submit" value="add.edit.button">
</td>
</tr>
</table>
<tags:hiddenOperationParam/>
</form>
</w:populate>

See Also :
Prefer JSTL tags
A Web App Framework - WEB4J

Prefer JSTL tags


JSTL is a standard set of tags used for implementing common tasks in Java Server Pages. It's probably
best to prefer the JSTL over other tag libraries, if possible, since the average web programmer can be
expected to be familiar with it.
To give an example of typical JSTL syntax, here are some JSP snippets which use the core, format, and
function parts of JSTL:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
...
<c:forEach var="restaurant" items="${itemsForListing}" varStatus="index">
<tr class="row_highlight">
<td title="Line Number">${index.count}</td>
<td>${restaurant.name}</td>
<td align="center">${restaurant.location}</td>
29

Collected Java Practices

<td>${restaurant.price}</td>
<td align="center">${restaurant.comment}</td>
</tr>
</c:forEach>
...
System Properties (${fn:length(systemProperties)})
<c:forEach var="entry" items="${systemProperties}">
<b>${entry.key}</b> = ${entry.value}
</c:forEach>
<jsp:useBean id="now" class="java.util.Date" />
Copyright <fmt:formatDate value="${now}" pattern="yyyy" />

See Also :
Emit flexible URLs
Always maintain HttpSessions

Prefer UTF-8 in all layers


If an application displays text with strange, unexpected characters, the likely cause is an incorrect
character encoding.
Character encodings control how tools translate raw bytes into text. The best default character encoding is
likely UTF-8. It can represent characters in almost all languages, and in an efficient manner. So, it seems
to make sense to adopt UTF-8 as an excellent default character encoding.
Simple ASCII encoding should usually be avoided, since it's so limited. As well, the default encoding
used by the Servlet specification is ISO-8859-1, which is restricted to West European languages.
In a web application, character encodings are used in three separate areas - the browser, the server, and
the database. To work together correctly, the same character encoding must be used in each of these
areas. See this excellent article by John O'Connor for further discussion.
Browser
The browser uses an encoding to present text to the user, and to send requests (often with parameters) to
the server. The request parameter encoding will be the same as the page encoding, unless instructed
otherwise. A JSP can instruct the browser on the desired encoding by using a page directive, such as:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

META tags may be used instead:


<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

As usual, such a policy should be defined in one place, if possible (for example, in a template page).
Server
In principle, the browser should respond to the server by including the character encoding it has already
received from the server. In practice, however, browsers do not do a very good job at this. So, even
though a JSP has indicated the character encoding, it's likely a good practice to "reset" the character
encoding of the request, using, for example :
30

Collected Java Practices

request.setCharacterEncoding("UTF-8");

AController could perform this for every incoming request, perhaps using a value configured
inweb.xml. This method must be called early in processing, before any parameter values are retrieved.
For reference, the servlet API has these methods for managing character encoding :
ServletRequest.getCharacterEncoding
ServletRequest.setCharacterEncoding
ServletResponse.setLocale
ServletResponse.setContentType

Database
The database has a character encoding as well. Please consult your database documentation for further
information.
Stated Encoding Must Match Actual Encoding
It's important to note that such encoding settings don't define the encoding as such, in the sense that they
don't define or change how the text is actually represented as bytes. Rather, they act simply as advice to
tools, by stating what the encoding is supposed to be. Of course, it's an error to advise a tool that the
encoding is X when it's actually Y.
A good example of this is the encoding of Java Server Pages. If a JSP is saved as ISO-8859-1, and its
page directive states that its encoding is UTF-8, then that's a mistake. Such mistakes are very easy to
make, since the error is not detected until later, when you see weird characters in your web page. Thus,
you need to pay attention to which encoding is in effect when you save such files on your system.
Unfortunately, many systems are not set up to use UTF-8 as the default.
See Also :
Beware of Byte Order Marks

Prevent self-linking
It's often the case that a web page links to itself. This can be frustrating for the user, especially if the page
takes a long time to load.
Instead of self-linking, a page can suppress links to itself, and at the same time highlight the link text in
some way, to clearly indicate the current page. The user is able to painlessly answer the common question
"Where am I on this site?", at the same time as they are prevented from following a link that is usually
useless. Such a style clearly has more compassion for the user's experience.
There are two ways to avoid self-linking in a set of navigation links :
copy and paste the set of navigation links across all pages, and manually alter each possible
occurrence of self-linking.
define the set of navigation links in one place, and create some means of dynamically suppressing
self-linking when generating each page. This is the preferred style, since it defines the menu in a
single place.
For the dynamic style, there is no standard way to define how such links are suppressed. An
implementation might rely on a naming convention, for example, to identify self-linking items that
31

Collected Java Practices

should be altered in some manner.


Example
This example uses WEB4J's HighlightCurrentPage custom tag to dynamically avoid self-linking.
The following JSP snippet appears in a template, and displays a small navigation bar. At runtime, one
link in the navigation bar is suppressed, and shows only plain text instead of the full link. In addition, it
highlights the text (using a CSS class) to render it more obvious as the current page. This is all
implemented with <w:highlightCurrentPage> . The tag uses one of two policies to identify links that are
to be altered:
matching the current URI to the end of the link's HREF target (this the default style)
matching the textual body of the link to a substring of the TTitle request parameter used by the
WEB4J templating mechanism
<div class="menu-bar">
<w:highlightCurrentPage styleClass='highlight'>
<c:url value="/main/home/HomePageAction.do" var="homeURL"/>
<A href='${homeURL}'>Home</a>
<c:url value="/main/rsvp/RsvpShow.do" var="showRsvpURL"/>
<A href='${showRsvpURL}'>Rsvp</a>
<c:url value="/main/visit/VisitAction.do?Operation=List" var="visitEditURL"/>
<A HREF='${visitEditURL}'>Visits</A>
<c:url value="/main/rating/RatingAction.do?Operation=List" var="ratingEditURL"/>
<A HREF='${ratingEditURL}'>Ratings</A>
<c:url value="/all/logoff/LogoffAction.do" var="logoffURL"/>
<A href='${logoffURL}'>Log Off</a>
</w:highlightCurrentPage>
</div>

See Also :
Web usability guidelines
A Web App Framework - WEB4J

Refactor large Controllers


Implementations of HttpServlet are sometimes informally called Controller classes. They form the
entry point of client HTTP requests into a web app. Controller classes should be fairly compact, and
their code should in general be at a high level of abstraction. Controller classes do a lot of work, but
most of their work should be performed by delegating to other classes. A well designed application will
have a Controller which is not excessively lengthy. If you use a framework, the Controller class is
often defined as part of the framework, and not by your application.
Examples of tasks which can be delegated by the Controller to another class include:
translating raw user requests into problem domain objects
logging user request statistics
validating user input
interacting with the database
ensuring necessary objects are in scope for subsequent display
forwarding to a JSP for display to the user
handling any exceptions thrown by the application
32

Collected Java Practices

Example
Here is part of the Controller from the WEB4J framework. It delegates most of its work to:
a RequestParser , a higher level wrapper around the underlying request
an implementation of an Action interface
a ResponsePage, to represent the final page served to the user
an ApplicationFirewall , to verify basic sanity of the request
a TroubleTicket , to gather diagnostic information, and email it to the webmaster

/**
* Controller that delegates to various framework classes.
*/
public class Controller extends HttpServlet {
//..elided
/** Call {@link #processRequest}. */
@Override public final void doGet(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
processRequest(aRequest, aResponse);
}
/** Call {@link #processRequest}. */
@Override public final void doPost(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
processRequest(aRequest, aResponse);
}
/**
* Handle all HTTP <tt>GET</tt> and <tt>POST</tt> requests.
*
* <P>This method can be overridden, if desired. Most applications will not need
* to override this method.
*
* <P>Operations include :
* <ul>
* <li>set the request character encoding to the value configured in <tt>web.xml</tt>
* <li>get an instance of {@link RequestParser}
* <li>get its {@link Action}, and execute it
* <li>perform either a forward or a redirect to its
* {@link hirondelle.web4j.action.ResponsePage}
* <li>if an unexpected problem occurs, create a {@link TroubleTicket}, log it, and
* email it to the webmaster email address configured in <tt>web.xml</tt>
* <li>if the response time exceeds a configured threshold, build a
* {@link TroubleTicket}, log it, and email it to the webmaster address configured
* in <tt>web.xml</tt>
* </ul>
*/
protected void processRequest(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
RequestParser requestParser = RequestParser.getInstance(aRequest, aResponse);
try {
Action action = requestParser.getWebAction();
ApplicationFirewall appFirewall = BuildImpl.forApplicationFirewall();
appFirewall.doHardValidation(action, requestParser);
ResponsePage responsePage = action.execute();
if ( responsePage.getIsRedirect() ) {
redirect(responsePage, aResponse);
}
else {
forward(responsePage, aRequest, aResponse);
}
}
catch (BadRequestException ex){
33

Collected Java Practices

if( Util.textHasContent(ex.getErrorMessage()) ){
aResponse.sendError(ex.getStatusCode(), ex.getErrorMessage());
}
else {
aResponse.sendError(ex.getStatusCode());
}
}
catch (Throwable ex) {
//Bugs OR rare conditions, for example datastore failure
logAndEmailSeriousProblem(ex, aRequest);
aResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
stopwatch.stop();
if ( stopwatch.toValue() >= fPOOR_PERFORMANCE_THRESHOLD ) {
logAndEmailPerformanceProblem(stopwatch.toValue(), aRequest);
}
}
/**
* Inform the webmaster of an unexpected problem with the deployed application.
*
* <P>Typically called when an unexpected <tt>Exception</tt> occurs in
* {@link #processRequest}. Uses {@link TroubleTicket#mailToWebmaster}.
*
* <P>Also, stores the trouble ticket in application scope, for possible
* later examination.
*/
protected final void logAndEmailSeriousProblem (
Throwable ex, HttpServletRequest aRequest
) throws AppException {
TroubleTicket troubleTicket = new TroubleTicket(ex, aRequest);
fLogger.severe("TOP LEVEL CATCHING Throwable");
fLogger.severe( troubleTicket.toString() );
log("SERIOUS PROBLEM OCCURRED.");
log( troubleTicket.toString() );
aRequest.getSession().getServletContext().setAttribute(
MOST_RECENT_TROUBLE_TICKET, troubleTicket
);
troubleTicket.mailToWebmaster();
}
/**
* Inform the webmaster of a performance problem.
*
* <P>Called only when the response time of a request is above the threshold
* value configured in <tt>web.xml</tt>.
*
* <P>Builds a <tt>Throwable</tt> with a description of the problem, then creates and

* emails a {@link TroubleTicket} to the webmaster.


*
* @param aMilliseconds response time of a request in milliseconds
*/
protected final void logAndEmailPerformanceProblem(
long aMilliseconds, HttpServletRequest aRequest
) throws AppException {
String message =
"Response time of web application exceeds configured performance threshold."
+ NEW_LINE +
"Time : " + aMilliseconds + " milliseconds."
;
Throwable ex = new Throwable(message);
TroubleTicket troubleTicket = new TroubleTicket(ex, aRequest);
fLogger.severe("Poor response time : " + aMilliseconds + " milliseconds");
fLogger.severe(troubleTicket.toString());
log("Poor response time : " + aMilliseconds + " milliseconds");
log(troubleTicket.toString());
troubleTicket.mailToWebmaster();
}
//..elided
}

34

Collected Java Practices

See Also :
Use Model-View-Controller framework
Parse parameters into domain objects
Command objects
Send trouble-ticket emails
A Web App Framework - WEB4J

Repel invalid requests


The Open Web Application Security Project has practical guidelines for implementing a secure web site.
The first item on their list of security concerns is validating requests.
A reasonable approach is to first validate all requests before performing any other processing. Such
checks can include:
check for requests whose overall size is unreasonably large (some attacks send requests with large
payloads, in an attempt to overload the server)
check for unknown parameter names
"sanity checks" for unreasonable parameter values, not expected during normal operation (for
example, text of unreasonably large size, or a checkbox taking an unexpected value)
Early in processing, sanity checks on parameter values may be either complete or partial validations:
complete - for example, items presented to the user in a static drop down list, under normal
operation, will take only the values defined by the web application. Any other value constitutes an
invalid request (almost always a hack) which may be given a short, unpolished response, perhaps
in static HTML.
partial - for example, a free form text area can be checked for size, but not for detailed content. As
a second example, a business identifer can be checked for textual form, but validating it against the
datastore is not appropriate at this early stage in processing
Checks on parameter values might be performed at two stages in processing - early sanity checks (as
described above), and later "business" validations. For example, if an Age is typed into a text input
control, the parameter value can be validated in two steps:
first, validate the input can indeed build an Integer. This validation might be performed on the
application's behalf by a framework which defines reasonable policies for converting text into an
Integer , Date , BigDecimal , and so on.
second, validate the Integer is, say, in the range 0..150. This sort of validation can only be
performed by an application, not by a framework.
This two-step validation style is used in the WEB4J framework. In WEB4J, business validations are
performed by a Model Object constructor.
See Also :
Parse parameters into domain objects
A Web App Framework - WEB4J

35

Collected Java Practices

Reuse login page for errors


The Servlet API defines a form-based login mechanism, which allows you to define a login page and an
associated login error page. Here is an example of such an entry in web.xml:
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/Login.jsp</form-login-page>
<form-error-page>/LoginError.jsp</form-error-page>
</form-login-config>
</login-config>

It's useful to note that you don't have to specify two different pages.
For example:
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/Login.jsp</form-login-page>
<form-error-page>/Login.jsp?Retry=True</form-error-page>
</form-login-config>
</login-config>

That is, the same page can be reused for login errors. In the presence of the Retry parameter, the
Login.jsp will display a simple error message.
Here is a snippet from a Login.jsp which uses this style:
<form method="POST" action='<%= response.encodeURL("j_security_check") %>'>
<table align="center">
<c:if test='${not empty param["Retry"]}'>
<tr>
<td colspan='2' align='center'><b>Please try again.</b></td>
</tr>
<tr>
<td>&nbsp;</td>
</tr>
</c:if>
<tr>
<td><label>Name</label></td>
<td><input type="text" name="j_username"></td>
</tr>
<tr>
<td><label>Password</label></td>
<td><input type="password" name="j_password"></td>
</tr>
<tr align="center">
<td colspan="2"><input type="submit" value="Login"></td>
</tr>
</table>
</form>

For security reasons, many recommend not giving specific error information. For example, stating
explicitly that the password is incorrect is undesirable, since that information is useful to hackers.
For similar reasons, when displaying an error it's likely best not to repeat the user's original input, and to
leave the login form blank.

Send trouble-ticket emails


In web applications, sending email is a common task.
36

Collected Java Practices

Example
Here's an example of a utility class which sends simple emails. It can also validate an email address. It
uses an entry in web.xml for an SMTP host name.
import javax.servlet.ServletConfig;
import java.util.Properties;
import
import
import
import
import
import
import

javax.mail.Session;
javax.mail.Transport;
javax.mail.Message;
javax.mail.MessagingException;
javax.mail.internet.InternetAddress;
javax.mail.internet.AddressException;
javax.mail.internet.MimeMessage;

/**
* Send simple email from the webmaster to a receiver.
*
* <P>The emails sent by this class include sender, receiver, subject, and
* body. No features such as attachments are supported by this class. If more
* flexibity is needed, please use {@link javax.mail} directly.
*/
public final class Mailer {
/**
* Called upon startup, to allow this class to extract mail server
* config information from <code>web.xml</code>.
*/
public static void init(ServletConfig aConfig){
fConfig = aConfig;
}
/**
* Validate the form of an email address.
*
* <P>Return <code>true</code> only if
*<ul>
* <li> <code>aEmailAddress</code> can successfully construct an
* {@link javax.mail.internet.InternetAddress}
* <li> when parsed with a "@" delimiter, <code>aEmailAddress</code> contains
* two tokens which have content.
*</ul>
*
*<P> The second condition arises since local email addresses, simply of the form
* "<tt>albert</tt>", for example, are valid but almost always undesired.
*/
public static boolean isValidEmailAddress(String aEmailAddress){
if (aEmailAddress == null) return false;
boolean result = true;
try {
InternetAddress emailAddr = new InternetAddress(aEmailAddress);
if (! hasNameAndDomain(aEmailAddress)) {
result = false;
}
}
catch (AddressException ex){
result = false;
}
return result;
}
/**
* Send an email to a single recipient, using a hard-coded Webmaster email address.
*
* @param aToAddress satisfies {@link #isValidEmailAddress}.
* @param aSubject has content.
* @param aBody has content.
*/
public void send(String aToAddress, String aSubject, String aBody){
if (!textHasContent(aSubject) || !textHasContent(aBody)) {
throw new IllegalArgumentException("Must have content.");
}
if (isMailDisabled()){
37

Collected Java Practices

log("Mailing is disabled.");
return;
}
Session session = Session.getDefaultInstance( getMailServerConfig(), null );
MimeMessage message = new MimeMessage( session );
try {
message.setFrom(new InternetAddress("admin@blah.com"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(aToAddress));
message.setSubject(aSubject);
message.setText(aBody);
Transport.send(message); //thread-safe?
}
catch (MessagingException ex){
log("CANNOT SEND EMAIL." + ex);
}
log("Mail is sent.");
}
// PRIVATE
private static ServletConfig fConfig;
/**
* Identifies the item in web.xml which contains the configured
* mail server.
*/
private static final String fMAIL_SERVER = "MailServer";
/**
* Identifies the lack of any configured mail server in web.xml.
* If fMAIL_SERVER takes this value, then mailing is disabled, and
* this class will abort all attempts to send email.
*/
private static final String fNONE = "NONE";
private String getMailServer(){
return fConfig.getServletContext().getInitParameter(fMAIL_SERVER);
}
private boolean isMailDisabled(){
return getMailServer().equalsIgnoreCase(fNONE);
}
/**
* Return the configured mail server in the form of a Properties object.
*/
private Properties getMailServerConfig(){
Properties result = new Properties();
result.put("mail.host", getMailServer());
return result;
}
private static boolean hasNameAndDomain(String aEmailAddress){
String[] tokens = aEmailAddress.split("@");
return
tokens.length == 2 &&
textHasContent(tokens[0]) &&
textHasContent(tokens[1])
;
}
private static void log(Object aObject){
System.out.println(String.valueOf(aObject));
}
private static boolean textHasContent(String aText){
return aText != null && aText.trim().length()>0;
}
}

A common type of email is a "trouble ticket", sent to the webmaster when a problem occurs in a
deployed application. Such an email provides a detailed listing of information which may help to resolve
38

Collected Java Practices

the problem. A TroubleTicket class would gather together such information, and a class similar to the
above Mailer would be used to send it to the webmaster.
Example
Here, a Controller creates and sends a TroubleTicket if
an unexpected Exception is thrown (a bug occurs)
the response time exceeds some configured level
This implementation uses a setting in web.xml for throttling down on excessive numbers of similar
trouble tickets.
/**
* Servlet Controller.
* Sends email to webmaster when problem occurs, or when response time
* exceeds a configured value.
*/
public class Controller extends HttpServlet {
@Override public final void init(ServletConfig aConfig) throws ServletException {
//..elided
TroubleTicket.init(aConfig, appInfo);
}
/** Call {@link #processRequest}. */
@Override public final void doGet(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
processRequest(aRequest, aResponse);
}
/** Call {@link #processRequest}. */
@Override public final void doPost(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
processRequest(aRequest, aResponse);
}
/**
* Handle all HTTP <tt>GET</tt> and <tt>POST</tt> requests.
*
* <P>Operations include :
* <ul>
* <li>if an unexpected problem occurs, create a {@link TroubleTicket}, log it, and
* email it to the webmaster email address configured in <tt>web.xml</tt>
* <li>if the response time exceeds a configured threshold, build a
* {@link TroubleTicket}, log it, and email it to the webmaster address configured
* in <tt>web.xml</tt>
* </ul>
*/
protected void processRequest(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
RequestParser requestParser = RequestParser.getInstance(aRequest, aResponse);
try {
Action action = requestParser.getWebAction();
ResponsePage responsePage = action.execute();
//..elided
}
catch (BadRequestException ex){
//..elided
}
catch (Throwable ex) {
//Bugs OR rare conditions, for example datastore failure
logAndEmailSeriousProblem(ex, aRequest);
aResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
}
stopwatch.stop();
39

Collected Java Practices

if (stopwatch.toValue() >= fPOOR_PERFORMANCE_THRESHOLD) {


logAndEmailPerformanceProblem(stopwatch.toValue(), aRequest);
}
}
/**
* Inform the webmaster of an unexpected problem with the deployed application.
*
* <P>Typically called when an unexpected <tt>Exception</tt> occurs in
* {@link #processRequest}. Uses {@link TroubleTicket#mailToWebmaster}.
*
* <P>Also, stores the trouble ticket in application scope, for possible
* later examination.
*/
protected final void logAndEmailSeriousProblem (
Throwable ex, HttpServletRequest aRequest
) throws AppException {
TroubleTicket troubleTicket = new TroubleTicket(ex, aRequest);
fLogger.severe("TOP LEVEL CATCHING Throwable");
fLogger.severe( troubleTicket.toString() );
log("SERIOUS PROBLEM OCCURRED.");
log( troubleTicket.toString() );
aRequest.getSession().getServletContext().setAttribute(
MOST_RECENT_TROUBLE_TICKET, troubleTicket
);
troubleTicket.mailToWebmaster();
}
/**
* Inform the webmaster of a performance problem.
*
* <P>Called only when the response time of a request is above the threshold
* value configured in <tt>web.xml</tt>.
*
* <P>Builds a <tt>Throwable</tt> with a description of the problem, then creates and

* emails a {@link TroubleTicket} to the webmaster.


*
* @param aMilliseconds response time of a request in milliseconds
*/
protected final void logAndEmailPerformanceProblem(
long aMilliseconds, HttpServletRequest aRequest
) throws AppException {
String message =
"Response time of web application exceeds configured performance threshold."
+ NEW_LINE +
"Time : " + aMilliseconds + " milliseconds."
;
Throwable ex = new Throwable(message);
TroubleTicket troubleTicket = new TroubleTicket(ex, aRequest);
fLogger.severe("Poor response time : " + aMilliseconds + " milliseconds");
fLogger.severe( troubleTicket.toString() );
log("Poor response time : " + aMilliseconds + " milliseconds");
log( troubleTicket.toString() );
troubleTicket.mailToWebmaster();
}
}

See Also :
Refactor large Controllers
A Web App Framework - WEB4J
Launch other applications

Serve binary content


40

Collected Java Practices

Servlets usually serve some form of text - HTML, XML, JSON data, plain text, and so on. Servlets can
also serve binary content, such as images or .pdf files.
Here's an example of a simple servlet that serves binary content. It serves a .pdf file of a fixed name,
located in the web application's root directory.
import java.io.*;
import
import
import
import
import

javax.servlet.ServletException;
javax.servlet.ServletOutputStream;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;

/**
Servlet that serves binary data, not text.
<P>This servlet serves a static .pdf file, but the idea is the same with other
forms of data.
*/
public class ServeBinary extends HttpServlet {
@Override protected void doGet(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
serveBinary(aRequest, aResponse);
}
@Override protected void doPost(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws ServletException, IOException {
serveBinary(aRequest, aResponse);
}
// PRIVATE
//elided...
private void serveBinary(
HttpServletRequest aRequest, HttpServletResponse aResponse
) throws IOException {
log("ServeBinary servlet.");
//IMPORTANT: set the MIME type of the response stream
aResponse.setContentType("application/pdf");
//serve a fixed file, located in the root folder of this web app
try (
ServletOutputStream output = aResponse.getOutputStream();
InputStream input = getServletContext().getResourceAsStream("test.pdf");
){
//transfer input stream to output stream, via a buffer
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}
log("Done.");
}
}

Session-scope objects often Serializable


41

Collected Java Practices

Although not required, it's sometimes helpful if objects placed in session scope implement
Serializable . Some would do this only if necessary, since implementing Serializable is not trivial.
This allows the servlet container to either store the contents of a session on disk, or to transfer session
contents over the network to another server.
In the case of a restart, the web container may attempt to implement a "failover" strategy by attempting to
serialize all data stored in session scope, in order to recover the data after the restart has completed - this
will work only if such data implementsSerializable.
See Also :
Implementing Serializable
Loss of data during restart

Struts seems mediocre


The Struts 1 front end framework was one of the first java web application tools to be published, and at
one time it was widely used.
(Calling Struts an "application framework" is inaccurate. Struts assists the developer in building only the
front end of an application. It does nothing for the model layer or the database layer. It's not a full-stack
framework.)
It can be argued with some force that Struts 1 is a mediocre tool. Here are some reasons why.
No policies for translating request parameters into common java objects.
All java web applications need to translate raw request parameter values into common items such as
and BigDecimal . However, Struts 1 provides no support for this task. It does not define
reasonable default policies, nor does it encourage the user to define such policies in any way.
Integer , Date ,

ActionForms are a massive parallel structure to the "real" model.


The ActionForm classes are not intended as the actual model. They are intended as a preliminary
validation buffer between the raw HTTP request and the "real" model class. (For example, an
ActionForm might validate that a request parameter can be parsed into an Integer , whereas the related
model class might validate that the Integer is within the range 1..10 .) ActionForm s include only
String and boolean items, and nothing else. Since ActionForm s and model classes exist as a pair, this
creates massive unnecessary parallelism.
This point is closely related to the first one. If centrally defined policies for translating request parameters
into Integer, Date , BigDecimal , and so on, existed, then model objects could indeed be constructed out
of request parameters more or less directly.
Furthermore, even though Struts 1 requires that model objects be constructed out of related ActionForm s,
it gives no direct support to the programmer for performing this task.
Data validation is separated from the Model Object.
A fundamental idea in object programming is to unite data with related actions. Typical business
applications are excellent for illustrating this idea, since business data usually has many related
42

Collected Java Practices

validations. Thus, the Model Object should typically encapsulate both data and validations. In Struts 1,
however, data validation is completely separated from the Model Object. Why? Why have the creators
of Struts 1 lost sight of such a fundamental, basic idea in object programming? It does not seem to have
any justification.
Standard HTML forms are not used.
In Struts 1, forms usually depart widely from standard HTML, and are usually implemented with a
specific set of custom tags. This is neither necessary nor desirable. It's not necessary since other tools
have demonstrated that dynamically populated forms can simply reuse standard HTML forms as part of
their implementation. It's not desirable since the page author cannot reuse their knowledge of standard
HTML, but is instead forced to learn a new set of custom tags specific to Struts 1. This is unproductive.
Actions must be thread-safe.
Actions are always cached and reused by Struts 1. This is both a performance optimization and a
constraint, since it requires Actions to be safe for use in a multi-threaded environment.

As a performance optimization, this policy is of dubious quality. The stack traces of a web container are
large. If an Action is "just another object", it's of very little benefit to cache and reuse it. On the other
hand, designs in which an Action holds a reference to an expensive object (such as a database
connection) are often inferior.
As a constraint, Joshua Bloch explains clearly in Effective Java that ensuring a class is usable in a multithreaded environment is a non-trivial task. Forcing all Action implementations to be thread-safe seems
an onerous requirement having doubtful benefit. For example, in a Struts 1 Action, data shared between
methods cannot be stored in a field; rather, it must be passed as a parameter.
Struts 1 is an unimpressive implementation of the Command pattern.
The execute method has four arguments, which must always be passed in, regardless of whether or not
the argument is actually relevant to a particular action. In the Design Patterns book, the number of
arguments to the execute method is exactly zero. There is a good reason for this, and it's illustrated by
almost every design pattern in the Gang Of Four book: the central methods of any design are free of
implementation details. Data needed by the implementation of an abstraction is almost always passed to a
constructor. This keeps the abstractions clean, and places data specific to the implementation in a place
where inheritance and polymorphism do not apply (the constructor).
In addition, Struts 1 always requires a cast to be performed in an Action, to get access to a specific
ActionForm . Casts are something to be avoided unless absolutely necessary, since they lead to runtime
errors.
The web4j tool was built out of strong dissatisfaction with the Struts 1 front end framework.
See Also :
Parse parameters into domain objects
Command objects
Pre-populate forms
A Web App Framework - WEB4J

Thread-safety in web apps


43

Collected Java Practices

In a web application, you should verify that the following items are safe for operation in a multi-threaded
environment:
servlet classes
servlet filter classes
items placed in application scope
items placed in session scope
Many applications use a framework. Frameworks usually define the servlet class, and the thread-safety of
those servlets will be explicitly stated by a well-documented framework. Many frameworks require the
application developer to define actions for implementing features. In this case, the framework owns the
servlet, and the servlet in turn uses your action objects. A well-designed framework will create your
action objects on the fly, on the single thread assigned to handle a single request. This in turn means that
your actions will be confined to a single thread; thus, in this case, your action classes will have no need
for thread-safety.
The most glaring example of a framework which violates this rule is Struts 1. In Struts 1, your actions
must be designed for operation in a multi-threaded environment. This is a poor design for a framework,
since ensuring a class is thread-safe is both non-trivial to implement and easy to forget. This is an
onerous and unnecessary burden for an application developer.
In the case of servlet filters, a framework is not usually used when defining them. In this case, you have
to be careful that your servlet filter class is indeed safe for operation in a multi-threaded environment.
For objects placed in either application scope or session scope, the simplest design is to ensure that the
object is immutable, such that external synchronization is never necessary.
It's clear to most people that objects placed in application scope will be accessed by more than one
thread. However, even objects placed in session scope can also be used by more than one thread. From
the Servlet Specification:
"Multiple servlets executing request threads may have active access to a single session object at the same
time. The Developer has the responsibility for synchronizing access to session resources as appropriate."
Here are some ways in which multiple threads might access the same session:
web apps containing multiple servlets can share access to the same session
Ajax requests from a single client
multiple browser windows on a single client
repeated clicks on a submit button
using HTML frames to request multiple pages at the same time

See Also :
Immutable objects
Document thread safety

Understand details of FORM tags


44

Collected Java Practices

When building web applications, you should have an excellent understanding of how <FORM> tags work.
In particular, there are a number of quirky behaviors that should be noted. When in doubt, you can verify
form behavior in various browsers. Since these quirks are often related to browser implementations, and
not to the HTML specfication itself, these quirks may change over time.
SELECT Controls
Even if the user has made no selection at all in a <SELECT> control, browsers will automatically submit
the first <OPTION> appearing in the body of a <SELECT> . As a workaround, it's often useful to ensure that
the first <OPTION> is a blank value, instead of a 'real' one.
File Upload Controls
Curiously, there is apparently no attribute to specify the text of the button used for file upload controls.
The text is apparently deduced by the browser from language settings on the client, and not by settings in
HTML or HTTP headers.
Reminder: if a form includes a file upload control, then it must have :
method='POST'
enctype='multipart/form-data'

Reminder: the Servlet API 2.5 has poor support for file upload requests. As soon as a form includes a file
upload control, the format of the underlying request is completely changed. In particular, if no special
measures are taken, none of the form's data will be available from request.getParameter(String) ; this
applies to all of the form's data, both file upload controls and regular controls.
Tools such as Apache Commons FileUpload are often used to handle such forms. Also note that support
for file upload requests has been added to version 3.0 of the Servlet Specification.
Submit Controls
It's often desirable to distinguish between the appearance of a control, as seen by the end user, and the
actual underlying value submitted to the server. (This is particularly true in multilingual applications.)
This is indeed possible for all controls, except for the Submit control, whose value attribute is used both
for its visual appearance and for its submitted value.
This is a problem in multilingual applications where forms have more than one submit button (further
discussion).
Checkbox Controls
When a checkbox control is checked, its name and value are submitted to the server. If the checkbox
control is not checked, then it is not submitted at all. That is, the server has two possible tasks: if the
request parameter is present, then access its value. If the request parameter is not submitted at all, then
the server must assume some default value such as 'null' or 'false'.
Disabled Controls and Read-Only Controls
The readonly and disabled attributes are distinct, and have different behavior. The most important
distinction is that readonly items are submitted, while disabled items are not.
45

Collected Java Practices

When rendering reports or listings that include boolean values, it is tempting to use a disabled checkbox.
However, some feel that such disabled checkboxes don't have a very pleasing appearance. One alternative
is to use an image instead of a checkbox control, to present the boolean value. (One might even use an
image of a checkbox.)
Hitting Enter to Submit
Forms are usually submitted by hitting a Submit button. However, forms can often be submitted just by
hitting the 'Enter' key. This behavior is commonly used for web site search boxes.
Be careful that the server can handle both styles of submission - Enter key and Submit button.
TEXTAREA Controls
You often need to exercise care regarding how new lines are handled by your <TEXTAREA> controls. The
non-standard wrap attribute can often be added to a TEXTAREA to define a desired policy for new line
characters. Its values are :
wrap=(virtual|physical|off)

Reminder - POST Versus GET


If an operation has a side-effect (an edit to the database, for example), then method='POST' is highly
recommended. If an operation has no side-effect (a list or search operation, for example), then
method='GET' is highly recommended.
See Also :
Beware multilingual submit buttons
Wrap file upload requests

Use Cascading Style Sheets


Cascading Style Sheets (CSS) centralize decisions on how web pages are styled - fonts, colors, layouts,
and so on.
Some advantages of using CSS:
as usual, centralized policies greatly reduce maintenance effort
many visual effects offered by CSS cannot be produced by simple HTML
file download times improve slightly, since repeated markup is eliminated
pleasing designs are easy to produce
Support for CSS varies among browsers. Some care must be taken that pages render adequately in the
most popular ones.
Some recommendations on using CSS:
name items at a high level of abstraction, without exposing implementation details. For example, a
good name for a style class is highlight , and a bad name is bold-and-yellow
to maximize reuse, prefer external style sheets over styles defined in a single HTML page
46

Collected Java Practices

name style sheets with a .css extension, to ensure they work with tools which may incorrectly
require that extension
if using pseudo classes for links, use a:link and a:visited as a pair
for font sizes, usually prefer em or % (relative), over ex and px (absolute)
the font-size styles of xx-small , x-small , and so on, are not consistent across browers, and
should likely be avoided, as a kind of absolute measure.
when referring to specific fonts, always include a generic font family to be used as an alternative:
serif , sans-serif , monospace
some recommend that !important should be avoided
favor techniques which minimize the amount of markup
use the CSS Specification as reference, and use the CSS Validator to verify your work
Reminders:
. denotes a class
# denotes an id
: denotes a pseudo-class, as in a:visited
[] denotes attribute selectors

according to the specification, class and id names are case sensitive


element box = content area + padding + border + margin (in order from middle outwards)
the width of an item refers only to the content area, and not the whole element box
the LINK tag is placed in the HEAD tag
a typical LINK :
<link rel="stylesheet" type="text/css" href="styles.css" media="all" >

Be Aware of Browser Caches


Browsers cache style sheets. If an update to a web site includes changes to a style sheet, then the
developer will often need to manually refresh the browser's cache, in order to see the updates (the
specifics apparently depend on the browser). However, when deploying such an update, it's not acceptable
to expect the end user to do the same thing.
To ensure that all users see the updated version of a stylesheet, there's a simple workaround : just rename
the style sheet. Of course, related references need to reflect the new name as well. This will force the
browser to always load the updated version.

Example style sheet


/* Color scheme comes first. */
body {
background-color: rgb(255,255,255);
}
a {
color: rgb(0,100,26);
}
a:hover {
background-color:rgb(164,255,158);
}
a:visited {
color: rgb(0,150,26);
}
.opening-quote{
background-color: rgb(85%, 85%, 85%);
border-top: 1px solid rgb(50%,50%,50%);
border-left: 5px solid rgb(50%,50%,50%);
border-right: 5px solid rgb(50%,50%,50%);
border-bottom: 1px solid rgb(50%,50%,50%);
}
img.photo {
border-color: rgb(50%,50%,50%);
47

Collected Java Practices

}
.highlight {
background-color: #ffff66;
}
.sidebar{
background-color: rgb(85%, 85%, 85%);
border-top: 1px solid rgb(50%,50%,50%);
border-left: 5px solid rgb(50%,50%,50%);
border-right: 5px solid rgb(50%,50%,50%);
border-bottom: 1px solid rgb(50%,50%,50%);
}
/* End of the color scheme. */
body {
margin: 0;
padding:0;
font: 1.0em Verdana, Arial, Helvetica, sans-serif;
}
a {
font-weight: bold;
text-decoration: none;
}
h2{
font: bold 20px Verdana, Arial, Helvetica, sans-serif;
border-bottom: 1px solid;
}
h3{
font: bold 16px Verdana, Arial, Helvetica, sans-serif;
}
h4{
font: bold 16px Verdana, Arial, Helvetica, sans-serif;
}
blockquote.abstract{
padding: 0.3em;
}
ul {
list-style-type: square;
}
tr {
vertical-align: top
}
/*
Tables used for user input.
*/
form.user-input table {
background-color: rgb(83%, 83%, 83%);
border-style: solid;
border-width: 2px;
border-color: rgb(45%,45%,45%);
padding: 1.0em;
}
/* improves alignment of form controls */
form.user-input input {
margin: 0;
}
/*
REPORTS
Here, reports are implemented with tables, and refer to any kind of listing.
*/
table.report {
background-color: rgb(83%, 83%, 83%);
border-style: solid;
border-width: 2px;
border-color: rgb(45%,45%,45%);
border-collapse: collapse;
empty-cells: show;
caption-side: bottom;
}
table.report td, th {
/*white-space: nowrap;*/
48

Collected Java Practices

border: 1px ridge rgb(65%,65%,65%);


padding: 0.30em;
}
/* no underline for sorting links */
table.report th a {
text-decoration: none;
}
table.report th img {
padding: 0;
margin: 0;
}
table.report tbody {
border: 1px solid black;
}
/*
Highlighting the row when the cursor hovers above it
increases legibility.
*/
table.report tr:hover {
background-color: #FFFDE3;
}
table.report caption {
font-weight: bold;
text-align: center;
padding: 0.5em;
}
table.report caption:after {
content : " - ";
}
table.report caption:before {
content : " - ";
}
/*
MESSAGES of various kinds.
*/
.message {
font-weight: bolder;
}
.warning {
font-weight: bolder;
}
.error {
font-weight: bolder;
color: rgb(255,0,0);
}
p.display-messages {
text-align: center;
}
img.photo {
margin-top:1.0em;
margin-bottom:1.0em;
margin-left:1.0em;
margin-right:1.0em;
border-style: solid;
border-width: 2px;
}
img.photo-home {
margin: 0;
border-style: solid;
border-width: 2px;
float: left;
}
/* The top banner */
#header{
text-align:center;
padding-top:1.70em;
}
.loud {
font-size: 150%;
padding: 1.0em;
}
#bodycontent{
margin-left: 0px;
49

Collected Java Practices

margin-right: 0px;
}
#nav-menu{
padding-top:18px;
padding-left:15px;
width:120px;
float:left;
display:block;
}
#nav-menu ul {
list-style-type: none;
display:block;
padding: 0;
margin: 0;
}
#nav-menu li {
text-decoration: none;
display:block;
}
#nav-menu li a {
padding-left: 7px;
padding-top: 6px;
height: 22px;
/* width: 113px; */
width: 70px;
display: block;
text-decoration: none;
font-weight: bold;
}
/* Search form in menu */
#nav-menu li form {
padding-left: 7px;
padding-top: 6px;
height: 22px;
width: 113px;
font-weight: bold;
}
#content{
/* margin-left: 135px; */
margin-left: 100px;
padding-left:10px;
padding-right:10px;
padding-top:1px;
}
#contentdisabled{
margin: 10px;
padding:10px;
}
.maincopy a:hover {
text-decoration: underline;
}
#textcontent{
height:100%;
padding-left:0;
padding-right:15px;
}
div.author {
font-style: italic;
padding-top: 0em;
margin-top: 0.25em;
text-align: right;
}
.opening-quote{
margin: 10px;
padding: 10px;
}
.sidebar{
margin: 10px;
padding: 10px;
50

Collected Java Practices

}
#footer{
text-align:center;
height:24px;
padding-top: 10px;
padding-left: 0px;
font: 11px Verdana, Arial, Helvetica, sans-serif;
padding-right: 10px;
border-right-width: 0px;
border-bottom-width: 0px;
border-left-width: 0px;
}
.small-graphic{
float:left;
margin-right:0.2em;
}
/* sometimes footer is too high on page */
div.spacer {
height: 14.0em;
}
div.smallspacer {
height: 7.0em;
}
div.bigspacer {
height: 30.0em;
}
/* Styles for print. */
@media print {
* {
color: black !important;
background: white !important;
}
body {
font-family: "Times New Roman", serif;
font-size: 12pt;
}
a {
text-decoration: none;
}
img.photo {
display: none;
}
div#nav-menu{
display: none;
}
div#header{
display: none;
}
}

Use extensions for fine-grained security


The <security-constraint> item in web.xml implements role-based security restrictions for your web
application. It's <http-method> attribute lets you to specify POST , GET and so on, to restrict what kind of
action is taken.
However, there's a big problem with this technique: browsers typically implement only POST and GET ;
they typically don't implement PUT and DELETE. This means that <http-method> is not very useful, in
practice, for implementing fine-grained security constraints.
(It's important to note that the role-based security restrictions defined by the servlet specification do
nothing for restrictions based on ownership of data, such as seen in many public web sites. Such
restrictions prevent one user from editing items created by some other user, for example.)
51

Collected Java Practices

There is an alternative to using <http-method>: use the extension appearing in the URL. In this case,
URLs take the form:
.../Account.list
.../Account.add
.../Account.delete
.../Account.fetch?Id=45

When thinking of security, it's natural to think in terms of nouns and verbs:
what is being operated on - the noun
what exactly is being done to it - the verb
In the above example, Account is the noun, while the extension ( .list , .add , and so on) is the verb.
With this style, any degree of granularity for security constraints can be implemented. One can mix and
match the nouns and the verbs independently of each other, in a natural way.
Example 1
Only a manager can perform this specific delete operation :
<security-constraint>
<web-resource-collection>
<web-resource-name>Deleting Members</web-resource-name>
<url-pattern>/main/member/MemberAction.delete</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>

Example 2
A reader can read, but not write to the database :
<security-constraint>
<web-resource-collection>
<web-resource-name>View Operations</web-resource-name>
<url-pattern>*.list</url-pattern>
<url-pattern>*.fetch</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>reader</role-name>
</auth-constraint>
</security-constraint>

Example 3
An editor can both read and write to a database :
<security-constraint>
<web-resource-collection>
<web-resource-name>Edit Operations</web-resource-name>
<url-pattern>*.list</url-pattern>
<url-pattern>*.fetch</url-pattern>
<url-pattern>*.add</url-pattern>
<url-pattern>*.change</url-pattern>
<url-pattern>*.delete</url-pattern>
<url-pattern>*.fetchForChange</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>editor</role-name>
</auth-constraint>
</security-constraint>

Example 4

52

Collected Java Practices

Only an webmaster can access URLs starting with /webmaster/* :


<security-constraint>
<web-resource-collection>
<web-resource-name>Webmaster</web-resource-name>
<url-pattern>/webmaster/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>webmaster</role-name>
</auth-constraint>
</security-constraint>

Example 5
Only an megawebmaster can access /webmaster/Logs.delete :
<security-constraint>
<web-resource-collection>
<web-resource-name>Log Deletion</web-resource-name>
<url-pattern>/webmaster/Logs.delete</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>megawebmaster</role-name>
</auth-constraint>
</security-constraint>

Here's a table showing whether access is granted in various cases, given the above constraints:
For User With Role(s)
Accessing URL
Allow Access?
editor

../Account.list

editor

../Account.delete

reader

../Account.list

reader

../Account.delete

editor

/main/member/MemberAction.delete

reader, manager

/main/member/MemberAction.delete

reader

/webmaster/Logs.list

webmaster

/webmaster/Logs.list

webmaster

/webmaster/Logs.delete

Use Model-View-Controller framework


Most web applications use a Model-View-Controller framework. In particular, they usually use what is
called the "Model 2" style, which has these components:
Controller - extends HttpServlet, acts as the point of entry into the application, and delegates to
various worker classes to fulfill a request. In particular, the Controller is a user of Model and View
objects.
Model - data-centric classes encapsulating problem domain objects. Each class corresponds
roughly to the rows of a database table. Model objects can be constructed from a ResultSet of a
database query, or from HTTP request parameters.
View - implemented as Java Server Pages (or a similar tool), primarily concerned with presentation
and formatting of Model objects which have been placed in scope by the Controller (or its
53

Collected Java Practices

delegate).

See Also :
Refactor large Controllers
JSPs should contain only presentation logic
Model Objects

Use templates to standardize layout


Templates are commonly used to enforce a uniform appearance across a set of related pages - often even
for an entire web site.
If using JSPs, templates can be easily implemented using either the <@include> directive or the
<jsp:include> action.
Example
Here, a template JSP defines the layout. It includes a header, footer, and navigation links. A
<jsp:include> inserts the main content or "body" of the page. There are two parameters to the template,
which are passed in as request parameters :
TTitle, used in the <title> tag
TBody , which is simply the name

of the JSP which supplies all page content not otherwise defined

in the template.
The <tags:xxx/> items refer to .tag files, that contain small JSP snippets.
<%@ include file="/JspHeader.jsp" %>
<
!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=uTF-8">
<%-- TTitle is a request parameter for the templated page title. --%>
<title>
Fish and Chips Club - ${param.TTitle} </title>
<link rel="stylesheet" type="text/css" href="../../stylesheet.css" media="all">
</head>
<body>
<div align="center">
<img class="no-margin" src="../../images/logo.jpg" alt="Fish And Chips Club">
</div>
<div class="header">
"The Total Fish & Chips Dining Experience"
</div>
<div class="menu-bar">
<w:highlightCurrentPage styleClass='highlight'>
<c:url value="/main/home/HomePageAction.do" var="homeURL"/>
<A href='${homeURL}'>Home</a>
<c:url value="/main/rsvp/RsvpShow.do" var="showRsvpURL"/>
<A href='${showRsvpURL}'>Rsvp</a>
<c:url value="/all/logoff/LogoffAction.do" var="logoffURL"/>
<A href='${logoffURL}'>Log Off</a>
</w:highlightCurrentPage>
</div>
54

Collected Java Practices

<%-- Display error and information messages. --%>


<tags:displayMessages/>
<%-- TBody is a request parameter for the templated page body. --%>
<div class="body">
<c:if test="${not empty param.TBody}">
<jsp:include page='${param.TBody}' flush="true"/>
</c:if>
<c:if test="${empty param.TBody}">
<jsp:include page="Error.jsp" flush="true"/>
</c:if>
</div>
<tags:footer/>
</body>
</html>

See Also :
Always maintain HttpSessions
Prefer JSTL tags
Prevent self-linking
A Web App Framework - WEB4J

Validate email addresses


When a user inputs an email address, it may be validated by passing it to the constructor of
InternetAddress . If it doesn't comply with RFC822, then a checked exception will be thrown.
Note that a simple local address, such as "joe" , complies with RFC822. If this type of address is
undesired (and it usually is), then some extra validation will be needed.
Example
Here's a class that validates email addresses through the static method isValidEmailAddress . Note that it
treats local addresses as invalid (as implemented by the call to hasNameAndDomain).
package hirondelle.web4j.util;
import
import
import
import
import
import

java.util.regex.*;
javax.mail.internet.AddressException;
javax.mail.internet.InternetAddress;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
javax.servlet.http.HttpSession;

/**
* Static convenience methods for common web-related tasks.
*/
public final class WebUtil {
/**
* Validate the form of an email address.
*
* <P>Return <tt>true</tt> only if
*<ul>
* <li> <tt>aEmailAddress</tt> can successfully construct an
* {@link javax.mail.internet.InternetAddress}
* <li> when parsed with "@" as delimiter, <tt>aEmailAddress</tt> contains
* two tokens which satisfy {@link hirondelle.web4j.util.Util#textHasContent}.
*</ul>
55

Collected Java Practices

*
*<P> The second condition arises since local email addresses, simply of the form
* "<tt>albert</tt>", for example, are valid for
* {@link javax.mail.internet.InternetAddress}, but almost always undesired.
*/
public static boolean isValidEmailAddress(String aEmailAddress){
if (aEmailAddress == null) return false;
boolean result = true;
try {
InternetAddress emailAddr = new InternetAddress(aEmailAddress);
if (! hasNameAndDomain(aEmailAddress)) {
result = false;
}
}
catch (AddressException ex){
result = false;
}
return result;
}
private static boolean hasNameAndDomain(String aEmailAddress){
String[] tokens = aEmailAddress.split("@");
return
tokens.length == 2 &&
Util.textHasContent(tokens[0]) &&
Util.textHasContent(tokens[1])
;
}
//..elided
}

See Also :
Send trouble-ticket emails
A Web App Framework - WEB4J
Launch other applications

Validate generated HTML


Verifying that your HTML pages are strictly compliant with the HTML specification is simple, and
highly recommended.
Simply point this validator (published by the W3C) to your URL. It will fetch the page, parse it, and
inform you of any irregularities.

Web usability guidelines


Here are some points taken from Designing Web Usability, by Jakob Nielsen.
General points:
follow widespread user expectations
keeps things simple and fast
56

Collected Java Practices

keep download times as short as possible; enabling zip compression on your server is almost
always a good idea
Rendering:
don't hard-code font size
don't disable the browser's ability to change font size
don't hard-code absolute pixel sizes
rendering must be reasonable for a wide range of screen resolutions
if layout is based on tables, use several tables instead of one large one - the first table will be
rendered while the others are still downloading
if using tables, use the COLGROUP and COL tags to define both the number of columns and their
relative percentage widths - this allows a smart browser to start rendering a single table before it
has received all of its content
include width and height for images - allows faster rendering
include ALT text for images
break up blocks of text with lots of white space
Navigation:
use standard colours for links
avoid horizontal scrolling
don't use frames
half the users will go directly to the search box, without examining the structure of the site in any
way
store the text entered by users into the search box, for later examination
the attention of many users will initially go to the center of a page, not the top
high quality page titles and metadata are important for bookmarks and search engines

See Also :
Minimize site response time
Prevent self-linking

Wrap file upload requests


Support for file uploads was added relatively late to the Servlet Specification, starting with version 3.0.
Earlier versions of the spec have poor support for file uploads. The following applies to such earlier
versions.
If a form contains one or more file upload controls, the format of the underlying HTTP request changes
dramatically. Unless special steps are taken, it's likely that request.getParameter(String) will return
null for all parameters (both regular parameters and file upload parameters).
Reminder - if a form includes a file upload control, then it must have:
method='POST'
enctype='multipart/form-data'

One technique for handling file upload requests uses a wrapper for the underlying request, such that
request.getParameter(String) and related methods may be used in the usual way.
57

Collected Java Practices

Example
The following wrapper uses the Apache Commons FileUpload tool to parse the request into both regular
parameters and file upload parameters. An action class may use this class as it would any request, with
one exception: to access the methods related specifically to files, a cast is necessary.
import
import
import
import
import
import
import
import

java.util.*;
java.io.*;
javax.servlet.http.HttpServletRequestWrapper;
javax.servlet.http.HttpServletRequest;
org.apache.commons.fileupload.FileUploadException;
org.apache.commons.fileupload.servlet.ServletFileUpload;
org.apache.commons.fileupload.disk.DiskFileItemFactory;
org.apache.commons.fileupload.FileItem;

/**
* Wrapper for a file upload request (before Servlet 3.0).
*
* <P>This class uses the Apache Commons
* <a href='http://commons.apache.org/fileupload/'>File Upload tool</a>.
* The generous Apache License will very likely allow you to use it in your
* applications as well.
*/
public class FileUploadWrapper extends HttpServletRequestWrapper {
/** Constructor. */
public FileUploadWrapper(HttpServletRequest aRequest) throws IOException {
super(aRequest);
ServletFileUpload upload = new ServletFileUpload( new DiskFileItemFactory());
try {
List<FileItem> fileItems = upload.parseRequest(aRequest);
convertToMaps(fileItems);
}
catch(FileUploadException ex){
throw new IOException("Cannot parse underlying request: " + ex.toString());
}
}
/**
* Return all request parameter names, for both regular controls and file upload
* controls.
*/
@Override public Enumeration<String> getParameterNames() {
Set<String> allNames = new LinkedHashSet<>();
allNames.addAll(fRegularParams.keySet());
allNames.addAll(fFileParams.keySet());
return Collections.enumeration(allNames);
}
/**
* Return the parameter value. Applies only to regular parameters, not to
* file upload parameters.
*
* <P>If the parameter is not present in the underlying request,
* then <tt>null</tt> is returned.
* <P>If the parameter is present, but has no associated value,
* then an empty string is returned.
* <P>If the parameter is multivalued, return the first value that
* appears in the request.
*/
@Override public String getParameter(String aName) {
String result = null;
List<String> values = fRegularParams.get(aName);
if(values == null){
//you might try the wrappee, to see if it has a value
}
else if (values.isEmpty()) {
//param name known, but no values present
result = "";
}
else {
//return first value in list
result = values.get(FIRST_VALUE);
}
return result;
58

Collected Java Practices

}
/**
* Return the parameter values. Applies only to regular parameters,
* not to file upload parameters.
*/
@Override public String[] getParameterValues(String aName) {
String[] result = null;
List<String> values = fRegularParams.get(aName);
if(values != null) {
result = values.toArray(new String[values.size()]);
}
return result;
}
/**
* Return a {@code Map<String, List<String>>} for all regular parameters.
* Does not return any file upload parameters at all.
*/
@Override public Map<String, List<String>> getParameterMap() {
return Collections.unmodifiableMap(fRegularParams);
}
/**
* Return a {@code List<FileItem>}, in the same order as they appear
* in the underlying request.
*/
public List<FileItem> getFileItems(){
return new ArrayList<FileItem>(fFileParams.values());
}
/**
* Return the {@link FileItem} of the given name.
* <P>If the name is unknown, then return <tt>null</tt>.
*/
public FileItem getFileItem(String aFieldName){
return fFileParams.get(aFieldName);
}
// PRIVATE
/** Store regular params only. May be multivalued (hence the List). */
private final Map<String, List<String>> fRegularParams = new LinkedHashMap<>();
/** Store file params only. */
private final Map<String, FileItem> fFileParams = new LinkedHashMap<>();
private static final int FIRST_VALUE = 0;
private void convertToMaps(List<FileItem> aFileItems){
for(FileItem item: aFileItems) {
if ( isFileUploadField(item) ) {
fFileParams.put(item.getFieldName(), item);
}
else {
if( alreadyHasValue(item) ){
addMultivaluedItem(item);
}
else {
addSingleValueItem(item);
}
}
}
}
private boolean isFileUploadField(FileItem aFileItem){
return ! aFileItem.isFormField();
}
private boolean alreadyHasValue(FileItem aItem){
return fRegularParams.get(aItem.getFieldName()) != null;
}
private void addSingleValueItem(FileItem aItem){
List<String> list = new ArrayList<>();
list.add(aItem.getString());
fRegularParams.put(aItem.getFieldName(), list);
}
59

Collected Java Practices

private void addMultivaluedItem(FileItem aItem){


List<String> values = fRegularParams.get(aItem.getFieldName());
values.add(aItem.getString());
}
}

The wrapper is in turn used by a Filter :


import
import
import
import
import
import
import
import

java.io.*;
javax.servlet.Filter;
javax.servlet.FilterChain;
javax.servlet.FilterConfig;
javax.servlet.ServletException;
javax.servlet.ServletRequest;
javax.servlet.ServletResponse;
javax.servlet.http.HttpServletRequest;

/**
* Filter that wraps an underlying file upload request (before Servlet 3.0).
*
* <P>This filter should be configured only for those operations that use a
* file upload request.
*/
public final class FileUploadFilter implements Filter {
public void init(FilterConfig aConfig) throws ServletException {
//do nothing
}
public void destroy() {
//do nothing
}
public void doFilter(
ServletRequest aRequest, ServletResponse aResponse, FilterChain aChain
) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) aRequest;
if ( isFileUploadRequest(request) ) {
FileUploadWrapper wrapper = new FileUploadWrapper(request);
aChain.doFilter(wrapper, aResponse);
}
else {
aChain.doFilter(aRequest, aResponse);
}
}
private boolean isFileUploadRequest(HttpServletRequest aRequest){
return
aRequest.getMethod().equalsIgnoreCase("POST") &&
aRequest.getContentType().startsWith("multipart/form-data")
;
}
}

The Filter is configured in web.xml to handle specific URLs :


<web-app>
<filter>
<filter-name>FileUpload</filter-name>
<display-name>File Upload</display-name>
<description>
Applied to file upload requests.
Wraps the request, allowing it to be used as a regular request,
'as if' it were parsed by the Servlet API.
</description>
<filter-class>com.blah.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>FileUpload</filter-name>
<url-pattern>/someurl/*</url-pattern>
</filter-mapping>
60

Collected Java Practices

<!-- Required by the Apache FileUpload tool. -->


<listener>
<listener-class>
org.apache.commons.fileupload.servlet.FileCleanerCleanup
</listener-class>
</listener>
</web-app>

There's a variation regarding the request.getDispatcher(String) method which can be important


here. This method takes a path argument, and that path can contain query parameters not present in the
original request. Therefore, the above wrapper will not 'see' such query parameters unless special
measures are taken. For example, getParameter(String) might be modified along these lines:
@Override public String getParameter(String aName) {
String result = null;
List values = fRegularParams.get(aName);
if(values == null){
//see if the 'wrappee' has a value
String superValue = super.getParameter(aName);
if(Util.textHasContent(superValue)) {
result = superValue;
}
}
...elided...
}

Finally, for a second example of a file upload request wrapper, see this article by Jason Hunter.
See Also :
Understand details of FORM tags

A Swing App - StocksMonitor


is an example Swing application built with JDK 1.7. It has 65 classes, and its source
code is used by this site to illustrate many points related to Swing. Code examples taken from
StocksMonitor are presented in their entirety, without editing in any way, in order to provide realistic
examples. (See web4j for an example web application.)
StocksMonitor

StocksMonitor

periodically retrieves current stock prices from the web.

The following are provided for StocksMonitor :


a zip file of its source code
its complete javadoc
a JavaWebStart launch point, allowing users to download, launch, and use the application. (Please
note that the application jar is self signed by javapractices.com.) System requirements : JRE 1.7.0
or better. If you experience any problems with the launch, please let us know.
(A smaller app called Movies is also available. It's more appropriate for beginners.)
Screen shot of the StocksMonitor app:

61

Collected Java Practices

See Also :
A Web App Framework - WEB4J

Actions
The Command pattern is described in Design Patterns, where menu items are used as an example
implementation.
In Swing, the Action interface and the AbstractAction base class implement the Command pattern.
They eliminate duplication of code, and separate tasks from the details of their invocation:
the same Action object can be invoked in multiple ways, usually through a JMenuItem and
JToolBar button ( JButton and JTextField can also be used)
text, mnemonic, accelerator, tooltip, command String, and icon are all defined once in an
implementation of the Action, and are all shared by all associated graphical elements
the enabled/disabled state of an Action is always reflected by all associated graphical elements
Action objects can be passed directly to the constructor of JMenuItem and JButton , and also to
JToolBar.add

Example
A JMenuItem corresponding to File->Save can be created using this Action:
package hirondelle.stocks.file;
import
import
import
import
import
import
import
import

java.awt.event.*;
javax.swing.*;
java.util.*;
hirondelle.stocks.portfolio.PortfolioDAO;
hirondelle.stocks.portfolio.CurrentPortfolio;
hirondelle.stocks.util.ui.UiUtil;
java.util.logging.Logger;
hirondelle.stocks.util.Util;

/**
* Save the edits performed on the {@link CurrentPortfolio}, and update the display
62

Collected Java Practices

* to show that the <tt>CurrentPortfolio</tt> no longer needs a save.


*/
public final class FileSaveAction extends AbstractAction implements Observer {
/**
* Constructor.
*
* @param aCurrentPortfolio is to be saved by this action.
*/
public FileSaveAction(CurrentPortfolio aCurrentPortfolio) {
super("Save", UiUtil.getImageIcon("/toolbarButtonGraphics/general/Save"));
fCurrentPortfolio = aCurrentPortfolio;
fCurrentPortfolio.addObserver(this);
putValue(SHORT_DESCRIPTION, "Save edits to the current portfolio");
putValue(
ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK)
);
putValue(LONG_DESCRIPTION, "Save edits to the current portfolio");
putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
}
@Override public void actionPerformed(ActionEvent e) {
fLogger.info("Saving edits to the current portfolio.");
PortfolioDAO portfolioDAO = new PortfolioDAO();
portfolioDAO.save(fCurrentPortfolio.getPortfolio());
fCurrentPortfolio.setNeedsSave(false);
fCurrentPortfolio.notifyObservers();
}
/**
* Synchronize the state of this object with the state of the
* <tt>CurrentPortfolio</tt> passed to the constructor.
*
* This action is enabled only when the <tt>CurrentPortfolio</tt> is titled and
* needs a save.
*/
@Override public void update(Observable aPublisher, Object aData) {
setEnabled(fCurrentPortfolio.getNeedsSave() && !fCurrentPortfolio.isUntitled());
}
// PRIVATE
private CurrentPortfolio fCurrentPortfolio;
private static final Logger fLogger = Util.getLogger(FileSaveAction.class);
}

See Also :
Align menu items

Align menu items


Misalignment of text in menus is a common occurrence in Swing applications. It occurs when icons
appear for some menu items, but not for all.
One way to correct this problem is to implement an empty Icon having the desired size, but whose
paintIcon method does nothing. For menu items without an icon, adding an empty Icon will simply
move the text over the desired amount.
Example
package hirondelle.stocks.util.ui;
import javax.swing.Icon;
63

Collected Java Practices

import java.awt.Component;
import java.awt.Graphics;
import hirondelle.stocks.util.Args;
/**
* Represents a square icon having no graphical content.
*
* <P>Intended for use with <tt>Action</tt> and <tt>JMenuItem</tt>.
* Alignment of text is poor when the same menu mixes menu items without an icon with
* menu items having an icon. In such cases, items without an icon can use
* an <tt>EmptyIcon</tt> to take up the proper amount of space, and allow
* for alignment of all text in the menu.
*/
final class EmptyIcon implements Icon {
/**
* Convenience object for small icons, whose size matches the size of
* small icons in Sun's graphics repository.
*/
static final EmptyIcon SIZE_16 = new EmptyIcon(16);
/**
* Convenience object for large icons, whose size matches the size of
* large icons in Sun's graphics repository.
*/
static final EmptyIcon SIZE_24 = new EmptyIcon(24);
/**
* EmptyIcon objects are always square, having identical height and width.
*
* @param aSize length of any side of the icon in pixels, must
* be in the range 1..100 (inclusive).
*/
EmptyIcon(int aSize) {
Args.checkForRange(aSize, 1, 100);
fSize = aSize;
}
/**
* Return the icon size (width is same as height).
*/
@Override public int getIconWidth() {
return fSize;
}
/**
* Return the icon size (width is same as height).
*/
@Override public int getIconHeight() {
return fSize;
}
/**
* This implementation is empty, and paints nothing.
*/
@Override public void paintIcon(Component c, Graphics g, int x, int y) {
//empty
}
// PRIVATE
private int fSize;
}

See Also :
Actions

Change theme
64

Collected Java Practices

The default Java look-and-feel has a theme mechanism, whereby custom colors and fonts may be
defined. This is particularly useful for users with poor vision.
Here's an illustration of a theme created for people with low vision:

Changing theme is accomplished like so:


package myapp.ui;
import javax.swing.*;
import javax.swing.plaf.metal.*;
final class UpdateTheme {
void update(MetalTheme aTheme, JFrame aFrame){
MetalLookAndFeel.setCurrentTheme(aTheme);
try {
UIManager.setLookAndFeel(new MetalLookAndFeel());
}
catch ( UnsupportedLookAndFeelException ex ){
System.out.println("Cannot set new Theme for Java Look and Feel.");
}
SwingUtilities.updateComponentTreeUI(aFrame);
}
}

Defining Themes
Here, the Theme class defines the set of MetalTheme objects available to the application.
package hirondelle.stocks.util.ui;
import
import
import
import
import
import
import

javax.swing.plaf.metal.*;
javax.swing.plaf.basic.BasicBorders;
javax.swing.border.*;
javax.swing.plaf.*;
javax.swing.*;
java.awt.*;
java.util.*;

/**
* Defines all themes which can allow the user to customize the Java Look and Feel.
65

Collected Java Practices

*
* <P>This application uses only the cross-platform Java Look-and-Feel, and never
* attempts to adapt to the native look-and-feel (Windows, Metal, Mac).
*/
public final class Theme {
/*
* Implementation Note:
* This item has not been converted to an enum class, since it
* contains so many data-related settings.
*/
/**
* A theme identical to the default Java look-and-feel, but whose name
* is "Default" instead of the cryptic "Steel", and which provides a
* <tt>toString</tt> method (required if <tt>Theme</tt> objects
* passed to a combo box). Used as the base class for all other themes
* used in this application.
*/
public static final MetalTheme DEFAULT = new Default();
/**
* Much like {@link #DEFAULT}, but uses some blue-green colors.
*/
public static final MetalTheme AQUA = new Aqua();
/**
* Differs from {@link #DEFAULT} only in font sizes.
*/
public static final MetalTheme LARGE_FONT = new LargeFont();
/**
* Large fonts, and high contrast black and white colors.
*
* <P>This is an amalgam of two example themes from the JDK swing examples ; there
* is apparently no recommended standard for a low-vision theme.
*/
public static final MetalTheme LOW_VISION = new LowVision();
/**
* Convert <tt>aText</tt> into its corresponding <tt>Theme</tt> object,
* if possible.
*
* @param aText possibly-null text which may map to a Theme.
* @return null if <tt>aText</tt> is null, else try to match to a
* known <tt>Theme</tt>.
* @throws IllegalArgumentException if <tt>aText</tt> cannot be
* matched to a known theme.
*/
public static MetalTheme valueOf(String aText) {
if (aText == null) return null;
for(MetalTheme theme: VALUES){
if (aText.endsWith(theme.getName())){
return theme;
}
}
throw new IllegalArgumentException("Cannot parse into Theme object:" + aText);
}
/**
* Return true if <tt>aTheme</tt> uses a larger font than the default; this is the
* case only for <tt>LARGE_FONT</tt> and <tt>LOW_VISION</tt>.
*
* <P>Themes with large font sizes need particular care, as their use may
* require changes outside those provided through
* <tt>SwingUtilities.updateComponentTreeUI</tt>.
*/
public static boolean hasLargeFont(MetalTheme aTheme) {
return aTheme == LARGE_FONT || aTheme == LOW_VISION;
}
private static final MetalTheme[] fValues = {
DEFAULT,
AQUA,
LARGE_FONT,
LOW_VISION
};
/**Allows user to iterate over all elements of this enumeration.
66

*/

Collected Java Practices

public static final java.util.List<MetalTheme> VALUES =


Collections.unmodifiableList(Arrays.asList(fValues)
);
private
private
private
private

static
static
static
static

final
final
final
final

String
String
String
String

fDEFAULT_NAME = "Default";
fAQUA_NAME = "Aqua";
fLARGE_FONT_NAME = "Large Font";
fLOW_VISION_NAME = "Low Vision";

/*
* All items below are private nested classes which define the various
* themes.
*/
private static class Default extends DefaultMetalTheme {
public String getName(){
return fName;
}
/**
* This override is provided such that Theme objects can
* be directly passed to JComboBox, instead of Strings. (This would
* not be necessary if getName had been named toString instead).
*/
@Override public final String toString() {
return getName();
}
private final String fName = fDEFAULT_NAME;
}
private static class Aqua extends Default {
public String getName(){ return fName; }
protected ColorUIResource getPrimary1() {
protected ColorUIResource getPrimary2() {
protected ColorUIResource getPrimary3() {
private final String fName = fAQUA_NAME;
private final ColorUIResource fPrimary1 =
private final ColorUIResource fPrimary2 =
private final ColorUIResource fPrimary3 =
}

return fPrimary1; }
return fPrimary2; }
return fPrimary3; }
new ColorUIResource(102, 153, 153);
new ColorUIResource(128, 192, 192);
new ColorUIResource(159, 235, 235);

private static class LargeFont extends Default {


public String getName(){ return fName; }
//fonts are larger than defaults
public FontUIResource getControlTextFont() { return fControlFont;}
public FontUIResource getSystemTextFont() { return fSystemFont;}
public FontUIResource getUserTextFont() { return fUserFont;}
public FontUIResource getMenuTextFont() { return fControlFont;}
public FontUIResource getWindowTitleFont() { return fWindowTitleFont;}
public FontUIResource getSubTextFont() { return fSmallFont;}
private final String fName = fLARGE_FONT_NAME;
private final FontUIResource fControlFont = new FontUIResource("Dialog",
Font.BOLD, 18);
private final FontUIResource fSystemFont = new FontUIResource("Dialog", Font.PLAIN,
18);
private final FontUIResource fWindowTitleFont = new FontUIResource(
"Dialog", Font.BOLD,18
);
private final FontUIResource fUserFont = new FontUIResource("SansSerif",
Font.PLAIN, 18);
private final FontUIResource fSmallFont = new FontUIResource("Dialog", Font.PLAIN,
14);
}
private static class LowVision extends LargeFont {
public String getName() { return fName; }
//colors are mostly black and white
public ColorUIResource getPrimaryControlHighlight() { return fPrimaryHighlight;}
public ColorUIResource getControlHighlight() { return super.getSecondary3(); }
public ColorUIResource getFocusColor() { return getBlack(); }
public ColorUIResource getTextHighlightColor() { return getBlack(); }
public ColorUIResource getHighlightedTextColor() { return getWhite(); }
public ColorUIResource getMenuSelectedBackground() { return getBlack(); }
public ColorUIResource getMenuSelectedForeground() { return getWhite(); }
public ColorUIResource getAcceleratorForeground() { return getBlack(); }
public ColorUIResource getAcceleratorSelectedForeground() { return getWhite(); }
public void addCustomEntriesToTable(UIDefaults aTable) {
super.addCustomEntriesToTable(aTable);
67

Collected Java Practices

aTable.put( "ToolTip.border", fBlackLineBorder);


aTable.put( "TitledBorder.border", fBlackLineBorder);
aTable.put( "ScrollPane.border", fBlackLineBorder);
aTable.put( "TextField.border", fTextBorder);
aTable.put( "PasswordField.border", fTextBorder);
aTable.put( "TextArea.border", fTextBorder);
aTable.put( "TextPane.border", fTextBorder);
aTable.put( "EditorPane.border", fTextBorder);
aTable.put(
"InternalFrame.closeIcon",
MetalIconFactory.getInternalFrameCloseIcon(fInternalFrameIconSize)
);
aTable.put(
"InternalFrame.maximizeIcon",
MetalIconFactory.getInternalFrameMaximizeIcon(fInternalFrameIconSize)
);
aTable.put(
"InternalFrame.iconifyIcon",
MetalIconFactory.getInternalFrameMinimizeIcon(fInternalFrameIconSize)
);
aTable.put(
"InternalFrame.minimizeIcon",
MetalIconFactory.getInternalFrameAltMaximizeIcon(fInternalFrameIconSize)
);
aTable.put( "ScrollBar.width", fScrollBarWidth );
}
protected ColorUIResource getPrimary1() { return fPrimary1; }
protected ColorUIResource getPrimary2() { return fPrimary2; }
protected ColorUIResource getPrimary3() { return fPrimary3; }
protected ColorUIResource getSecondary2() { return fSecondary2; }
protected ColorUIResource getSecondary3() { return fSecondary3; }
private static final String fName = fLOW_VISION_NAME;
private final ColorUIResource fPrimary1 = new ColorUIResource(0, 0, 0);
private final ColorUIResource fPrimary2 = new ColorUIResource(204, 204, 204);
private final ColorUIResource fPrimary3 = new ColorUIResource(255, 255, 255);
private final ColorUIResource fPrimaryHighlight = new
ColorUIResource(102,102,102);
private final ColorUIResource fSecondary2 = new ColorUIResource(204, 204, 204);
private final ColorUIResource fSecondary3 = new ColorUIResource(255, 255, 255);
private final Border fBlackLineBorder = new BorderUIResource(new
LineBorder(getBlack()));
private final Object fTextBorder = new BorderUIResource(
new CompoundBorder(fBlackLineBorder, new BasicBorders.MarginBorder())
);
private final int fInternalFrameIconSize = 30;
private final Integer fScrollBarWidth = new Integer(25);
}
}

See Also :
Look and Feel guidelines

Consider JGoodies Forms for layouts


One of the painful aspects of Swing is layouts. The layouts provided by the JDK are inadequate in many
practical cases. The GridBagLayout class is particularly frustrating. Although its basic idea is sound, the
details of its use are difficult and frustrating to master. As well, using GridBagLayout usually creates
code repetition, which needs additional effort to reduce or eliminate.
As an alternative, you may consider the excellent JGoodies Forms tool. It takes the interesting approach
of defining a small mini-language for defining its layouts (an example of a Domain Specific Language).
While at first somewhat cryptic, this mini-language is quite effective in practice. The end result is almost
68

Collected Java Practices

always pleasing to the eye. Another nice feature is its debug mode, whereby a colored grid is laid over
the form, so you may easily verify the x-y coordinates of its cells.
See Also :
Layout Managers

Don't bury dialogs


The JDialog class uses the idea of an owner object. The owner is the class which is using the JDialog,
either a Frame or another Dialog. It's possible to pass null as the owner, but that's a very bad habit. If no
owner is defined, then various highly desirable behaviors are lost:
inheriting position from the owner
inheriting application icon from the owner
correct ALT+TAB behavior when switching between applications
The last point may seem minor at first glance, but it's actually a major problem. When proper ALT+TAB
behavior is absent, the dialog can easily get 'lost' behind your application. Many end users have no idea
how to recover access to such lost dialogs. This becomes especially serious when the dialog is modal
(which they usually are) since, not only can the user not find the dialog, but they are unable to interact
with any other screens in the application. In this situation, many end users will naturally conclude that
the application is hung, and they will do what they always do when an application is hung - they will
reboot the machine.
This issue can be addressed in your Standard Dialog class, which may enforce the rule that an owner be
specified.
As a backup style, your Standard Dialog might use the active frame as its owner, if none is explicitly
passed by the caller. Here is a code snippet showing how to get the active frame.
import java.awt.Frame;
public final class ActiveFrame {
/** Return the currently active frame. */
public static Frame getActiveFrame() {
Frame result = null;
Frame[] frames = Frame.getFrames();
for (int i = 0; i < frames.length; i++) {
Frame frame = frames[i];
if (frame.isVisible()) {
result = frame;
break;
}
}
return result;
}
}

See Also :
Standardized dialogs
69

Collected Java Practices

Don't subclass JDialog or JFrame


When creating a class based on JDialog or JFrame, there are two choices:
either subclass the JDK class using extends
define a field (instance variable) to hold the JDialog or JFrame
The second method has a nice advantage: your class's javadoc will not be cluttered up with items that are
usually irrelevant. The Java Swing API is very extensive, and it's common for its javadoc to be a bit
overwhelming. Using JDialog and JFrame as a field, and not as a base class, will automatically remove
all such extraneous methods from your own class's javadoc.
Of course, sometimes the caller will want access to the underlying JDialog or JFrame. In that case, just
return the needed object using a getXXX() method.
Example
Here is a GUI class which does not extend any base class. Note how the JFrame object is passed in to its
constructors. In addition, it uses a standard dialog class.
package hirondelle.movies.edit;
import
import
import
import
import

hirondelle.movies.util.ui.OnClose;
hirondelle.movies.util.Edit;
hirondelle.movies.util.Util;
hirondelle.movies.util.ui.UiUtil;
hirondelle.movies.util.ui.StandardDialog;

import
import
import
import
import

java.util.logging.Logger;
java.util.*;
java.awt.event.ActionEvent;
java.awt.event.ActionListener;
javax.swing.*;

/**
Dialog allowing user to input {@link Movie} information.
<P>This view can be used to either add a new Movie, or to change an existing one.
<P>It's important to note that validation of user entries is <em>not</em>
performed by this class. Rather, it's performed by the {@link Movie} class.
*/
final class MovieView {
/**
Constructor.
<P>Called when adding a new {@link Movie}.
*/
MovieView(JFrame aParent) {
fEdit = Edit.ADD;
buildGui(aParent, "Add Movie");
fStandardDialog.display();
}
/**
Constructor.
<P>Called when editing an existing {@link Movie}. The text fields are simply
prepopulated with the text taken from the currently selected row of the table.
*/
MovieView(JFrame aParent, Movie aSelectedMovie) {
fLogger.fine("Editing selected movie:" + aSelectedMovie);
fEdit = Edit.CHANGE;
fId = aSelectedMovie.getId();
buildGui(aParent, "Edit Movie");
populateFields(aSelectedMovie);
fStandardDialog.display();
70

Collected Java Practices

}
/**
Return the movie id. The id is used by the database, but is never shown to the
user,
nor is it ever edited by the end user. This method is supplied since it's
convenient to carry the id with the other information related to a movie, and the
{@link MovieDAO} needs a way to uniquely identify records.
*/
String getId() {
return fId;
}
/** The title of the movie, as entered by the user. */
String getTitle() {
return fTitle.getText();
}
/** The date the movie was viewed, as entered by the user. */
String getDateViewed() {
return fDateViewed.getText();
}
/** The movie rating, as entered by the user. */
String getRating() {
return fRating.getText();
}
/** The comment on the movie, as entered by the user. */
String getComment() {
return fComment.getText();
}
/** Close the view. */
void closeDialog() {
fStandardDialog.dispose();
}
/** Return the underlying dialog. */
JDialog getDialog() {
return fStandardDialog.getDialog();
}
// PRIVATE
private StandardDialog fStandardDialog;
private Edit fEdit;
private String fId;
private JTextField fTitle = new JTextField();
private JTextField fDateViewed = new JTextField();
private JTextField fRating = new JTextField();
private JTextField fComment = new JTextField();
private JButton fEditButton;
private static final Logger fLogger = Util.getLogger(MovieView.class);
/** Populate the GUI with data from the movie. */
private void populateFields(Movie aSelectedMovie) {
fTitle.setText(Util.format(aSelectedMovie.getTitle()));
fDateViewed.setText(Util.format(aSelectedMovie.getDateViewed()));
fRating.setText(Util.format(aSelectedMovie.getRating()));
fComment.setText(aSelectedMovie.getComment());
}
private void buildGui(JFrame aParent, String aDialogTitle) {
fStandardDialog = new StandardDialog(
aParent, aDialogTitle, true, OnClose.DISPOSE, getUserInputArea(), getButtons()
);
fStandardDialog.setDefaultButton(fEditButton);
}
private JPanel getUserInputArea() {
JPanel result = new JPanel();
result.setLayout(new BoxLayout(result, BoxLayout.Y_AXIS));
addTextField(fTitle, "Title", result);
addTextField(fDateViewed, "Date Viewed", result);
addTextField(fRating, "Rating", result);
addTextField(fComment, "Comment", result);
UiUtil.alignAllX(result, UiUtil.AlignX.LEFT);
return result;
71

Collected Java Practices

}
private void addTextField(JTextField aTextField, String aLabel, JPanel aPanel) {
JLabel label = new JLabel(aLabel);
aPanel.add(label);
aPanel.add(aTextField);
aTextField.setColumns(15);
}
private java.util.List<JButton> getButtons() {
java.util.List<JButton> result = new ArrayList<>();
fEditButton = new JButton(fEdit.toString());
fEditButton.addActionListener(new MovieController(this, fEdit));
result.add(fEditButton);
JButton cancel = new JButton("Cancel");
cancel.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent arg0) {
closeDialog();
}
});
result.add(cancel);
return result;
}
}

See Also :
Standardized dialogs

Filter table rows


Filtering table rows according to some criterion is a common requirement in Swing applications. JSE 6
has added a filter and sort mechanism to JTable, using RowFilter and TableRowSorter .
The following example is for JSE 1.5. This example uses Quote objects, which are data-centric objects
containing information regarding the current price of a stock.
QuoteFilter

is an abstract base class (sometimes called an "ABC") which:

defines isAcceptable, which must be implemented by concrete subclasses (these represent


specific filters)
defines sift , a template method which calls isAcceptable, and which applies a particular filter to
a given Collection of Quote objects
does not have knowledge of any GUI elements

package hirondelle.stocks.table;
import hirondelle.stocks.quotes.Quote;
import java.util.*;
/**
* Allows collections of {@link Quote} objects
* to be filtered according to a criterion defined by implementors.
* JDK less than 6.
*/
public abstract class QuoteFilter {
72

Collected Java Practices

/**
* Defines the criteria by which <tt>aQuote</tt> is accepted or rejected
* by this filter.
*/
abstract public boolean isAcceptable(Quote aQuote);
/**
* Return a <tt>List</tt> which has the same
* iteration order as <tt>aQuotes</tt>, but which includes only those elements
* which satisfy {@link #isAcceptable}.
*/
public final List<Quote> sift(Collection<Quote> aQuotes ){
/*
* This is an example of a template method : the general outline is
* defined here in this abstract base class, but the implementation of
* specific steps (in this case the method isAcceptable) is left to
* concrete subclasses.
*
* Note as well that this method is final, so that no subclass can override this
* implementation.
*/
List<Quote> result = new ArrayList<>();
for(Quote quote : aQuotes){
if (isAcceptable(quote)) {
result.add(quote);
}
}
return result;
}
}

The QuoteFilterFactory class


defines all concrete implementations of QuoteFilter (using static private classes)
provides a graphical interface for user selection of these filter implementations (using a JTree )
QuoteFilterFactory uses the idea of attaching a "user object" to each node of a JTree . These user
objects are the QuoteFilter implementations. When the user clicks on a new node, listeners attached
QuoteFilterFactory are informed of the change, and use getSelectedFilter to fetch the
corresponding filter. They then use the filter object to update the list underlying the TableModel , and
then finally call fireTableDataChanged , to ensure the change becomes visible to the user.

to

/**
* Graphical component which allows the end user to select a
* {@link QuoteFilter}, and informs its listeners of changes
* to this selection.
* JDK less than 6.
*/
public final class QuoteFilterFactory extends JScrollPane implements Observer {
/**
* Constructor.
*
* @param aCurrentPortfolio is observed by this class, since the list of
* possible filters displayed by this class depends on its content.
*/
public QuoteFilterFactory (CurrentPortfolio aCurrentPortfolio){
fCurrentPortfolio = aCurrentPortfolio;
//bad practice - 'this' is not fully defined until the constructor has returned:
fCurrentPortfolio.addObserver(this);
fSelectedFilter = NO_SELECTION_FILTER;
initGui();
}
/**
* Return the <tt>QuoteFilter</tt>
* attached to the currently selected item. If no selection is present,
* then return a <tt>QuoteFilter</tt> which accepts all {@link Quote} objects.
*/
public QuoteFilter getSelectedFilter(){
73

Collected Java Practices

assert fSelectedFilter != null : "Filter has null value.";


return fSelectedFilter;
}
//elided...
// PRIVATE
private CurrentPortfolio fCurrentPortfolio;
/**
* The {@link QuoteFilter} corresponding to the user's
* current non-null selection.
*/
private QuoteFilter fSelectedFilter;
/**
* The {@link QuoteFilter} corresponding to the absence of
* any user selection.
*/
private QuoteFilter NO_SELECTION_FILTER =
new QuoteFilterAcceptAll(Consts.EMPTY_STRING)
;
//elided...
// All QuoteFilter implementations:
/**
* No {@link Quote} objects are rejected by this filter.
*
*<P> Since more than one tree node uses this filter, text is
* passed to the constructor to allows for customized display.
*/
static private class QuoteFilterAcceptAll extends QuoteFilter {
QuoteFilterAcceptAll(String aText) {
fText = aText;
}
public boolean isAcceptable(Quote aQuote){
return true;
}
@Override public String toString(){
return fText;
}
private final String fText;
}
/**
* {@link Quote} objects are accepted only if they are from a specific
* {@link Exchange}.
*/
static private final class QuoteFilterExchange extends QuoteFilter {
QuoteFilterExchange(Exchange aExchange) {
fTargetExchange = aExchange;
}
@Override public boolean isAcceptable(Quote aQuote){
return aQuote.getStock().getExchange() == fTargetExchange;
}
@Override public String toString(){
return fTargetExchange.toString();
}
private Exchange fTargetExchange;
}
/**
* {@link Quote} objects are accepted only if todays price change is
* non-negative.
*/
static private final class QuoteFilterGainers extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return aQuote.getChange().doubleValue() >= 0.0;
}
@Override public String toString(){
return GAINERS;
}
}
/**
74

Collected Java Practices

* {@link Quote} objects are accepted only if todays price change is negative.
*/
static private final class QuoteFilterLosers extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return aQuote.getChange().doubleValue() < 0.0;
}
@Override public String toString(){
return LOSERS;
}
}
/** {@link Quote} objects are accepted only if it is an index. */
static private final class QuoteFilterIndex extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return aQuote.getStock().isIndex();
}
@Override public String toString(){
return INDEX;
}
}
/** {@link Quote} objects are accepted only if it is not an index. */
static private final class QuoteFilterNonIndex extends QuoteFilter {
@Override public boolean isAcceptable(Quote aQuote){
return !aQuote.getStock().isIndex();
}
@Override public String toString(){
return NON_INDEX;
}
}
}

See Also :
Observers and listeners
Sort table rows

Indicate table sort


Sorting table rows is a common task in Swing applications. The simplest way involves a call to a single
method, JTable.setAutoCreateRowSorter(true) . This will attach a generic sorter to your table. It's
invoked by clicking on one of the column headers.
Example
import
import
import
import

java.awt.event.ActionEvent;
java.awt.event.ActionListener;
java.util.Collections;
java.util.List;

import
import
import
import
import
import

javax.swing.JButton;
javax.swing.JFrame;
javax.swing.JPanel;
javax.swing.JScrollPane;
javax.swing.JTable;
javax.swing.RowSorter;

public class GenericTableSort {


/** Simplest way to add sorting to a table. */
public static void main(String... aArgs){
GenericTableSort app = new GenericTableSort();
75

Collected Java Practices

app.buildAndDisplayGui();
}
// PRIVATE
private void buildAndDisplayGui(){
JFrame frame = new JFrame("Test Generic Table Sort");
addSortableTableTo(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private void addSortableTableTo(JFrame aFrame){
JPanel panel = new JPanel();
Object[][] data = { {1,"T"},
{2,"B"}, {3,"A"},
String[] cols = {"One", "Two"};
final JTable table = new JTable(data, cols);

{4, "F"}};

table.setAutoCreateRowSorter(true); //a generic sorter


//without the scroll pane, you won't see the headers
panel.add(new JScrollPane(table));
JButton revert = new JButton("Revert Sort");
revert.addActionListener( new ActionListener(){
@Override public void actionPerformed(ActionEvent aEvent) {
List<RowSorter.SortKey> NO_SORT = Collections.emptyList();
table.getRowSorter().setSortKeys(NO_SORT);
}
});
panel.add(revert);
aFrame.getContentPane().add(panel);
}
}

To unsort a table, and revert back to its original state, you need to make a (rather cryptic) call to
table.getRowSorter().setSortKeys(null)

This is done in the above example. The null value is required, and here acts as a flag to reverse any
sorting.
Custom Sorting
The default sorting shown above acts on a single column. In some cases, you may want more control over
sorting. For example, you may need to implement a sort that depends on more than one column (a
common requirement). In that case, one alternative is outlined here.
First, define the different Comparator s you need, perhaps directly in your Model Object. (These
Comparator s know nothing about the user interface, but they are ultimately the classes that do the actual
sorting.) Then it becomes a case of wiring together clicks on the column header to the proper
Comparator , sorting the data, and then refreshing the screen.
More precisely:
attach a MouseAdapter to the JTableHeader, to listen for clicks on the table's header.
translate the clicks into a column index.
instruct your TableModel to sort itself on the given column index; these sorts are implemented by
the N different Comparator s that you've defined for this purpose.
refresh the display by calling fireTableDateChanged() in the TableModel .
Here's a snippet for the first steps:
76

Collected Java Practices

myTable.getTableHeader().addMouseListener(new MySorter());
private final class MySorter extends MouseAdapter {
@Override public void mouseClicked(MouseEvent aEvent) {
int colIdx = myTable.getColumnModel().getColumnIndexAtX(aEvent.getX());
myTableModel.sortByColumn(colIdx);
}
}

The above technique is used by the Movies app, by the following classes:
Movie - the Model Object, that defines the needed Comparator s
MainWindow - wires the clicks on the table header a method call on the table model (here called
sortByColumn )
MovieTableModel - uses the Comparator s to sort its list of Model Objects
A disadvantage in the above technique is that there's no visual indicator for the selected sort.

Input dialogs
Input dialogs allows users to input new items, or to change old ones. It's often helpful to implement a
class which can handle both cases, since their data validation requirements are almost always identical.
Example
Here, the StockEditor class can either add a new Stock object, or change a Stock which already exists.
When a change is performed, all input fields are pre-populated with current values. When an add is
performed, all fields are either blank or pre-populated with default values.
Internally, StockEditor uses a standard dialog. It also uses a RegexInputVerifier for all text validations,
using regular expressions. Text validation proceeds as follows:
the user enters text into a JTextField
when focus leaves the JTextField , the input is verified versus a specified regular expression
if invalid, the text of the JTextField is altered to explicitly show what is invalid, and the system
beeps
the validation mechanism does not force the focus to remain in the offending JTextField
For example, here is the result of inputing an invalid Quantity , which must be an integer:

When the OK button is selected, the input is processed only if

77

Collected Java Practices

The "INVALID" text is absent from the start of all text fields
Stock.isValidInput returns true (this is a second, independent check)
If there is an error, the user is informed by a JOptionPane message.
package hirondelle.stocks.portfolio;
import
import
import
import
import
import

java.util.*;
java.util.regex.Pattern;
java.math.BigDecimal;
javax.swing.*;
java.awt.*;
java.awt.event.*;

import
import
import
import
import
import
import

hirondelle.stocks.quotes.Stock;
hirondelle.stocks.quotes.Exchange;
hirondelle.stocks.util.Args;
hirondelle.stocks.util.Util;
hirondelle.stocks.util.Consts;
hirondelle.stocks.util.ui.StandardEditor;
hirondelle.stocks.util.ui.UiUtil;

/**
* Dialog allows user to either add new a {@link Stock} to the
* {@link CurrentPortfolio}, or to change the parameters of a <tt>Stock</tt>
* which is already in the <tt>CurrentPortfolio</tt>.
*/
final class StockEditor {
/**
* Constructor.
*
* @param aFrame parent to which dialogs of this class are attached.
*/
StockEditor(JFrame aFrame) {
Args.checkForNull(aFrame);
fFrame = aFrame;
}
/**
* Return the {@link Stock} representing a new
* item which the user has input, or <tt>null</tt> if the user cancels the dialog.
*/
Stock addStock(){
showDialog("Add New Stock", null);
return fNewStock;
}
/**
* Return the possibly-edited version of <tt>aOldStock</tt>, representing
* the desired changes, or <tt>null</tt> if the user cancels the dialog.
*
* @param aOldStock {@link Stock} which the end user wishes to change.
*/
Stock changeStock(Stock aOldStock){
Args.checkForNull(aOldStock);
showDialog("Change Stock", aOldStock);
return fNewStock;
}
// PRIVATE
/**
* Always returned to the caller, and is null if the user cancels the
* dialog.
*/
private Stock fNewStock;
private
private
private
private
private

JTextField fNameField;
JTextField fTickerField;
JComboBox<Exchange> fExchangeField;
JTextField fQuantityField;
JTextField fAveragePriceField;

private JFrame fFrame;


private void showDialog(String aTitle, Stock aInitialValue){
78

Collected Java Practices

Editor editor = new Editor(aTitle, fFrame, aInitialValue);


editor.showDialog();
}
private final class Editor extends StandardEditor {
/**
* @param aInitialValue is possibly-null; if null, then the editor represents
* and Add action, otherwise it represents a Change action, and all fields will
* be pre-populated with values taken from aInitialValue.
*/
Editor(String aTitle, JFrame aParent, Stock aInitialValue){
super(aTitle, aParent, StandardEditor.CloseAction.HIDE);
fInitialValue = aInitialValue;
}
@Override protected JComponent getEditorUI () {
return getEditor(fInitialValue);
}
@Override protected void okAction() {
initNewStock(this);
}
private Stock fInitialValue;
}
private JComponent getEditor(Stock aInitialValue){
JPanel content = new JPanel();
content.setLayout(new GridBagLayout());
addCompanyName(content, aInitialValue);
addTickerField(content, aInitialValue);
addExchange(content, aInitialValue);
addQuantity(content, aInitialValue);
addAveragePrice(content, aInitialValue);
return content;
}
/**
* Set fNewStock, if the input is valid.
*
* If there is at least one error message in the text input fields, then
* inform the user that the error must be corrected before proceeding.
*
* <p>If no error messages, but input still detected as faulty as
* per {@link Stock#isValidInput(List, String, String, Exchange, Integer,
BigDecimal)},
* then inform user of the problem.
*
* @param aEditor is the dialog which will be closed if no errors occur.
*/
private void initNewStock(StandardEditor aEditor){
if (hasErrorMessage()) {
String message = "Please correct the input error before proceeding.";
JOptionPane.showMessageDialog(
fFrame, message, "Invalid Input", JOptionPane.INFORMATION_MESSAGE
);
return;
}
String name = fNameField.getText();
String ticker = fTickerField.getText();
Exchange exchange =
Exchange.valueFrom(fExchangeField.getSelectedItem().toString());
Integer numShares = null;
if (Util.textHasContent(fQuantityField.getText())) {
numShares = Integer.valueOf(fQuantityField.getText());
}
else {
numShares = Consts.ZERO;
}
BigDecimal avgPrice = null;
if (Util.textHasContent(fAveragePriceField.getText())) {
avgPrice = new BigDecimal(fAveragePriceField.getText());
}
else {
avgPrice = new BigDecimal( "0.00" );
}
java.util.List<String> errorMessages = new ArrayList<>();
if (Stock.isValidInput( errorMessages, name, ticker, exchange, numShares,
avgPrice)) {
fNewStock =
Stock(name, ticker, exchange, numShares, avgPrice);
79

Collected Java Practices

new
aEditor.dispose();
}
else {
Object[] message = errorMessages.toArray();
JOptionPane.showMessageDialog(
fFrame, message, "Invalid Input", JOptionPane.INFORMATION_MESSAGE
);
}
}
/**
* Return true only if at least one of the text input fields contains
* an error message.
*/
private boolean hasErrorMessage(){
return
fNameField.getText().startsWith(RegexInputVerifier.ERROR_MESSAGE_START) ||
fTickerField.getText().startsWith(RegexInputVerifier.ERROR_MESSAGE_START) ||
fQuantityField.getText().startsWith(RegexInputVerifier.ERROR_MESSAGE_START) ||
fAveragePriceField.getText().startsWith(RegexInputVerifier.ERROR_MESSAGE_START
);
}
private void addCompanyName(JPanel aContent, Stock aInitialValue) {
fNameField = UiUtil.addSimpleEntryField(
aContent,
"Company Name:",
(aInitialValue == null ? null : aInitialValue.getName()),
KeyEvent.VK_C,
UiUtil.getConstraints(0,0),
"No spaces on the ends, and at least one character"
);
fNameField.setInputVerifier(RegexInputVerifier.TEXT);
}
private void addTickerField(JPanel aContent, Stock aInitialValue) {
fTickerField = UiUtil.addSimpleEntryField(
aContent,
"Ticker:",
(aInitialValue == null ? null : aInitialValue.getTicker()),
KeyEvent.VK_T,
UiUtil.getConstraints(1,0),
"No spaces on the ends, 1-20 characters: " +
"letters, periods, underscore and ^"
);
String regex = "(\\^)?([A-Za-z._]){1,20}";
RegexInputVerifier tickerVerifier =
new RegexInputVerifier(Pattern.compile(regex),
RegexInputVerifier.UseToolTip.FALSE
);
fTickerField.setInputVerifier( tickerVerifier );
}
private void addExchange(JPanel aContent, Stock aInitialValue) {
JLabel exchange = new JLabel("Exchange:");
exchange.setDisplayedMnemonic(KeyEvent.VK_X);
aContent.add( exchange, UiUtil.getConstraints(2,0) );
DefaultComboBoxModel<Exchange> exchangesModel =
new DefaultComboBoxModel<>(Exchange.VALUES.toArray(new Exchange[0]))
;
fExchangeField = new JComboBox<Exchange>(exchangesModel);
if (aInitialValue != null) {
fExchangeField.setSelectedItem( aInitialValue.getExchange() );
}
exchange.setLabelFor(fExchangeField);
GridBagConstraints constraints = UiUtil.getConstraints(2,1);
constraints.fill = GridBagConstraints.HORIZONTAL;
aContent.add(fExchangeField, constraints);
}
private void addQuantity(JPanel aContent, Stock aInitialValue) {
fQuantityField = UiUtil.addSimpleEntryField(
aContent,
"Quantity:",
(aInitialValue == null ? null :
aInitialValue.getNumShares().toString()),
KeyEvent.VK_Q,
UiUtil.getConstraints(3,0),
80

Collected Java Practices

"Zero or Integer (possible leading minus sign)"


);
fQuantityField.setInputVerifier(RegexInputVerifier.INTEGER);
}
private void addAveragePrice(JPanel aContent, Stock aInitialValue) {
fAveragePriceField = UiUtil.addSimpleEntryField(
aContent,
"Average Price:",
(aInitialValue == null ? null :
aInitialValue.getAveragePrice().toString()),
KeyEvent.VK_A,
UiUtil.getConstraints(4,0),
"Zero or Positive Number (two decimals optional)"
);
fAveragePriceField.setInputVerifier(RegexInputVerifier.NON_NEGATIVE_MONEY);
}
}

See Also :
Standardized dialogs
Verify input with regular expressions
Verify input with Model Objects

Launch other applications


In Swing applications, the Desktop class gives you a simple way to launch common tools found on the
great majority of personal computers. With Desktop, you can:
open the default web browser, pointed to a given URI.
open the default email client, ready to compose an email. This is accomplished by passing a mailTo
URI to the Desktop class. The mailTo URI lets you specify the recipients of the email, its subject,
and even its content. The size of the content is limited, however (which is a bit annoying). For
example, Microsoft Outlook limits you to 512 characters. A second limitation is that file
attachments cannot be specified using a mailto URI.
also has operations that use the system's file associations, which map file extensions to
corresponding default applications. Such mapping is used to:

Desktop

open a file for editing or viewing (text file, graphics file, or whatever)
print a file with an application's print command
The Desktop class has a pleasingly simple API. Here's an example of its use. Note that when you create
an email, you often need to pay attention to encoding issues (see below).
import
import
import
import
import
import
import
import
import
import
import

java.awt.Desktop;
java.io.IOException;
java.io.UnsupportedEncodingException;
java.net.URI;
java.net.URISyntaxException;
java.nio.file.Path;
java.nio.file.Paths;
java.text.CharacterIterator;
java.text.StringCharacterIterator;
java.util.regex.Matcher;
java.util.regex.Pattern;
81

Collected Java Practices

/** Exercise the Desktop class. */


public final class ExerDesktop {
public static void main(String... aArgs){
if(Desktop.isDesktopSupported()){
ExerDesktop test = new ExerDesktop();
test.openURLInDefaultBrowser("http://www.date4j.net");
//hard-coded to a given file :
test.openFile("C:\\Temp\\blah.txt");
test.composeEmail();
}
else {
log("Desktop is not supported by this host.");
}
}
/** Open a browser. */
void openURLInDefaultBrowser(String aUrl){
try {
URI uri = new URI(aUrl);
fDesktop.browse(uri);
}
catch (URISyntaxException ex) {
log("URL is malformed. Cannot open browser.");
}
catch (IOException ex) {
log("Cannot open browser.");
}
}
/**
Open a file using the system's mapping of file extension to default application.
*/
void openFile(String aFileName){
Path path = Paths.get(aFileName);
try {
fDesktop.open(path.toFile());
}
catch (IOException ex) {
log("File not found: " + aFileName);
}
}
/**
Opens the default email client with prepopulated content, but doesn't send the
email.
You should test your own email clients and character sets.
*/
void composeEmail(){
try {
String url =
"mailTo:you@xyz.com,me@abc.com" +
"?subject=" + getEmailSubject() +
"&body=" + getEmailBody()
;
URI mailTo = new URI(url);
//log(mailTo);
fDesktop.mail(mailTo);
}
catch (IOException ex) {
log("Cannot launch mail client");
}
catch (URISyntaxException ex) {
log("Bad mailTo URI: " + ex.getInput());
}
}
// PRIVATE
private Desktop fDesktop = Desktop.getDesktop();
private static final Pattern SIMPLE_CHARS = Pattern.compile("[a-zA-Z0-9]");
private static void log(Object aMessage){
System.out.println(String.valueOf(aMessage));
}
private String getEmailSubject(){
return encodeUnusualChars("This is test #1.");
82

Collected Java Practices

}
private String getEmailBody(){
StringBuilder result = new StringBuilder();
String NL = System.getProperty("line.separator");
result.append("Hello,");
result.append(NL);
result.append(NL);
//exercises a range of common characters :
result.append("Testing 1 2 3. This is a 'quote', _yes_? ($100.00!) 5*3");
return encodeUnusualChars(result.toString());
}
/**
This is needed to handle special characters.
This method hasn't been tested with non-Latin character sets.
Encodes all text except characters matching [a-zA-Z0-9].
All other characters are hex-encoded. The encoding is '%' plus the hex
representation of the character in UTF-8.
<P>See also :
http://tools.ietf.org/html/rfc2368 - mailto
http://tools.ietf.org/html/rfc1738 - URLs
*/
private String encodeUnusualChars(String aText){
StringBuilder result = new StringBuilder();
CharacterIterator iter = new StringCharacterIterator(aText);
for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
char[] chars = {c};
String character = new String(chars);
if(isSimpleCharacter(character)){
result.append(c);
}
else {
hexEncode(character, "UTF-8", result);
}
}
return result.toString();
}
private boolean isSimpleCharacter(String aCharacter){
Matcher matcher = SIMPLE_CHARS.matcher(aCharacter);
return matcher.matches();
}
/**
For the given character and encoding, appends one or more hex-encoded characters.
For double-byte characters, two hex-encoded items will be appended.
*/
private static void hexEncode(String aCharacter, String aEncoding, StringBuilder
aOut) {
try {
String HEX_DIGITS = "0123456789ABCDEF";
byte[] bytes = aCharacter.getBytes(aEncoding);
for (int idx = 0; idx < bytes.length; idx++) {
aOut.append('%');
aOut.append(HEX_DIGITS.charAt((bytes[idx] & 0xf0) >> 4));
aOut.append(HEX_DIGITS.charAt(bytes[idx] & 0xf));
}
}
catch (UnsupportedEncodingException ex) {
log(ex.getStackTrace());
}
}
}

See Also :
Send an email
Send trouble-ticket emails
83

Collected Java Practices

Validate email addresses

Layout Managers
Layout Managers should almost always be used. Hard coding positions and sizes is usually not
appropriate.
Most common layouts:
GridBagLayout - excellent when many
BoxLayout (and associated Box class) -

components involved
"glue" and "struts" (defined in Box , not BoxLayout ),
combined with proper alignment and equalization of sizes, produce results pleasing to the eye
BorderLayout - often suitable for a top level container
Least common layouts:
FlowLayout - items can appear on different rows, according
GridLayout - forces all components to be the same size
SpringLayout - added in JDK 1.4

to size of container

Layout Managers are often mixed together in a single frame or dialog, where a top level container has its
own Layout Manager (often a BoxLayout or BorderLayout), and smaller parts use their own layout,
completely independent of the others.
Please see the topic on JGoodies Forms for an interesting alternative to the standard JDK classes for
layouts.
Example 1
Here's an example of a dialog box which uses four nested BoxLayout objects to arrange its elements. The
brightly colored areas group elements which share the same BoxLayout :

Red - (vertical layout) "struts" provide uniform spacing around each JButton, while "glue" takes up the
remaining extra space below the Delete button
Blue - (horizontal layout) again, struts provide uniform spacing around each JButton, and glue takes up
the remaining extra space to the left of the OK button.
JTable

Add-Change-Delete
84

Collected Java Practices

Black - (horizontal layout) contains the

and the

buttons

Yellow - (vertical layout) the top level container for Black and Blue
Yellow and Blue form a standardized dialog, in which top level layout and the OK, Cancel buttons are
defined by an abstract base class. Concrete subclasses define both the contents of Black and the action
taken by the OK button.
The sizing and alignment of JButton objects in BoxLayout is not automatic, unfortunately. See Common
utility tasks for these related utility methods :
getCommandRow
getCommandColumn
equalizeSizes
alignAllX , alignAllY

Example 2
Here's a typical data entry dialog :

Blue - the standardized command row (same as Example 1)


Black - uses GridBagLayout . See Common utility tasks for these utility methods related to
GridBagLayout and GridBagConstraints :
addSimpleEntryField - add a related JLabel and JTextField to a GridBagLayout
addSimpleDisplayField - add a pair of related JLabel objects to a GridBagLayout
getConstraints - a default set of constraints, which can be modified as desired

Yellow - the standardized top level layout (same as Example 1)


See Also :
Standardized dialogs
Swing utility tasks
Consider JGoodies Forms for layouts

Look and Feel guidelines


The Java Look And Feel Design Guidelines published by Oracle have many useful recommendations
85

Collected Java Practices

regarding user interfaces.


Some important points:
changing look and feel is meant as a design tool, not as a runtime tool; that is, Oracle recommends
that applications should not allow the end user to change the look and feel at runtime.
labels should always have an associated mnemonic, to allow navigation through the keyboard
( Alt+F , for example) instead of the mouse. Two common exceptions are the default button
(activated by hitting Enter or Return) and the Cancel button (activated by hitting Escape) - these
should not have an associated mnemonic.
choose mnemonics according to the typical menu styles, if possible; otherwise, select the first
letter, a prominent consonant, or a prominent vowel, in that order of preference.
use the JLabel.setLabelFor method to associate a mnemonic with an element which would not
otherwise have one, for example a JTextField .
common shortcut keys ( Ctrl-N forFile-> New , for example) usually have the Ctrl key as a
modifier.
the spacing of elements should be multiples of 6 pixels (6N) ; if one of the elements has a white
border, then the spacing should be one pixel less (6N - 1).
dialog title bars should contain the application name, for example "MyApp: Preferences", and
"MyApp: About".
JOptionPane messages should begin with a short, bold header on a single line (this can be
implemented using HTML tags <b> and <br> ).
Oracle publishes a useful graphics repository
Menus
typical menu style follows the order File, Object, Edit, Format, View, and Help. Any further menus
are placed immediately before Help.
menu titles should be a single word, while menu items may have more than one word
ellipses (...) are used only when further user input is required
Capitalization
almost all short textual items (menus, buttons, labels, for example) should have headline
capitalization, where all words are capitalized except for common words with up to three letters (a,
an, the, and, or, so, yet, etc.), and do not appear as the first or last word.
if the text is not short, then the capitalization used in ordinary prose (sentence capitalization) should
be used instead.

See Also :
Standardized dialogs
Change theme
Input dialogs

Minimal Swing application


Here's a simple, minimal Swing application. Such a class is useful when you need to quickly exercise or
test a specific technique. Simply edit the class to exercise the desired part of the Swing API.
import java.awt.event.ActionEvent;
86

Collected Java Practices

import
import
import
import
import
import

java.awt.event.ActionListener;
javax.swing.JButton;
javax.swing.JFrame;
javax.swing.JLabel;
javax.swing.JPanel;
javax.swing.JOptionPane;

/**
Simple harness for testing GUI code.
<P>To use this class, edit the code to suit your needs.
*/
public final class MinimalSwingApplication {
/**
Build and display minimal GUI.
<P>The GUI has a label and an OK button.
The OK button launches a simple message dialog.
No menu is included.
*/
public static void main(String... aArgs){
MinimalSwingApplication app = new MinimalSwingApplication();
app.buildAndDisplayGui();
}
// PRIVATE
private void buildAndDisplayGui(){
JFrame frame = new JFrame("Test Frame");
buildContent(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private void buildContent(JFrame aFrame){
JPanel panel = new JPanel();
panel.add(new JLabel("Hello"));
JButton ok = new JButton("OK");
ok.addActionListener(new ShowDialog(aFrame));
panel.add(ok);
aFrame.getContentPane().add(panel);
}
private static final class ShowDialog implements ActionListener {
/** Defining the dialog's owner JFrame is highly recommended. */
ShowDialog(JFrame aFrame){
fFrame = aFrame;
}
@Override public void actionPerformed(ActionEvent aEvent) {
JOptionPane.showMessageDialog(fFrame, "This is a dialog");
}
private JFrame fFrame;
}
}

Less trivial Swing applications are also available for download.

Observers and listeners


The Observer pattern (also known as Listener, or Publish-Subscribe) is particularly useful for graphical
applications.
The general idea is that one or more objects (the subscribers) register their interest in being notified of
changes to another object (the publisher). The subscribers can also change the state of the publisher,
although this is not obligatory (one might call these "active" observers, as opposed to "passive" ones,
87

Collected Java Practices

which do not change the state of the publisher). Interfaces are used to ensure that publishers and
subscribers have only minimal knowledge of each other.
When using Swing, there is a choice among using Observable and Observer , or one of the many
XXXListener interfaces which apply to GUI components.
Although Observer is indeed an interface, Observable is neither an interface nor an abstract base class:
it's a concrete class. This is very unusual. Almost all implementations of Design Patterns define the
important items as either interfaces or abstract base classes, not as concrete classes.
Example
The CurrentPortfolio class is a non-graphical class which encapsulates data regarding a set of stocks a stock portfolio. It's the central abstraction of its application, and a number of other classes need to
interact with it, both as active and passive listeners. Since it's a non-graphical class, it extends
Observable . Note how it calls:
Observable.setChanged()

whenever its underlying data changes, informing its listeners of changes to the portfolio.
The FileSaveAction class is an "active" Observer of the CurrentPortfolio: it both reacts to changes
in the CurrentPortfolio, and changes it. It reacts to CurrentPortfolio in its update method, where it
enables the action only when the CurrentPortfolio has unsaved edits. When FileSaveAction itself is
executed, on the other hand, its actionPerformed method acts upon CurrentPortfolio by changing its
state to "no unsaved edits".
package hirondelle.stocks.file;
import
import
import
import
import
import
import
import

java.awt.event.*;
javax.swing.*;
java.util.*;
hirondelle.stocks.portfolio.PortfolioDAO;
hirondelle.stocks.portfolio.CurrentPortfolio;
hirondelle.stocks.util.ui.UiUtil;
java.util.logging.Logger;
hirondelle.stocks.util.Util;

/**
* Save the edits performed on the {@link CurrentPortfolio}, and update the display
* to show that the <tt>CurrentPortfolio</tt> no longer needs a save.
*/
public final class FileSaveAction extends AbstractAction implements Observer {
/**
* Constructor.
*
* @param aCurrentPortfolio is to be saved by this action.
*/
public FileSaveAction(CurrentPortfolio aCurrentPortfolio) {
super("Save", UiUtil.getImageIcon("/toolbarButtonGraphics/general/Save"));
fCurrentPortfolio = aCurrentPortfolio;
fCurrentPortfolio.addObserver(this);
putValue(SHORT_DESCRIPTION, "Save edits to the current portfolio");
putValue(
ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK)
);
putValue(LONG_DESCRIPTION, "Save edits to the current portfolio");
putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
}
@Override public void actionPerformed(ActionEvent e) {
fLogger.info("Saving edits to the current portfolio.");
PortfolioDAO portfolioDAO = new PortfolioDAO();
portfolioDAO.save(fCurrentPortfolio.getPortfolio());
fCurrentPortfolio.setNeedsSave(false);
fCurrentPortfolio.notifyObservers();
}
88

Collected Java Practices

/**
* Synchronize the state of this object with the state of the
* <tt>CurrentPortfolio</tt> passed to the constructor.
*
* This action is enabled only when the <tt>CurrentPortfolio</tt> is titled and
* needs a save.
*/
@Override public void update(Observable aPublisher, Object aData) {
setEnabled(fCurrentPortfolio.getNeedsSave() && !fCurrentPortfolio.isUntitled());
}
// PRIVATE
private CurrentPortfolio fCurrentPortfolio;
private static final Logger fLogger = Util.getLogger(FileSaveAction.class);
}

See Also :
Filter table rows

Preferences dialogs
Settings for user preferences are used in most graphical applications. These settings control the operation
and appearance of a program, and need to be available to many classes. On the other hand, the dialogs
for user preferences are usually quite extensive, and can take a long time to build and display to the user.
An advantageous design would allow quick programmatic access to preferences, but construct GUIs only
if necessary.
Example
A common style is to use a JTabbedPane, with each pane corresponding to a set of related preferences:

89

Collected Java Practices

Here, each pane is defined as an implementation of an interface - PreferencesEditor:


package hirondelle.stocks.preferences;
import javax.swing.JComponent;
/**
* Allows editing of a set of related user preferences.
*
* <P>Implementations of this interface do not define a "stand-alone" GUI,
* but rather a component (usually a <tt>JPanel</tt>) which can be used by the
* caller in any way they want. Typically, a set of <tt>PreferencesEditor</tt>
* objects are placed in a <tt>JTabbedPane</tt>, one per pane.
*/
public interface PreferencesEditor {
/**
* Return a GUI component which allows the user to edit this set of related
* preferences.
*/
JComponent getUI();
/**
* The name of the tab in which this <tt>PreferencesEditor</tt>
* will be placed.
*/
String getTitle();
/**
* The mnemonic to appear in the tab name.
*
* <P>Must match a letter appearing in {@link #getTitle}.
* Use constants defined in <tt>KeyEvent</tt>, for example <tt>KeyEvent.VK_A</tt>.
*/
int getMnemonic();
/**
* Store the related preferences as they are currently displayed, overwriting
* all corresponding settings.
*/
void savePreferences();
/**
* Reset the related preferences to their default values, but only as
* presented in the GUI, without affecting stored preference values.
*
* <P>This method may not apply in all cases. For example, if the item
* represents a config which has no meaningful default value (such as a mail server
* name), the desired behavior may be to only allow a manual change. In such a
90

Collected Java Practices

* case, the implementation of this method must be a no-operation.


*/
void matchGuiToDefaultPreferences();
}

The getUI method of this interface defines the graphical aspect of a PreferencesEditor implementations do not extend a JComponent , but rather return a JComponent from the getUI method.
This allows callers to access preference values from implementations of PreferencesEditor without
necessarily constructing a GUI.
Here's an example implementation for the preferences shown above. Note the addition of these methods,
specific to this class, which allow programmatic, read-only access to stored preference values (and not
the preference values as currently displayed in the GUI):
hasShowToolBar
hasLargeIcons
getTheme

This implementation extends Observable , such that interested classes may register their interest in
changes to this set of preferences:
package hirondelle.stocks.preferences;
import
import
import
import
import
import
import

java.util.*;
java.util.logging.*;
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.util.prefs.*;
javax.swing.plaf.metal.MetalTheme;

import
import
import
import

hirondelle.stocks.util.ui.Theme;
hirondelle.stocks.util.ui.UiConsts;
hirondelle.stocks.util.ui.UiUtil;
hirondelle.stocks.util.Util;

/**
* Allows editing of user preferences related to the general
* appearance of the application, such as font size, toolbar icon size, theme,
* and the like.
*
* <P>Also allows programmatic read-only access to the current stored preferences
* for these items.
*/
public final class GeneralLookPreferencesEditor extends Observable
implements PreferencesEditor {
@Override public JComponent getUI(){
JPanel content = new JPanel();
GridBagLayout gridbag = new GridBagLayout();
content.setLayout(gridbag);
addShowToolbarAndLargeIcons(content);
addTheme(content);
addRestoreDefaults(content);
UiUtil.addVerticalGridGlue(content, 4);
matchGuiToStoredPrefs();
return content;
}
@Override public String getTitle() {
return TITLE;
}
@Override public int getMnemonic() {
return MNEMONIC;
}
savePreferences(){
91

Collected Java Practices

@Override public void


fLogger.fine("Updating general preferences.");
fPrefs.putBoolean(SHOW_TOOL_BAR_KEY, fShowToolBar.isSelected());
fPrefs.putBoolean(USE_LARGE_ICONS, fLargeIcons.isSelected());
fPrefs.put(THEME_NAME_KEY, fThemes.getSelectedItem().toString());
setChanged();
notifyObservers();
}
@Override public void matchGuiToDefaultPreferences(){
fShowToolBar.setSelected(SHOW_TOOLBAR_DEFAULT);
fLargeIcons.setSelected(USE_LARGE_ICONS_DEFAULT);
fThemes.setSelectedItem(Theme.valueOf(THEME_NAME_DEFAULT));
}
/**
* Return the stored user preference for hiding or showing the toolbar.
*/
public boolean hasShowToolBar(){
return fPrefs.getBoolean(SHOW_TOOL_BAR_KEY, SHOW_TOOLBAR_DEFAULT);
}
/**
* Return the stored user preference for using large icons.
*/
public boolean hasLargeIcons(){
return fPrefs.getBoolean(USE_LARGE_ICONS, USE_LARGE_ICONS_DEFAULT);
}
/**
* Return the stored user preference for the theme to be applied to the Java
* look-and-feel.
*/
public MetalTheme getTheme(){
String themeName = fPrefs.get(THEME_NAME_KEY, THEME_NAME_DEFAULT);
return Theme.valueOf(themeName);
}
// PRIVATE
private static final String GENERAL_LOOK_NODE = "stocksmonitor/ui/prefs/GeneralLook";
private Preferences fPrefs = Preferences.userRoot().node(GENERAL_LOOK_NODE);
private static final String TITLE = "General Look";
private static final int MNEMONIC = KeyEvent.VK_G;
private static final boolean SHOW_TOOLBAR_DEFAULT = true;
private static final String SHOW_TOOL_BAR_KEY = "ShowToolbar";
private static final boolean USE_LARGE_ICONS_DEFAULT = false;
private static final String USE_LARGE_ICONS = "UseLargeIcons";
//Theme name is mapped to Theme using Theme.valueOf
private static final String THEME_NAME_DEFAULT = "Default";
private static final String THEME_NAME_KEY = "ThemeName";
private JCheckBox fShowToolBar;
private JCheckBox fLargeIcons;
private JComboBox<MetalTheme> fThemes;
private static final Logger fLogger =
Util.getLogger(GeneralLookPreferencesEditor.class);
private void matchGuiToStoredPrefs(){
fShowToolBar.setSelected(hasShowToolBar());
fLargeIcons.setSelected(hasLargeIcons());
fThemes.setSelectedItem(getTheme());
}
private void addShowToolbarAndLargeIcons(JPanel aContent){
JLabel toolbar = new JLabel("Show:");
aContent.add(toolbar, getConstraints(0,0));
fShowToolBar = new JCheckBox("Toolbar");
fShowToolBar.setMnemonic(KeyEvent.VK_O);
aContent.add(fShowToolBar, getConstraints(0,1));
JLabel iconSize = new JLabel("Icon Size:");
92

Collected Java Practices

aContent.add(iconSize, getConstraints(1,0));
fLargeIcons = new JCheckBox("Use Large Icons");
fLargeIcons.setMnemonic(KeyEvent.VK_U);
iconSize.setLabelFor(fLargeIcons);
aContent.add(fLargeIcons, getConstraints(1,1));
}
private void addTheme(JPanel aContent) {
JLabel theme = new JLabel("Theme:");
theme.setDisplayedMnemonic(KeyEvent.VK_T);
aContent.add(theme, getConstraints(2,0));
DefaultComboBoxModel<MetalTheme> themesModel = new
DefaultComboBoxModel<MetalTheme>(
Theme.VALUES.toArray(new MetalTheme[0])
);
fThemes = new JComboBox<MetalTheme>(themesModel);
theme.setLabelFor(fThemes);
aContent.add(fThemes, getConstraints(2,1));
}
private void addRestoreDefaults(JPanel aContent) {
JButton restore = new JButton("Restore Defaults");
restore.setMnemonic(KeyEvent.VK_D);
restore.addActionListener( new ActionListener() {
@Override public void actionPerformed(ActionEvent event) {
matchGuiToDefaultPreferences();
}
});
GridBagConstraints constraints = UiUtil.getConstraints(3,1);
constraints.insets = new Insets(UiConsts.ONE_SPACE, 0,0,0);
aContent.add( restore, constraints );
}
private GridBagConstraints getConstraints(int aY, int aX){
GridBagConstraints result = UiUtil.getConstraints(aY, aX);
result.insets = new Insets(0, 0, UiConsts.ONE_SPACE, UiConsts.ONE_SPACE);
return result;
}
}

See Also :
Standardized dialogs
Using preferences

Provide an uncaught exception handler


In Java programs, what happens when an uncaught RuntimeException is thrown, but is not handled by
your program? In the simplest Java programs run from the console and executed in a single thread, the
default behavior is to:
print a stack trace to the console
terminate the thread (and thus terminate the program itself)
There are two common kinds of programs where the above behavior is not in effect:
programs running in a servlet container
Swing applications
93

Collected Java Practices

In Swing applications, the default behavior still prints the stack trace to the console, but the Event
Dispatch Thread (the main Swing thread) is not terminated. The Event Dispatch Thread always remains
alive, in spite of any RuntimeExceptions thrown by your program.
However, simply printing a stack trace to the console in a Swing application is not very informative for
the average user. Instead, you should likely override Java's default handling of uncaught exceptions. The
benefits are:
the end user is kept informed of problems more effectively
new behavior can be added, such as logging or sending emails to support staff
If you're using JDK 5+, then you may define your own UncaughtExceptionHandler . They can be
defined at 3 different levels. From the highest level to the lowest level, they are:
for the whole Java Runtime, call Thread.setDefaultUncaughtExceptionHandler upon startup
for a ThreadGroup, override ThreadGroup.uncaughtException
for a single Thread, call Thread.setUncaughtExceptionHandler
Example
Here's an example of such a handler class.
package hirondelle.movies.exception;
import hirondelle.movies.util.Util;
import hirondelle.movies.util.ui.UiUtil;
import
import
import
import

java.io.PrintWriter;
java.io.StringWriter;
java.io.Writer;
java.util.logging.Logger;

import javax.swing.JOptionPane;
/**
Custom handler for any uncaught exceptions.
<P>By default, a Swing app will handle uncaught exceptions simply by
printing a stack trace to {@link System#err}. However, the end user will
not usually see that, and if they do, they will not likely understand it.
This class addresses that problem, by showing the end user a
simple error message in a modal dialog. (The dialog's owner is the
currently active frame.)
*/
public final class ExceptionHandler implements Thread.UncaughtExceptionHandler {
/**
Custom handler for uncaught exceptions.
<P>Displays a simple model dialog to the user, showing that an error has
occured. The text of the error includes {@link Throwable#toString()}.
The stack trace is logged at a SEVERE level.
*/
@Override public void uncaughtException(Thread aThread, Throwable aThrowable) {
fLogger.severe(getStackTrace(aThrowable));
JOptionPane.showMessageDialog(
UiUtil.getActiveFrame(), "Error: " + aThrowable.toString(),
"Error", JOptionPane.ERROR_MESSAGE
);
}
// PRIVATE
private static final Logger fLogger = Util.getLogger(ExceptionHandler.class);
private String getStackTrace(Throwable aThrowable) {
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
aThrowable.printStackTrace(printWriter);
return result.toString();
}
94

Collected Java Practices

For JDK 1.4, the simplest way of overriding the default handler for uncaught exceptions is to use the
following undocumented feature (which has actually been removed from JDK 7):
create a class with a no-argument constructor
add to it a method public void handle(Throwable aThrowable){...}
upon startup, add a System property named 'sun.awt.exception.handler', whose value is the
fully qualified class name of this new handler class

See Also :
Stack trace as String

Render table cells


Renderers customize how data is presented. For example, one may customize the rendering of table cells,
table column headers, or tree nodes.
To render table cells as desired, first ensure that the table model implements getColumnClass . This will
ensure Swing selects the most appropriate default renderer for each column.
If the default renderer is inadequate, then an implementation of TableCellRenderer (which has only one
method - getTableCellRendererComponent ) must be defined, in one of two ways:
subclass DefaultTableCellRenderer and override some methods - usually setValue or
getTableCellRendererComponent (suitable when rendering is similar to JLabel)
implement the interface directly by subclassing a component and adding an implementation of
getTableCellRendererComponent (suitable when rendering is not similar to JLabel)
It's important to note that getTableCellRendererComponent :
usually returns a reference to itself
should not create a new object each time it is called, for performance reasons; the intent is that a
single renderer object is reused for all the cells in a particular column
To use your custom renderer in a table, pass it to TableColumn.setCellRenderer():
myTable.getColumnModel().getColumn(idx).setCellRenderer(new MyRenderer());

Example 1
The default action of setValue is to place the result of toString into a cell. This value may not be
appropriate.
Here, Stock.toString would place unwanted data in the cell. The setValue method is overridden to
customize both the text placed in a cell, and its associated tooltip:
package hirondelle.stocks.table;
95

Collected Java Practices

import javax.swing.table.*;
import hirondelle.stocks.quotes.Stock;
import hirondelle.stocks.util.Util;
/**
* Display a {@link Stock} in a table cell by placing the
* full name in the cell, and by providing its Yahoo ticker
* (including suffix for the {@link hirondelle.stocks.quotes.Exchange}) as tooltip.
*/
final class RenderStockName extends DefaultTableCellRenderer {
@Override public void setValue(Object aValue) {
Object result = aValue;
if ((aValue != null) && (aValue instanceof Stock)) {
Stock stock = (Stock) aValue;
result = stock.getName();
setTooltip(stock);
}
super.setValue(result);
}
private void setTooltip(Stock aStock) {
StringBuilder tooltip = new StringBuilder("Yahoo Ticker: ");
tooltip.append(aStock.getTicker());
String suffix = aStock.getExchange().getTickerSuffix();
if (Util.textHasContent(suffix)) {
tooltip.append(".");
tooltip.append(suffix);
}
setToolTipText(tooltip.toString());
}
}

Example 2
Use the current locale to render a Number as a currency:
package hirondelle.stocks.table;
import javax.swing.table.*;
import javax.swing.*;
import java.text.NumberFormat;
/**
* Display a <tt>Number</tt> in a table cell in the format defined by
* {@link NumberFormat#getCurrencyInstance()}, and aligned to the right.
*/
final class RenderPrice extends DefaultTableCellRenderer {
RenderPrice() {
setHorizontalAlignment(SwingConstants.RIGHT);
}
@Override public void setValue(Object aValue) {
Object result = aValue;
if ((aValue != null) && (aValue instanceof Number)) {
Number numberValue = (Number)aValue;
NumberFormat formatter = NumberFormat.getCurrencyInstance();
result = formatter.format(numberValue.doubleValue());
}
super.setValue(result);
}
}

Example 3
Render as red or green, according to the sign of a Number. The implementation overrides
getTableCellRendererComponent instead of setValue , and returns a this reference.

96

Collected Java Practices

package hirondelle.stocks.table;
import javax.swing.table.*;
import javax.swing.*;
import java.awt.*;
/**
* Display a <tt>Number</tt> in a table cell as either red (for negative values)
* or green (for non-negative values), and aligned on the right.
*
* <P>Note that this class will work with any <tt>Number</tt> * <tt>Double</tt>, <tt>BigDecimal</tt>, etc.
*/
final class RenderRedGreen extends DefaultTableCellRenderer {
RenderRedGreen () {
setHorizontalAlignment(SwingConstants.RIGHT);
}
@Override public Component getTableCellRendererComponent(
JTable aTable, Object aNumberValue, boolean aIsSelected,
boolean aHasFocus, int aRow, int aColumn
) {
/*
* Implementation Note :
* It is important that no 'new' objects be present in this
* implementation (excluding exceptions):
* if the table is large, then a large number of objects would be
* created during rendering.
*/
if (aNumberValue == null) return this;
Component renderer = super.getTableCellRendererComponent(
aTable, aNumberValue, aIsSelected, aHasFocus, aRow, aColumn
);
Number value = (Number)aNumberValue;
if (value.doubleValue() < 0) {
renderer.setForeground(Color.red);
}
else {
renderer.setForeground(fDarkGreen);
}
return this;
}
// PRIVATE
//the default green is too bright and illegible
private Color fDarkGreen = Color.green.darker();
}

Here's an illustration of all three example renderers:

97

Collected Java Practices

See Also :
Sort table rows

Sort table rows


Sorting table rows is a common task in Swing applications. The simplest way involves a call to a single
method, JTable.setAutoCreateRowSorter(true) . This will attach a generic sorter to your table. The
sorting is controlled by clicking on the column headers.
Example
import
import
import
import

java.awt.event.ActionEvent;
java.awt.event.ActionListener;
java.util.Collections;
java.util.List;

import
import
import
import
import
import

javax.swing.JButton;
javax.swing.JFrame;
javax.swing.JPanel;
javax.swing.JScrollPane;
javax.swing.JTable;
javax.swing.RowSorter;

public class GenericTableSort {


/** Simplest way to add sorting to a table. */
public static void main(String... aArgs){
GenericTableSort app = new GenericTableSort();
app.buildAndDisplayGui();
}
// PRIVATE
private void buildAndDisplayGui(){
JFrame frame = new JFrame("Test Generic Table Sort");
addSortableTableTo(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
98

Collected Java Practices

private void addSortableTableTo(JFrame aFrame){


JPanel panel = new JPanel();
Object[][] data = { {1,"T"},
{2,"B"}, {3,"A"},
String[] cols = {"One", "Two"};
final JTable table = new JTable(data, cols);

{4, "F"}};

table.setAutoCreateRowSorter(true); //a generic sorter


//without the scroll pane, you won't see the headers
panel.add(new JScrollPane(table));
JButton revert = new JButton("Revert Sort");
revert.addActionListener( new ActionListener(){
@Override public void actionPerformed(ActionEvent aEvent) {
List<RowSorter.SortKey> NO_SORT = Collections.emptyList();
table.getRowSorter().setSortKeys(NO_SORT);
}
});
panel.add(revert);
aFrame.getContentPane().add(panel);
}
}

The above example unsorts the table when a button is clicked. To unsort a table, and revert it back to its
original state, you simply pass a null or empty List to this method:
table.getRowSorter().setSortKeys(List)

Custom sorting with SortKeys


In some cases, you may want more control over sorting. For example, you may need to implement a sort
that depends on more than one column (a common requirement). One option is to use a list of SortKey
objects to control the sorting.
Example:
import
import
import
import

java.awt.event.MouseAdapter;
java.awt.event.MouseEvent;
java.util.ArrayList;
java.util.List;

import
import
import
import
import
import
import
import

javax.swing.JFrame;
javax.swing.JPanel;
javax.swing.JScrollPane;
javax.swing.JTable;
javax.swing.RowSorter;
javax.swing.SortOrder;
javax.swing.table.TableModel;
javax.swing.table.TableRowSorter;

public class TableSortWithSortKey {


/** Custom table sorting, using SortKey. */
public static void main(String... aArgs){
TableSortWithSortKey app = new TableSortWithSortKey();
app.buildAndDisplayGui();
}
// PRIVATE
private JTable fTable;
private RowSorter<TableModel> fSorter;
private void buildAndDisplayGui(){
JFrame frame = new JFrame("Test Table Sort With Sort Key");
addSortableTableTo(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
99

Collected Java Practices

frame.pack();
frame.setVisible(true);
}
private void addSortableTableTo(JFrame aFrame){
JPanel panel = new JPanel();
Object[][] data = { {1,"T"},
{2,"B"},
String[] cols = {"One", "Two"};
fTable = new JTable(data, cols);

{3,"A"},

{4, "F"}};

fSorter = new TableRowSorter<>(fTable.getModel());


fTable.setRowSorter(fSorter);
fTable.getTableHeader().addMouseListener(new CustomSorter());
panel.add(new JScrollPane(fTable));
aFrame.getContentPane().add(panel);
}
/** Default sort behaviour, plus every third click removes the sort. */
private final class CustomSorter extends MouseAdapter {
@Override public void mouseClicked(MouseEvent aEvent) {
int columnIdx = fTable.getColumnModel().getColumnIndexAtX(aEvent.getX());
//build a list of sort keys for this column, and pass it to the sorter
//you can build the list to fit your needs here
//for example, you can sort on multiple columns, not just one
List<RowSorter.SortKey> sortKeys = new ArrayList<>();
//cycle through all orders; sort is removed every 3rd click
SortOrder order = SortOrder.values()[fCountClicks % 3];
sortKeys.add(new RowSorter.SortKey(columnIdx, order));
fSorter.setSortKeys(sortKeys);
++fCountClicks;
}
private int fCountClicks;
}
}

Custom sorting with Comparator s


Here's another option for implementing a custom sort. The advantage of this technique is that it's
controlled by the arbitrary Comparator s that you define. A disadvantage is that there's no visual indicator
for the selected sort on the column header.
First, define the different Comparator s you need, perhaps directly in your Model Object. (These
Comparator s know nothing about the user interface, but they are ultimately responsible for the actual
sorting.) Then it becomes a case of wiring together clicks on the column header to the proper
Comparator , sorting the data underlying your TableModel , and then refreshing the screen.
More precisely:
attach a MouseAdapter to the JTableHeader, to listen for clicks on the table's header.
translate the clicks into a column index.
instruct your TableModel to sort itself on the given column index; these sorts are implemented by
the N different Comparator s that you've defined for this purpose.
refresh the display by calling fireTableDateChanged() in the TableModel .
Here's a snippet for the first steps:
myTable.getTableHeader().addMouseListener(new MySorter());
private final class MySorter extends MouseAdapter {
@Override public void mouseClicked(MouseEvent aEvent) {
int colIdx = myTable.getColumnModel().getColumnIndexAtX(aEvent.getX());
myTableModel.sortByColumn(colIdx);
}
}

100

Collected Java Practices

The above technique is implemented by the Movies app, using the following classes:
Movie - the Model Object, that defines the
MainWindow - wires the clicks on the table
sortByColumn )
MovieTableModel - uses the Comparator s

needed Comparator s
header to a method call on the table model (here called
to sort its list of Model Objects

See Also :
Type-Safe Enumerations
Filter table rows

Splash screen
Splash screens are simple graphics which load quickly upon startup, and assure the user that the
application is loading promptly. The SplashScreen class was added to JSE 6, and should be used if
available.
If JSE 6 is not available, then the following technique is provided as an alternative.
Various policies for controlling the appearance and disappearance of the splash screen are possible. Here,
the splash screen disappears when a main window has finished loading.
This example is also of interest since it demonstrates clearly that the launch thread is unusual - it's distinct
from the event dispatch thread, where most of the work of a Swing application takes place:
upon launch, the launch thread shows a splash screen, which activates the event dispatch thread
once the splash screen is showing (or, more precisely, is "realized"), the launch thread acts as a
"worker thread", and can only close the splash screen indirectly, using EventQueue.invokeLater
to properly inject an event into the event dispatch thread

package hirondelle.stocks.main;
import java.awt.*;
import java.net.URL;
import java.awt.image.ImageObserver;
/**
* Present a simple graphic to the user upon launch of the application, to
* provide a faster initial response than is possible with the main window.
*
* <P>Adapted from an
* <a href=http://developer.java.sun.com/developer/qow/archive/24/index.html>item</a>
* on Sun's Java Developer Connection.
*
* <P>This splash screen appears within about 2.5 seconds on a development
* machine. The main screen takes about 6.0 seconds to load, so use of a splash
* screen cuts down the initial display delay by about 55 percent.
*
* <P>When JDK 6+ is available, its java.awt.SplashScreen class should be used instead
* of this class.
*/
final class SplashScreen extends Frame {
/**
* Construct using an image for the splash screen.
101

Collected Java Practices

*
* @param aImageId must have content, and is used by
* {@link Class#getResource(java.lang.String)} to retrieve the splash screen image.
*/
SplashScreen(String aImageId) {
/*
* Implementation Note
* Args.checkForContent is not called here, in an attempt to minimize
* class loading.
*/
if (aImageId == null || aImageId.trim().length() == 0){
throw new IllegalArgumentException("Image Id does not have content.");
}
fImageId = aImageId;
}
/**
* Show the splash screen to the end user.
*
* <P>Once this method returns, the splash screen is realized, which means
* that almost all work on the splash screen should proceed through the event
* dispatch thread. In particular, any call to <tt>dispose</tt> for the
* splash screen must be performed in the event dispatch thread.
*/
void splash(){
initImageAndTracker();
setSize(fImage.getWidth(NO_OBSERVER), fImage.getHeight(NO_OBSERVER));
center();
fMediaTracker.addImage(fImage, IMAGE_ID);
try {
fMediaTracker.waitForID(IMAGE_ID);
}
catch(InterruptedException ex){
System.out.println("Cannot track image load.");
}
SplashWindow splashWindow = new SplashWindow(this,fImage);
}
// PRIVATE
private final String fImageId;
private MediaTracker fMediaTracker;
private Image fImage;
private static final ImageObserver NO_OBSERVER = null;
private static final int IMAGE_ID = 0;
private void initImageAndTracker(){
fMediaTracker = new MediaTracker(this);
URL imageURL = SplashScreen.class.getResource(fImageId);
fImage = Toolkit.getDefaultToolkit().getImage(imageURL);
}
/**
* Centers the frame on the screen.
*
*<P>This centering service is more or less in {@link
hirondelle.stocks.util.ui.UiUtil};
* this duplication is justified only because the use of
* {@link hirondelle.stocks.util.ui.UiUtil} would entail more class loading, which is

* not desirable for a splash screen.


*/
private void center(){
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle frame = getBounds();
setLocation((screen.width - frame.width)/2, (screen.height - frame.height)/2);
}
private final class SplashWindow extends Window {
SplashWindow(Frame aParent, Image aImage) {
super(aParent);
fImage = aImage;
setSize(fImage.getWidth(NO_OBSERVER), fImage.getHeight(NO_OBSERVER));
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle window = getBounds();
setLocation((screen.width - window.width) / 2,(screen.height window.height)/2);
setVisible(true);
}
@Override public void paint(Graphics graphics) {
if (fImage != null) {
102

Collected Java Practices

graphics.drawImage(fImage,0,0,this);
}
}
private Image fImage;
}
/**
* Developer test harness shows the splash screen for a fixed length of
* time, without launching the full application.
*/
private static void main(String... aArgs){
SplashScreen splashScreen = new SplashScreen("StocksMonitor.gif");
splashScreen.splash();
try {
Thread.sleep(2000);
}
catch(InterruptedException ex) {
System.out.println(ex);
}
System.exit(0);
}
}

Here's an example of a class which launches an application using the above SplashScreen:
package hirondelle.stocks.main;
import
import
import
import

java.util.logging.*;
java.awt.EventQueue;
hirondelle.stocks.util.Util;
hirondelle.stocks.util.Consts;

/**
* Launch the application using an older version of a splash screen.
*
*<P>Perform tasks in this order :
*<ul>
* <li>log basic system information
* <li>promptly show a splash screen upon startup
* <li>show the main screen
* <li>remove the splash screen once the main screen is shown
*</ul>
*
* These tasks are performed in a thread-safe manner.
*/
public final class Launcher {
/**
* Launch the application and display the main window.
*
* @param aArgs are ignored by this application, and may take any value.
*/
public static void main (String... aArgs) {
/*
* Implementation Note:
*
* Note that the launch thread of any GUI application is in effect an initial
* worker thread - it is not the event dispatch thread, where the bulk of
processing
* takes place. Thus, once the launch thread realizes a window, then the launch
* thread should almost always manipulate such a window through
* EventQueue.invokeLater. (This is done for closing the splash
* screen, for example.)
*/
//verifies that assertions are on:
// assert(false) : "Test";
logBasicSystemInfo();
showSplashScreen();
showMainWindow();
EventQueue.invokeLater(new SplashScreenCloser());
fLogger.info("Launch thread now exiting...");
}
103

Collected Java Practices

// PRIVATE
private static SplashScreen fSplashScreen;
private static final Logger fLogger = Util.getLogger(Launcher.class);
private static final String SPLASH_IMAGE = "StocksMonitor.gif";
/**
* Show a simple graphical splash screen, as a quick preliminary to the main screen.
*/
private static void showSplashScreen(){
fLogger.info("Showing the splash screen.");
fSplashScreen = new SplashScreen(SPLASH_IMAGE);
fSplashScreen.splash();
}
/**
* Display the main window of the application to the user.
*/
private static void showMainWindow(){
fLogger.info("Showing the main window.");
StocksMonitorMainWindow mainWindow = new StocksMonitorMainWindow();
}
/**
* Removes the splash screen.
*
* Invoke this <tt>Runnable</tt> using
* <tt>EventQueue.invokeLater</tt>, in order to remove the splash screen
* in a thread-safe manner.
*/
private static final class SplashScreenCloser implements Runnable {
@Override public void run(){
fLogger.fine("Closing the splash screen.'");
fSplashScreen.dispose();
}
}
private static void logBasicSystemInfo() {
fLogger.info("Launching the application...");
fLogger.config(
"Operating System: " + System.getProperty("os.name") + " " +
System.getProperty("os.version")
);
fLogger.config("JRE: " + System.getProperty("java.version"));
fLogger.info("Java Launched From: " + System.getProperty("java.home"));
fLogger.config("Class Path: " + System.getProperty("java.class.path"));
fLogger.config("Library Path: " + System.getProperty("java.library.path"));
fLogger.config("Application Name: " + Consts.APP_NAME + "/" + Consts.APP_VERSION);
fLogger.config("User Home Directory: " + System.getProperty("user.home"));
fLogger.config("User Working Directory: " + System.getProperty("user.dir"));
fLogger.info("Test INFO logging.");
fLogger.fine("Test FINE logging.");
fLogger.finest("Test FINEST logging.");
}
}

See Also :
Swing threads

Standardized dialogs
Inconsistencies in a user interface usually decrease the perceived quality of an application. So, it's
probably a good idea to standardize the layout, appearance, and behavior of all the dialogs in your
application.
104

Collected Java Practices

Example
Here's a class which acts as a template for all dialogs havingOK andCancel buttons. It standardizes these
items:
the form of the dialog title
the dialog icon
the layout and spacing of principal elements
the centering behavior
the resizing behavior
Note the two abstract methods that must be implemented by its subclasses: getEditorUI and okAction .
The other methods are declared as final.
The callers of this class don't usually need to subclass it directly, at the top level. Rather, a private, nested
class can be used instead.
package hirondelle.stocks.util.ui;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import hirondelle.stocks.util.Args;
/**
* Abstract Base Class for a dialog with standard layout, buttons, and behavior.
* (The name <tt>StandardEditor</tt> was chosen since almost all non-trivial
* dialogs allow the user to edit data in some way.)
*
* <P>Use of this class will apply a standard appearance to
* dialogs in the application.
*
* <P> Subclasses implement the body of the dialog (wherein business objects
* are manipulated), and the action taken by the <tt>OK</tt> button.
*
* <P>Services of a <tt>StandardEditor</tt> include:
*<ul>
* <li>centering on the parent frame
* <li>reusing the parent's icon
* <li>standard layout and border spacing, based on Java Look and Feel guidelines.
* <li>uniform naming style for dialog title, with the application name appearing first
* <li><tt>OK</tt> and <tt>Cancel</tt> buttons at the bottom of the dialog * <tt>OK</tt> is the default, and the <tt>Escape</tt> key activates
* <tt>Cancel</tt> (the latter works only if the dialog receives the escape
* keystroke, and not one of its components)
* <li>disabling of resizing
*</ul>
*
* <P>The <tt>Escape</tt> key does not always work (for example, when a
* <tt>JTable</tt> row has the focus)
*/
public abstract class StandardEditor {
/**
* Constructor.
* @param aTitle text which appears in the title
* the application; must have content.
* @param aParent window to which this dialog is
* @param aCloseAction sets the behaviour of the
*/
protected StandardEditor (String aTitle, JFrame
Args.checkForContent(aTitle);
Args.checkForNull(aParent);
fTitle = aTitle;
fParent = aParent;
fCloseAction = aCloseAction.getValue();
}

bar after the name of


attached.
dialog upon close.
aParent, CloseAction aCloseAction) {

/**
* Forces calls to constructor to have greater clarity, by using an
* enumeration instead of integers.
105

Collected Java Practices

*/
protected enum CloseAction {
DISPOSE(JDialog.DISPOSE_ON_CLOSE),
HIDE(JDialog.HIDE_ON_CLOSE);
int getValue(){
return fAction;
}
private final int fAction;
private CloseAction(int aAction){
fAction = aAction;
}
}
/**
* Display this <tt>StandardEditor</tt> to the user.
*
* <P>Follows the Java Look and Feel guidelines for spacing elements.
*/
public final void showDialog(){
boolean isModal = true;
fDialog = new JDialog(fParent, UiUtil.getDialogTitle(fTitle), isModal);
fDialog.setDefaultCloseOperation(fCloseAction);
fDialog.setResizable(false);
addCancelByEscapeKey();
JPanel standardLayout = new JPanel();
standardLayout.setLayout(new BoxLayout(standardLayout, BoxLayout.Y_AXIS));
standardLayout.setBorder(UiUtil.getStandardBorder());
standardLayout.add(getEditorUI());
standardLayout.add(getCommandRow());
fDialog.getContentPane().add(standardLayout);
UiUtil.centerOnParentAndShow(fDialog);
}
/** Close the editor dialog.
public final void dispose(){
fDialog.dispose();
}

*/

/**
* Return the GUI which allows the user to manipulate the business
* objects related to this dialog; this GUI will be placed above the
* <tt>OK</tt> and <tt>Cancel</tt> buttons, in a standard manner.
*/
protected abstract JComponent getEditorUI();
/**
* The action taken when the user hits the <tt>OK</tt> button.
*/
protected abstract void okAction();
// PRIVATE
private final String fTitle;
private final JFrame fParent;
private JDialog fDialog;
private final int fCloseAction;
/**
* Return a standardized row of command buttons, right-justified and
* all of the same size, with OK as the default button, and no mnemonics used,
* as per the Java Look and Feel guidelines.
*/
private JComponent getCommandRow() {
JButton ok = new JButton("OK");
ok.addActionListener( new ActionListener() {
@Override public void actionPerformed(ActionEvent event) {
okAction();
}
});
fDialog.getRootPane().setDefaultButton( ok );
JButton cancel = new JButton("Cancel");
cancel.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent event) {
closeDialog();
}
});
106

Collected Java Practices

List<JComponent> buttons = new ArrayList<>();


buttons.add(ok);
buttons.add(cancel);
return UiUtil.getCommandRow(buttons);
}
/**
* Force the escape key to call the same action as pressing the Cancel button.
*
* <P>This does not always work. See class comment.
*/
private void addCancelByEscapeKey(){
String CANCEL_ACTION_KEY = "CANCEL_ACTION_KEY";
int noModifiers = 0;
KeyStroke escapeKey = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, noModifiers,
false);
InputMap inputMap =
fDialog.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
;
inputMap.put(escapeKey, CANCEL_ACTION_KEY);
AbstractAction cancelAction = new AbstractAction(){
@Override public void actionPerformed(ActionEvent e){
closeDialog();
}
};
fDialog.getRootPane().getActionMap().put(CANCEL_ACTION_KEY, cancelAction);
}
private void closeDialog(){
fDialog.dispose();
}
}

See Also :
Overridable methods need special care
Look and Feel guidelines
Swing utility tasks
Input dialogs
Don't subclass JDialog or JFrame

Swing in general
Swing is a large and complex part of the Java libraries. Perhaps not unexpectedly, it's also challenging to
learn and use. It also has some bugs.
Useful tools from Sun include:
the graphics repository of commonly used icons
Java Web Start for deploying and updating applications over a network
JavaHelp for creating HTML help systems
the Look and Feel Design Guidelines (also discussed here)
the Swing Tutorial
Long methods
There is a widespread tendency for GUI code - in particular initialization code - to be implemented with
very long methods. This is probably a bad idea, for the same reason that all excessively long methods are
107

Collected Java Practices

a bad idea - they are harder to understand and maintain.


Unnecessary Fields
Fields should be created only if necessary. If a particular element is created, wired into the GUI, and then
no longer referenced anywhere else in the class, then it should probably not exist as a field, but rather as
a simple local variable. This style will allow the more important GUI elements (fields) to stand out from
the less important ones (local variables).
Good habits
follow the Look and Feel guidelines
use a LayoutManager
use Action objects
create windows and dialogs with a uniform appearance
Oracle has stated that it will be focusing its efforts on JavaFX, and not Swing. Since JavaFX is the more
modern tool, you may want to seriously consider basing your app on JavaFX instead. Note as well that
existing Swing apps can be transitioned gradually over to JavaFX.
See Also :
Look and Feel guidelines
Standardized dialogs

Swing threads
Event Dispatch Thread
In a Swing application, most of the processing takes place in a single, special thread called the event
dispatch thread (EDT).
This thread becomes active after a component becomes realized: either pack , show , or
setVisible(true) has been called. When a top level window is realized, all of its components are also
realized. Swing is mostly single-threaded: almost all calls to realized components should execute in the
event dispatch thread. The thread-safe exceptions are:
some methods of JComponent : repaint, revalidate , invalidate
all addXXXListener and removeXXXListener methods
all methods explicitly documented as thread-safe
Worker Threads Keep GUIs Responsive
If a task needs a relatively long time to complete, then performing that task in the event dispatch thread
will cause the user interface to become unresponsive for the duration of the task - the GUI becomes
"locked". Since this is undesirable, such tasks are usually performed outside the event dispatch thread, on
what is commonly referred to as a worker thread.
When a worker thread completes its task, it needs a special mechanism for updating realized GUI
components, since, as stated above, realized components almost always need to be updated only from the
event dispatch thread.
With modern JDK's, the most common way of doing this is the SwingWorker class, introduced in JSE 6.
108

Collected Java Practices

It should be used if available.


For using timers, see this related topic.
Example 1
The FetchQuotesAction fetches stock price information. This is a good example of an operation which
should be on a separate thread. Since it's not known how long it will take, or even if a good web
connection is present, it's an operation which could very easily lock the GUI.
Below are the pertinent parts of its code. Note how a SwingWorker is divided into two parts:
doInBackground does the main work and returns any needed data, and done first calls get to access the
data, and then updates the user interface.
/**
* Fetch current quote data for the {@link CurrentPortfolio} from a data
* source on the web.
*/
public final class FetchQuotesAction extends AbstractAction implements Observer {
//elided...
/**
Fetch quotes from the web for the <tt>CurrentPortfolio</tt>.
This is called either explicitly, or periodically, by a Timer.
*/
@Override public void actionPerformed(ActionEvent e) {
fLogger.info("Fetching quotes from web.");
fSummaryView.showStatusMessage("Fetching quotes...");
SwingWorker<List<Quote>, Void> hardWorker = new HardWorker();
hardWorker.execute();
}
/**
* The set of {@link Stock} objects in which the user
* is currently interested.
*/
private CurrentPortfolio fCurrentPortfolio;
/**
* GUI element which is updated whenever a new set of quotes is obtained.
*/
private SummaryView fSummaryView;
//elided...
private final class HardWorker extends SwingWorker<java.util.List<Quote>, Void> {
@Override protected List<Quote> doInBackground() throws Exception {
List<Quote> result = null;
try {
result = fCurrentPortfolio.getPortfolio().getQuotes();
}
catch(DataAccessException ex){
ex.printStackTrace();
}
return result;
}
@Override protected void done() {
try {
//get the data fetched above, in doInBackground()
List<Quote> quotes = get();
if (quotes != null){
showUpdated(quotes);
}
else {
fSummaryView.showStatusMessage("Failed - Please connect to the web.");
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
109

Collected Java Practices

JDK < 6
Other techniques are needed when using older versions of the JDK. Two methods of the EventQueue
class, invokeLater and invokeAndWait , are provided for this purpose ( SwingUtilities has synonymous
methods as well). Sun recommends using invokeLater as the usual preferred style.
When using threads in this way, it is usually a good idea to use daemon threads, not user threads,
whenever possible: daemon threads will not prevent an application from terminating. Since threads are
user threads by default, an explicit call to Thread.setDaemon(true) is required.
The remaining examples use JSE 1.5.
Example 2
The Splash Screen topic (and in particular its Launcher class) is a good example of using a worker
thread. Here, the status of the launch thread as a worker thread is exploited to show a splash screen to the
user, but only until the main window has finished loading.
Example 3
The ColorTip class, shown below, changes the background color of a component for a short, fixed
interval of time, as a simple way of calling attention to that component.
Its worker thread does not work very hard - it sleeps a lot. The calls to sleep do not cause the GUI to
become unresponsive, however, since these calls do not take place in the event dispatch thread.
ColorTip

has three private, Runnable nested classes :

Worker - inserts specific time intervals between changing colors


ChangeColor - updates the GUI by changing the background color
RevertColor - updates the GUI by changing the background color

of a target component
back to its original color

package hirondelle.stocks.quotes;
import hirondelle.stocks.util.Args;
import hirondelle.stocks.util.Consts;
import hirondelle.stocks.util.Util;
import java.awt.Color;
import java.awt.EventQueue;
import java.util.logging.Logger;
import javax.swing.JComponent;
/**
* Calls user's attention to an aspect of the GUI (much like a
* <tt>ToolTip</tt>) by changing the background color of a
* component (typically a <tt>JLabel</tt>) for a few seconds;
* the component will always revert to its original background color
* after a short time has passed. This is done once, without repeating.
*
* <p>Example use case:
<pre>
//no initial delay, and show the new color for 2 seconds only
ColorTip tip = new ColorTip(0, 2, someLabel, temporaryColor);
tip.start();
</pre>
110

Collected Java Practices

*
* Uses a daemon thread, so this class will not prevent a program from
* terminating. Will not lock the GUI.
*/
final class ColorTip {
/**
* Constructor.
*
* @param aInitialDelay number of seconds to wait before changing the
* background color of <tt>aComponent</tt>, and must be in range 0..60 (inclusive).
* @param aActivationInterval number of seconds to display <tt>aTempColor</tt>,
* and must be in range 1..60 (inclusive).
* @param aComponent GUI item whose background color will be changed.
* @param aTempColor background color which <tt>aComponent</tt> will take for
* <tt>aActivationInterval</tt> seconds.
*/
ColorTip (
int aInitialDelay, int aActivationInterval, JComponent aComponent, Color
aTempColor
) {
Args.checkForRange(aInitialDelay, 0, Consts.SECONDS_PER_MINUTE);
Args.checkForRange(aActivationInterval, 1, Consts.SECONDS_PER_MINUTE);
Args.checkForNull(aTempColor);
fInitialDelay = aInitialDelay;
fActivationInterval = aActivationInterval;
fComponent = aComponent;
fTemporaryColor = aTempColor;
fOriginalColor = aComponent.getBackground();
fOriginalOpacity = aComponent.isOpaque();
}
/**
* Temporarily change the background color of the component, without interfering with

* the user's control of the gui, and without preventing program termination.
*
* <P>If the target temporary color is the same as the current background color, then

* do nothing. (This condition occurs when two <tt>ColorTip</tt> objects are


* altering the same item at nearly the same time, such that they "overlap".)
*/
void start(){
if (isSameColor()) return;
/*
* The use of a low-level Thread, instead of a more modern class, is unusual here.
* It's acceptable since other tools aren't a great match for this task, which is
to
* go back and forth TWICE (wait, color-on, wait, color-off) between a worker
thread
* and the Event Dispatch Thread; that's not a good match for either SwingWorker
* or javax.swing.Timer.
*/
Thread thread = new Thread(new Worker());
thread.setDaemon(true);
thread.start();
}
// PRIVATE
private final
private final
private final
private final
private final
private final

int fInitialDelay;
int fActivationInterval;
JComponent fComponent;
Color fTemporaryColor;
Color fOriginalColor;
int fCONVERSION_FACTOR = Consts.MILLISECONDS_PER_SECOND;

/**
* Stores the original value of the opaque property of fComponent.
*
* Changes to the background color of a component
* take effect only if the component is in charge of drawing its background.
* This is defined by the opaque property, which needs to be true for these
* changes to take effect.
*
* <P>If fComponent is not opaque, then this property is temporarily
* changed by this class in order to change the background color.
*/
private final boolean fOriginalOpacity;
private static final Logger fLogger = Util.getLogger(ColorTip.class);

111

Collected Java Practices

/**
* Return true only if fTemporaryColor is the same as the fOriginalColor.
*/
private boolean isSameColor(){
return fTemporaryColor.equals(fOriginalColor);
}
/**
The sleeping done by this class is NOT done on the Event Dispatch Thread;
that would lock the GUI.
*/
private final class Worker implements Runnable {
@Override public void run(){
try {
fLogger.fine("Initial Sleeping...");
Thread.sleep(fCONVERSION_FACTOR * fInitialDelay);
EventQueue.invokeLater(new ChangeColor());
fLogger.fine("Activation Sleeping...");
Thread.sleep(fCONVERSION_FACTOR * fActivationInterval);
EventQueue.invokeLater(new RevertColor());
}
catch (InterruptedException ex) {
fLogger.severe("Cannot sleep.");
}
fLogger.fine("Color worker done.");
}
}
private final class ChangeColor implements Runnable {
@Override public void run(){
if (! fOriginalOpacity) {
fComponent.setOpaque(true);
}
fComponent.setBackground(fTemporaryColor);
}
}
private final class RevertColor implements Runnable {
@Override public void run(){
fComponent.setBackground(fOriginalColor);
fComponent.setOpaque(fOriginalOpacity);
}
}
}

See Also :
Splash screen
Timers

Swing utility tasks


A utility class which collects common tasks can greatly reduce code duplication. This is especially true
for Swing, where many elements of GUI construction are repetitive.
Example
The UiUtil class has a number of static methods for:
centering windows on the screen, or with respect to a parent window
equalizing sizes of components
imposing an alignment on all items in a Box
112

Collected Java Practices

creating borders of a standard size


creating dialog titles of a standard format
retrieving images
creating pairs of components, both for user input ( JLabel and JTextField ) and simple display
(two JLabel objects)
using GridBagConstraints in a concise manner
formatting numbers, times, and percents

See Also :
Look and Feel guidelines
Layout Managers
Align menu items

Timers
Timers are used to perform actions periodically, after an initial delay, or both. Also, actions can be
performed only once, if desired.
Here are some reminders regarding the two flavors of timers, as used in Swing applications:
javax.swing.Timer

suitable for simpler cases, using low numbers of timers (say less than a dozen)
runs ActionListener objects on the event dispatch thread
if the task completes quickly (say under 300 milliseconds), then it may be run directly on the event
dispatch thread, without locking the GUI.
if the task takes a long time to complete, then it should do its work on a background thread, usually
with a SwingWorker
java.util.Timer

more scalable than javax.swing.Timer, and with additional scheduling features


runs TimerTask objects on a separate thread (not on the event dispatch thread)
needs to use EventQueue.invokeLater to update the GUI
Example 1
The AboutAction class uses a javax.swing.Timer. It's an About dialog which includes a JLabel for
displaying the current object heap size. This JLabel is updated every few seconds, to reflect any activity
of the garbage collector. (Note that the timer itself does not itself request garbage collection.)
Since this task is not an intensive one (see getHeapSize below), it can be executed directly on the event
dispatch thread without causing the GUI to become unresponsive. That is, a Swing timer may be used
directly, without a worker thread.
Here are the pertinent parts of the code:
/**
* Display a modal dialog, centered on the main window, which
* contains general information about both this application and
* the system on which it's running.
113

Collected Java Practices

*/
public final class AboutAction extends AbstractAction {
//elided...
@Override public void actionPerformed(ActionEvent e) {
fLogger.info("Showing the about box.");
showAboutBox();
}
// PRIVATE
//elided...
/** Displays the size of the object heap. */
private JLabel fObjectHeapSize;
/** Periodically updates the display of <tt>fObjectHeapSize</tt>. */
private javax.swing.Timer fTimer;
private ActionListener fHeapSizeUpdater;
private static final int UPDATE_FREQ = 2 * Consts.MILLISECONDS_PER_SECOND;
private static final long SLEEP_INTERVAL = 100;
private void showAboutBox(){
JTabbedPane aboutPane = new JTabbedPane();
aboutPane.addTab( "About" , getAboutPanel() );
aboutPane.setMnemonicAt(0, KeyEvent.VK_A);
aboutPane.addTab( "System Info" , getSystemInfoPanel() );
aboutPane.setMnemonicAt(1, KeyEvent.VK_S);
startHeapSizeTimer();
Icon image = UiUtil.getImageIcon("xray-small.jpg", this.getClass()) ;
String title = UiUtil.getDialogTitle("About");
JOptionPane.showMessageDialog(
fFrame, aboutPane, title, JOptionPane.OK_OPTION, image
);
stopHeapSizeTimer();
}
/** Periodically update the display of object heap size. */
private void startHeapSizeTimer(){
//SwingWorker isn't used here, since the action happens more than once,
//and the task doesn't take very long
fHeapSizeUpdater = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//this returns quickly; it won't lock up the GUI
updateHeapSizeDisplay();
}
};
fTimer = new javax.swing.Timer(UPDATE_FREQ, fHeapSizeUpdater);
fTimer.start();
fLogger.fine("Starting timer...");
}
/**
* Must be called when the About Box is closed - otherwise the timer will continue
* to operate.
*/
private void stopHeapSizeTimer(){
fLogger.fine("Stopping timer...");
fTimer.stop(); //stops notifying registered listeners
fTimer.removeActionListener(fHeapSizeUpdater); //removes the one registered
listener
fHeapSizeUpdater = null;
fTimer = null;
}
private void updateHeapSizeDisplay(){
fLogger.fine("Updating heap size...");
fObjectHeapSize.setText(getHeapSize());
}
/** Return a measure of the current heap size in kilobytes.*/
private String getHeapSize(){
long totalMemory = Runtime.getRuntime().totalMemory();
long freeMemory = Runtime.getRuntime().freeMemory();
Long memoryUseKB = new Long( (totalMemory - freeMemory)/Consts.ONE_KILOBYTE );
StringBuilder result = new StringBuilder();
result.append(UiUtil.getLocalizedInteger(memoryUseKB));
114

Collected Java Practices

result.append(" KB");
return result.toString();
}
}

Example 2
The FetchQuotesAction class uses a javax.swing.Timer to perform a task which is not short lived.
This is acceptable, however, since in this case the Timer is combined with a worker thread, and most of
the task is not performed on the event dispatching thread.
This is an example of a common requirement - that of performing an intensive, periodic task, using both
a timer and worker thread. Another example would be a graphical network management application,
which polls the state of many devices every few minutes, and then updates the user interface with the
results.
Here are the pertinent parts of the code:
/**
* Fetch current quote data for the {@link CurrentPortfolio} from a data
* source on the web.
*
* <P>This class performs most of its work in a background thread,
* using a javax.swing.Timer. The user interface remains responsive,
* regardless of the time taken for its work to complete.
*/
public final class FetchQuotesAction extends AbstractAction implements Observer {
//elided...
/**
* Start an internal Timer, which in turn calls {@link
#actionPerformed(ActionEvent)}.
*
* <P>This method must be called immediately after calling the constructor.
* (Since this operation uses a 'this' reference, it shouldn't be included in the
* constructor itself.)
*/
public void startTimer(){
fQuoteTablePrefEditor.addObserver(this);
fCurrentPortfolio.addObserver(this);
fTimer = new javax.swing.Timer(fUpdateFreq * CONVERSION_FACTOR, this);
fTimer.start();
}
/**
Fetch quotes from the web for the <tt>CurrentPortfolio</tt>.
This is called either explicitly, or periodically, by a Timer.
*/
@Override public void actionPerformed(ActionEvent e) {
fLogger.info("Fetching quotes from web.");
fSummaryView.showStatusMessage("Fetching quotes...");
SwingWorker<List<Quote>, Void> hardWorker = new HardWorker();
hardWorker.execute();
}
// PRIVATE
/**
* The set of {@link Stock} objects in which the user
* is currently interested.
*/
private CurrentPortfolio fCurrentPortfolio;
/**
* Periodically fetches quote data.
*
* <P>Use of a Swing Timer is acceptable here, in spite of the fact that the task
* takes a long time to complete, since the task does <em>not</em> in fact get
* executed on the event-dispatch thread, but on a separate worker thread.
*/
private javax.swing.Timer fTimer;
115

Collected Java Practices

/** The number of minutes to wait between fetches of quote information. */


private int fUpdateFreq;
private static final int CONVERSION_FACTOR =
Consts.MILLISECONDS_PER_SECOND * Consts.SECONDS_PER_MINUTE
;
//elided...
private final class HardWorker extends SwingWorker<java.util.List<Quote>, Void> {
@Override protected List<Quote> doInBackground() throws Exception {
List<Quote> result = null;
try {
result = fCurrentPortfolio.getPortfolio().getQuotes();
}
catch(DataAccessException ex){
ex.printStackTrace();
}
return result;
}
@Override protected void done() {
try {
//access the data returned by doInBackground():
List<Quote> quotes = get();
if (quotes != null){
showUpdated(quotes);
}
else {
fSummaryView.showStatusMessage("Failed - Please connect to the web.");
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
private void showUpdated(List<Quote> aQuotes) {
fQuoteTable.setQuoteTable(aQuotes);
fSummaryView.setQuotes(aQuotes);
StringBuilder warning = new StringBuilder();
if (hasNoZeroPrices(aQuotes, warning)){
fSummaryView.showStatusMessage("Done.");
}
else {
fSummaryView.showStatusMessage(warning.toString());
}
}
}

Example 3
For a simple example using java.util.Timer and java.util.TimerTask in a non-Swing context,
please see this related topic.
See Also :
Schedule periodic tasks
Swing threads

Using JavaHelp
Sun's JavaHelp tool creates help systems for graphical applications, using HTML topic files and XML to
define the topic hierarchy. Developers access its services through the javax.help package.
116

Collected Java Practices

Here's an example of using javax.help to launch a JavaHelp system from a standard Help menu.
package hirondelle.stocks.help;
import
import
import
import
import
import
import

java.awt.event.*;
javax.swing.*;
java.util.logging.*;
javax.help.*;
java.net.URL;
hirondelle.stocks.util.Args;
hirondelle.stocks.util.Util;

/**
* Display the help system for the application.
*
* <P>Display one of table of contents, index, or search tab, according
* to argument passed to the constructor. This implementation uses
* Sun's <a href=http://java.sun.com/products/javahelp/>JavaHelp</a> tool.
*
* <P>This action activates the Help key (often <tt>F1</tt>) for this application.
* When the help key is pressed, the help system's table of contents is displayed.
*
* <P>This action is unusual in that it corresponds to more than one menu item
* (Contents, Index, and Search).
*
* <P>Note: the displayed JavaHelp screen is not centered; it's left as is,
* since the JavaHelp GUI is often cut off at the bottom anyway, and centering would
* make this problem worse.
*/
public final class HelpAction extends AbstractAction {
/**
* Constructor.
*
* @param aFrame parent window to which the help window is attached
* @param aText name of the menu item for this help action
* @param aMnemonicKeyEvent mnemonic for <tt>aText</tt>
* @param aIcon possibly-null graphic to be displayed alongside the text, or
* in a toolbar
* @param aView determines which help window is to be displayed: Contents, Index,
* or Search
*/
public HelpAction(
JFrame aFrame, String aText, int aMnemonicKeyEvent, Icon aIcon, View aView
) {
super(aText, aIcon);
Args.checkForNull(aFrame);
Args.checkForNull(aText);
Args.checkForNull(aView);
fFrame = aFrame;
fView = aView;
putValue(SHORT_DESCRIPTION, "StocksMonitor Help");
putValue(LONG_DESCRIPTION, "Displays JavaHelp for StocksMonitor.");
putValue(MNEMONIC_KEY, new Integer(aMnemonicKeyEvent) );
initHelpSystem();
}
@Override public void actionPerformed(ActionEvent event) {
fLogger.info("Showing help system.");
fHelpBroker.setCurrentView( fView.toString() );
fDisplayHelp.actionPerformed( event );
}
/** Enumeration for the style of presentation of the the Help system. */
public enum View {
SEARCH("Search"),
CONTENTS("TOC"),
INDEX("Index");
@Override public String toString(){
return fName;
}
private View(String aName){
fName = aName;
}
private String fName;
}
// PRIVATE
117

Collected Java Practices

private JFrame fFrame;


private View fView;
/** Path used by a classloader to find the JavaHelp files. */
private static final String PATH_TO_JAVA_HELP =
"hirondelle/stocks/help/JavaHelp/HelpSet.hs"
;
private ClassLoader DEFAULT_CLASS_LOADER = null;
private static final Logger fLogger = Util.getLogger(HelpAction.class);
private HelpBroker fHelpBroker;
private CSH.DisplayHelpFromSource fDisplayHelp;
/** Initialize the JavaHelp system. */
private void initHelpSystem(){
//optimization to avoid repeated init
if ( fHelpBroker != null && fDisplayHelp != null) return;
//(uses the classloader mechanism)
ClassLoader loader = this.getClass().getClassLoader();
URL helpSetURL = HelpSet.findHelpSet(loader, PATH_TO_JAVA_HELP);
assert helpSetURL != null : "Cannot find help system.";
try {
HelpSet helpSet = new HelpSet(DEFAULT_CLASS_LOADER, helpSetURL);
fHelpBroker = helpSet.createHelpBroker();
fHelpBroker.enableHelpKey( fFrame.getRootPane(), "overview", helpSet );
fDisplayHelp = new CSH.DisplayHelpFromSource(fHelpBroker);
}
catch (HelpSetException ex) {
fLogger.severe("Cannot create help system with: " + helpSetURL);
}
assert fHelpBroker != null : "HelpBroker is null.";
}
}

Using preferences
The Preferences API is not part of Swing, but is often used in graphical applications. It provides a
lightweight and simple way of storing application and user preferences, without directly accessing the file
system or using JNDI.
Example
Here, the Preferences API is used to store and access user preferences related to the general look and feel
of an application:
toolbar visiblilty
icon size
colors and fonts
Note the field fPrefs, and the pairs of keys and default values for each preference.
package hirondelle.stocks.preferences;
import
import
import
import
import
import
import

java.util.*;
java.util.logging.*;
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.util.prefs.*;
javax.swing.plaf.metal.MetalTheme;

118

Collected Java Practices

import
import
import
import

hirondelle.stocks.util.ui.Theme;
hirondelle.stocks.util.ui.UiConsts;
hirondelle.stocks.util.ui.UiUtil;
hirondelle.stocks.util.Util;

/**
* Allows editing of user preferences related to the general
* appearance of the application, such as font size, toolbar icon size, theme,
* and the like.
*
* <P>Also allows programmatic read-only access to the current stored preferences
* for these items.
*/
public final class GeneralLookPreferencesEditor extends Observable
implements PreferencesEditor {
@Override public JComponent getUI(){
JPanel content = new JPanel();
GridBagLayout gridbag = new GridBagLayout();
content.setLayout(gridbag);
addShowToolbarAndLargeIcons(content);
addTheme(content);
addRestoreDefaults(content);
UiUtil.addVerticalGridGlue(content, 4);
matchGuiToStoredPrefs();
return content;
}
@Override public String getTitle() {
return TITLE;
}
@Override public int getMnemonic() {
return MNEMONIC;
}
@Override public void savePreferences(){
fLogger.fine("Updating general preferences.");
fPrefs.putBoolean(SHOW_TOOL_BAR_KEY, fShowToolBar.isSelected());
fPrefs.putBoolean(USE_LARGE_ICONS, fLargeIcons.isSelected());
fPrefs.put(THEME_NAME_KEY, fThemes.getSelectedItem().toString());
setChanged();
notifyObservers();
}
@Override public void matchGuiToDefaultPreferences(){
fShowToolBar.setSelected(SHOW_TOOLBAR_DEFAULT);
fLargeIcons.setSelected(USE_LARGE_ICONS_DEFAULT);
fThemes.setSelectedItem(Theme.valueOf(THEME_NAME_DEFAULT));
}
/**
* Return the stored user preference for hiding or showing the toolbar.
*/
public boolean hasShowToolBar(){
return fPrefs.getBoolean(SHOW_TOOL_BAR_KEY, SHOW_TOOLBAR_DEFAULT);
}
/**
* Return the stored user preference for using large icons.
*/
public boolean hasLargeIcons(){
return fPrefs.getBoolean(USE_LARGE_ICONS, USE_LARGE_ICONS_DEFAULT);
}
/**
* Return the stored user preference for the theme to be applied to the Java
* look-and-feel.
*/
public MetalTheme getTheme(){
String themeName = fPrefs.get(THEME_NAME_KEY, THEME_NAME_DEFAULT);
return Theme.valueOf(themeName);
}
// PRIVATE
private static final String GENERAL_LOOK_NODE = "stocksmonitor/ui/prefs/GeneralLook";
private Preferences fPrefs = Preferences.userRoot().node(GENERAL_LOOK_NODE);
119

Collected Java Practices

private static final String TITLE = "General Look";


private static final int MNEMONIC = KeyEvent.VK_G;
private static final boolean SHOW_TOOLBAR_DEFAULT = true;
private static final String SHOW_TOOL_BAR_KEY = "ShowToolbar";
private static final boolean USE_LARGE_ICONS_DEFAULT = false;
private static final String USE_LARGE_ICONS = "UseLargeIcons";
//Theme name is mapped to Theme using Theme.valueOf
private static final String THEME_NAME_DEFAULT = "Default";
private static final String THEME_NAME_KEY = "ThemeName";
private JCheckBox fShowToolBar;
private JCheckBox fLargeIcons;
private JComboBox<MetalTheme> fThemes;
private static final Logger fLogger =
Util.getLogger(GeneralLookPreferencesEditor.class);
private void matchGuiToStoredPrefs(){
fShowToolBar.setSelected(hasShowToolBar());
fLargeIcons.setSelected(hasLargeIcons());
fThemes.setSelectedItem(getTheme());
}
private void addShowToolbarAndLargeIcons(JPanel aContent){
JLabel toolbar = new JLabel("Show:");
aContent.add(toolbar, getConstraints(0,0));
fShowToolBar = new JCheckBox("Toolbar");
fShowToolBar.setMnemonic(KeyEvent.VK_O);
aContent.add(fShowToolBar, getConstraints(0,1));
JLabel iconSize = new JLabel("Icon Size:");
aContent.add(iconSize, getConstraints(1,0));
fLargeIcons = new JCheckBox("Use Large Icons");
fLargeIcons.setMnemonic(KeyEvent.VK_U);
iconSize.setLabelFor(fLargeIcons);
aContent.add(fLargeIcons, getConstraints(1,1));
}
private void addTheme(JPanel aContent) {
JLabel theme = new JLabel("Theme:");
theme.setDisplayedMnemonic(KeyEvent.VK_T);
aContent.add(theme, getConstraints(2,0));
DefaultComboBoxModel<MetalTheme> themesModel = new
DefaultComboBoxModel<MetalTheme>(
Theme.VALUES.toArray(new MetalTheme[0])
);
fThemes = new JComboBox<MetalTheme>(themesModel);
theme.setLabelFor(fThemes);
aContent.add(fThemes, getConstraints(2,1));
}
private void addRestoreDefaults(JPanel aContent) {
JButton restore = new JButton("Restore Defaults");
restore.setMnemonic(KeyEvent.VK_D);
restore.addActionListener( new ActionListener() {
@Override public void actionPerformed(ActionEvent event) {
matchGuiToDefaultPreferences();
}
});
GridBagConstraints constraints = UiUtil.getConstraints(3,1);
constraints.insets = new Insets(UiConsts.ONE_SPACE, 0,0,0);
aContent.add( restore, constraints );
}
private GridBagConstraints getConstraints(int aY, int aX){
GridBagConstraints result = UiUtil.getConstraints(aY, aX);
result.insets = new Insets(0, 0, UiConsts.ONE_SPACE, UiConsts.ONE_SPACE);
return result;
}
}

120

Collected Java Practices

See Also :
Preferences dialogs

Verify input with Model Objects


Validation of user input is important for almost all applications. There seems to be two places where you
may implement such validation logic
directly in the view-related classes
not in the view, but in your Model Object instead
Implementing validation in the Model Object has some strong advantages:
the Model Object is the natural home for such logic
since the code is not tied to the GUI, it is much simpler to test using tools such as JUnit
complex validations that depend on more than one input item are handled just as easily as any
other validation
Example
Here's a Model Object class named Movie . It performs all its validation in its constructors. If a problem
occurs, a checked exception named InvalidUserInput is thrown.
/**
Data-centric class encapsulating all fields related to movies (a 'model object').
<P>This class exists in order to encapsulate, validate, and sort movie information.
This class is used both to validate user input, and act as a 'transfer object' when
interacting with the database.
*/
public final class Movie implements Comparable<Movie>{
/**
Constructor taking regular Java objects natural to the domain.
@param aId optional, the database identifier for the movie. This
item is optional since, for 'add' operations, it has yet to be
assigned by the database.
@param aTitle has content, name of the movie
@param aDateViewed optional, date the movie was screened by the user
@param aRating optional, in range 0.0 to 10.0
@param aComment optional, any comment on the movie
*/
Movie(
String aId, String aTitle, Date aDateViewed, BigDecimal aRating, String aComment
) throws InvalidInputException {
fId = aId;
fTitle = aTitle;
fDateViewed = aDateViewed;
fRating = aRating;
fComment = aComment;
validateState();
}
/**
Constructor which takes all parameters as <em>text</em>.
<P>Raw user input is usually in the form of <em>text</em>.
This constructor <em>first</em> parses such text into the required 'base objects' -

{@link Date}, {@link BigDecimal} and so on. If those parse operations


121

Collected Java Practices

<em>fail</em>,
then the user is shown an error message. If N such errors are present in user
input,
then N <em>separate</em> message will be presented for each failure, one by one.
<P>If all such parse operations <em>succeed</em>, then the "regular" constructor
{@link #Movie(String, String, Date, BigDecimal, String)}
will then be called. It's important to note that this call to the second
constructor
can in turn result in <em>another</em> error message being shown to the
user (just one this time).
*/
Movie(
String aId, String aTitle, String aDateViewed, String aRating, String aComment
) throws InvalidInputException {
this(
aId, aTitle, Util.parseDate(aDateViewed, "Date Viewed"),
Util.parseBigDecimal(aRating, "Rating"), aComment
);
}
String getId(){ return fId; }
String getTitle(){ return fTitle; }
Date getDateViewed(){ return fDateViewed; }
BigDecimal getRating(){ return fRating; }
String getComment(){ return fComment; }
//elided...
// PRIVATE
private String fId;
private final String fTitle;
private final Date fDateViewed;
private final BigDecimal fRating;
private final String fComment;
private static final BigDecimal TEN = new BigDecimal("10.0");
private static final int EQUAL = 0;
private static final int DESCENDING = -1;
//elided...
private void validateState() throws InvalidInputException {
InvalidInputException ex = new InvalidInputException();
if(! Util.textHasContent(fTitle)) {
ex.add("Title must have content");
}
if (fRating != null){
if (fRating.compareTo(BigDecimal.ZERO) < 0) {
ex.add("Rating cannot be less than 0.");
}
if (fRating.compareTo(TEN) > 0) {
ex.add("Rating cannot be greater than 10.");
}
}
if (ex.hasErrors()) {
throw ex;
}
}
}

The user edits Movie data using a dialog (not listed here). When the user hits a button, execution passes
to the following MovieController class. The actionPerformed method first attempts to build a Movie
object from user input. If a problem is detected, then an error message is displayed to the user.
package hirondelle.movies.edit;
import
import
import
import

hirondelle.movies.exception.InvalidInputException;
hirondelle.movies.main.MainWindow;
hirondelle.movies.util.Edit;
hirondelle.movies.util.Util;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Logger;
122

Collected Java Practices

import javax.swing.JOptionPane;
/**
Add a new {@link Movie} to the database, or change an existing one.
<P>It's important to note that this class uses most of the other classes in
this feature to get its job done (it doesn't use the <tt>Action</tt> classes):
<ul>
<li>it gets user input from the view - {@link MovieView}
<li>it validates user input using the model - {@link Movie}
<li>it persists the data using the Data Access Object - {@link MovieDAO}
</ul>
*/
final class MovieController implements ActionListener {
/**
Constructor.
@param aView user interface
@param aEdit identifies what type of edit - add or change
*/
MovieController(MovieView aView, Edit aEdit){
fView = aView;
fEdit = aEdit;
}
/**
Attempt to add a new {@link Movie}, or edit an existing one.
<P>If the input is invalid, then inform the user of the problem(s).
If the input is valid, then add or change the <tt>Movie</tt>, close the dialog,
and update the main window's display.
*/
@Override public void actionPerformed(ActionEvent aEvent){
fLogger.fine("Editing movie " + fView.getTitle());
try {
createValidMovieFromUserInput();
}
catch(InvalidInputException ex){
informUserOfProblems(ex);
}
if ( isUserInputValid() ){
if( Edit.ADD == fEdit ) {
fLogger.fine("Add operation.");
fDAO.add(fMovie);
}
else if (Edit.CHANGE == fEdit) {
fLogger.fine("Change operation.");
fDAO.change(fMovie);
}
else {
throw new AssertionError();
}
fView.closeDialog();
MainWindow.getInstance().refreshView();
}
}
// PRIVATE
private final MovieView fView;
private Movie fMovie;
private Edit fEdit;
private MovieDAO fDAO = new MovieDAO();
private static final Logger fLogger = Util.getLogger(MovieController.class);
private void createValidMovieFromUserInput() throws InvalidInputException {
fMovie = new Movie(
fView.getId(), fView.getTitle(), fView.getDateViewed(),
fView.getRating(), fView.getComment()
);
}
private boolean isUserInputValid(){
return fMovie != null;
}
private void informUserOfProblems(InvalidInputException aException) {
Object[] messages = aException.getErrorMessages().toArray();
JOptionPane.showMessageDialog(
123

Collected Java Practices

fView.getDialog(), messages,
"Movie cannot be saved", JOptionPane.ERROR_MESSAGE
);
}
}

See Also :
Immutable objects
Use a testing framework (JUnit)
Verify input with regular expressions
Input dialogs
Validation belongs in a Model Object

Verify input with regular expressions


objects are often used to allow user input and editing of data. The InputVerifier class is
used to verify such input.
JTextField

Example
uses regular expressions to verify input. Verification is performed when focus
moves from an associated JTextField ; if an error is found, the system beeps and an error message is
placed in the JTextField .
RegexInputVerifier

Items to note:
and verify are defined originally in InputVerifier
the constructor uses a type-safe enumeration ( UseToolTip ) as an argument, instead of a primitive
boolean value. This forces constructor calls to have greater clarity.
there are several static convenience objects provided at the end of this class, which are useful for
many common cases
shouldYieldFocus

/**
* Verifies user input into a {@link javax.swing.text.JTextComponent} versus a
* regular expression.
*
*<P> Upon detection of invalid input, this class takes the following actions :
*<ul>
* <li> emit a beep
* <li> overwrite the <tt>JTextComponent</tt> to display the following:
* INVALID: " (input data) "
* <li> optionally, append the tooltip text to the content of the INVALID message; this
* is useful only if the tooltip contains helpful information regarding input.
* Warning : appending the tooltip text may cause the error
* text to be too long for the corresponding text field.
*</ul>
*
*<P> The user of this class is encouraged to always place conditions on data entry
* in the tooltip for the corresponding field.
*/
final class RegexInputVerifier extends InputVerifier {
/**
* Constructor.
*
124

Collected Java Practices

* @param aPattern regular expression against which all user input will
* be verified; <tt>aPattern.pattern</tt> satisfies
* {@link Util#textHasContent}.
* @param aUseToolTip indicates if the tooltip text should be appended to
* error messages displayed to the user.
*/
RegexInputVerifier(Pattern aPattern, UseToolTip aUseToolTip){
Args.checkForContent( aPattern.pattern() );
fMatcher = aPattern.matcher(Consts.EMPTY_STRING);
fUseToolTip = aUseToolTip.getValue();
}
/** Enumeration compels the caller to use a style which reads clearly. */
enum UseToolTip {
TRUE(true),
FALSE(false);
boolean getValue(){
return fToggle;
}
private boolean fToggle;
private UseToolTip(boolean aToggle){
fToggle = aToggle;
}
}
/**
* Always returns <tt>true</tt>, in this implementation, such that focus can
* always transfer to another component whenever the validation fails.
*
* <P>If <tt>super.shouldYieldFocus</tt> returns <tt>false</tt>, then
* notify the user of an error.
*
* @param aComponent is a <tt>JTextComponent</tt>.
*/
@Override public boolean shouldYieldFocus(JComponent aComponent){
boolean isValid = super.shouldYieldFocus(aComponent);
if (isValid){
//do nothing
}
else {
JTextComponent textComponent = (JTextComponent)aComponent;
notifyUserOfError(textComponent);
}
return true;
}
/**
* Return <tt>true</tt> only if the untrimmed user input matches the
* regular expression provided to the constructor.
*
* @param aComponent must be a <tt>JTextComponent</tt>.
*/
@Override public boolean verify(JComponent aComponent) {
boolean result = false;
JTextComponent textComponent = (JTextComponent)aComponent;
fMatcher.reset(textComponent.getText());
if (fMatcher.matches()) {
result = true;
}
return result;
}
/**
* The text which begins all error messages.
*
* The caller may examine their text fields for the presence of
* <tt>ERROR_MESSAGE_START</tt>, before processing input.
*/
static final String ERROR_MESSAGE_START = "INVALID: ";
/**
* Matches user input against a regular expression.
*/
private Matcher fMatcher;
/**
* Indicates if the JTextField's tooltip text is to be appended to
* error messages, as a second way of reminding the user.
*/
125

Collected Java Practices

private boolean fUseToolTip;


/*
* Various regular expression patterns used to
* construct convenience objects of this class:
*/
private static final String TEXT_FIELD = "^(\\S)(.){1,75}(\\S)$";
private static final String NON_NEGATIVE_INTEGER_FIELD = "(\\d){1,9}";
private static final String INTEGER_FIELD = "(-)?" + NON_NEGATIVE_INTEGER_FIELD;
private static final String NON_NEGATIVE_FLOATING_POINT_FIELD =
"(\\d){1,10}\\.(\\d){1,10}"
;
private static final String FLOATING_POINT_FIELD =
"(-)?" + NON_NEGATIVE_FLOATING_POINT_FIELD
;
private static final String NON_NEGATIVE_MONEY_FIELD = "(\\d){1,15}(\\.(\\d){2})?";
private static final String MONEY_FIELD = "(-)?" + NON_NEGATIVE_MONEY_FIELD;
/**
* Convenience object for input of integers: ...-2,-1,0,1,2...
*
* <P>From 1 to 9 digits, possibly preceded by a minus sign.
* Corresponds approximately to the spec of <tt>Integer.parseInt</tt>.
* The limit on the number of digits is related to size of <tt>Integer.MAX_VALUE</tt>
* and <tt>Integer.MIN_VALUE</tt>.
*/
static final RegexInputVerifier INTEGER =
new RegexInputVerifier(Pattern.compile(INTEGER_FIELD), UseToolTip.FALSE)
;
/**
* Convenience object for input of these integers: 0,1,2...
*
*<P> As in {@link #INTEGER}, but with no leading minus sign.
*/
static final RegexInputVerifier NON_NEGATIVE_INTEGER =
new RegexInputVerifier(Pattern.compile(NON_NEGATIVE_INTEGER_FIELD),
UseToolTip.FALSE)
;
/**
* Convenience object for input of short amounts of text.
*
* <P>Text contains from 1 to 75 non-whitespace characters.
*/
static final RegexInputVerifier TEXT =
new RegexInputVerifier(Pattern.compile(TEXT_FIELD), UseToolTip.FALSE)
;
/**
* Convenience object for input of decimals numbers, eg -23.23321, 100.25.
*
* <P>Possible leading minus sign, 1 to 10 digits before the decimal, and 1 to 10
* digits after the decimal.
*/
static final RegexInputVerifier FLOATING_POINT =
new RegexInputVerifier(Pattern.compile(FLOATING_POINT_FIELD), UseToolTip.FALSE)
;
/**
* Convenience object for input of non-negative decimals numbers, eg 23.23321,
100.25.
*
* <P>As in {@link #FLOATING_POINT}, but no leading minus sign.
*/
static final RegexInputVerifier NON_NEGATIVE_FLOATING_POINT =
new RegexInputVerifier(
Pattern.compile(NON_NEGATIVE_FLOATING_POINT_FIELD), UseToolTip.FALSE
)
;
/**
* Convenience object for input of money values, eg -23, 100.25.
*
* <P>Possible leading minus sign, from 1 to 15 leading digits, and optionally
* a decimal place and two decimals.
*/
static final RegexInputVerifier MONEY =
126

Collected Java Practices

new RegexInputVerifier(Pattern.compile(MONEY_FIELD), UseToolTip.FALSE)


;
/**
* Convenience object for input of non-negative money values, eg 23, 100.25.
*
* <P>As in {@link #MONEY}, except no leading minus sign
*/
static final RegexInputVerifier NON_NEGATIVE_MONEY =
new RegexInputVerifier(Pattern.compile(NON_NEGATIVE_MONEY_FIELD), UseToolTip.FALSE)
;
/**
* If an error message is currently displayed in aComponent, then
* do nothing; otherwise, display an error message to the user in a
* aComponent (see class description for format of message).
*/
private void notifyUserOfError(JTextComponent aTextComponent){
if ( isShowingErrorMessage(aTextComponent) ){
//do nothing, since user has not yet re-input.
}
else {
UiUtil.beep();
showErrorMessage(aTextComponent);
}
}
private boolean isShowingErrorMessage(JTextComponent aTextComponent){
return aTextComponent.getText().startsWith(ERROR_MESSAGE_START);
}
private void showErrorMessage(JTextComponent aTextComponent) {
StringBuilder message = new StringBuilder(ERROR_MESSAGE_START);
message.append("\"");
message.append(aTextComponent.getText());
message.append("\"");
if ( fUseToolTip ) {
message.append(aTextComponent.getToolTipText());
}
aTextComponent.setText(message.toString());
}
//elided...
}

See Also :
Type-Safe Enumerations
Parse text
Pattern-match lines of a file
Input dialogs
Use enums to restrict arguments

Avoid @throws in javadoc


Some argue that @throws should not be used at all. Instead, one may simply rely on the javadoc tool to
automatically document all exceptions placed in the throws clause. However, others disagree.
Checked Exceptions :
declaring only checked exceptions in the method's throws clause is a widely followed convention
javadoc automatically generates basic documentation for all exceptions in the throws clause
127

Collected Java Practices

the documentation is generated with no extra effort on your part


Unchecked Exceptions :
aren't typically placed in the method's throws clause.
are very rarely caught by the caller.
do not form part of the contract of the method, but rather represent what happens when the contract
is broken. The caller cannot recover from such errors.
almost always occur when a precondition on a parameter is not met. However, such conditions are
almost always already documented in a @param tag.
Therefore, if you :
only place checked exceptions in the throws clause
don't use @throws at all
then only checked exceptions will appear in javadoc. It can be argued that this is beneficial: since
checked exceptions are more important than unchecked ones, it's best that they stand out in javadoc,
without being mixed in with other exceptions of minor interest.
In almost all cases, a @throws tag simply repeats verbatim conditions already stated in a @param tag, and
doesn't add in any way to the specification of the method's behavior. Such repetition should be regarded
with grave suspicion. When a change occurs, it's far too easy to forget to update the javadoc in two
separate places.
A general comment regarding broken contracts can be stated once in the javadoc overview.html
document :
"If the requirements or promises of any method's contract are not fulfilled (that is, if there is a bug in
either the method or its caller), then an unchecked exception will be thrown. The precise type of such an
unchecked exception does not form part of any method's contract."
Example
BasketBall

has two constructors.

The first constructor includes several @throws tags in its javadoc. However, aside from the type of the
unchecked exception, all of these @throws tags are logically equivalent to some previous statement in a
@param tag. They add nothing to the contract.
The second constructor follows a different style. It has a single parameter, and the conditions on this
parameter are stated once (and once only) in its @param tag.
public final class BasketBall {
/**
* @param aManufacturer non-null and has visible content.
* @param aDiameter in centimeters, in the range 1..50.
* @throws IllegalArgumentException if aDiameter not in given range.
* @throws IllegalArgumentException if aManufacturer has no visible content.
* @throws NullPointerException if aManufacturer is null.
*/
BasketBall(String aManufacturer, int aDiameter){
//..elided
}
/**
* @param aDiameter in centimeters, in the range 1..50.
*/
BasketBall(int aDiameter){
//..elided
}
128

Collected Java Practices

// PRIVATE
private String fManufacturer;
private int fDiameter;
}

See Also :
Javadoc all exceptions
Design by Contract

Avoid empty catch blocks


Most contend that it's usually a very bad idea to have an empty catch block.
When the exception occurs, nothing happens, and the program fails for unknown reasons.
Example
The "tried our best" comments below are an example of what not to do :
import java.io.*;
import java.util.*;
import java.nio.file.*;
/** How NOT to implement a catch.
public final class BadCatch {

*/

public static void main(String... arguments) {


List<String> quarks = Arrays.asList(
"up", "down", "charm", "strange", "top", "bottom"
);
//serialize the List
Path path = Paths.get("quarks.ser");
try(
ObjectOutputStream output = new ObjectOutputStream(Files.newOutputStream(path))
){
output.writeObject(quarks);
}
catch (IOException ex) {
//TRIED OUR BEST
}
}
}

In general, when a exception occurs, it can be thrown up to the caller, or it can be caught in a catch
block. When catching an exception, some options include:
inform the user (strongly recommended)
log the problem, using the JDK logging services, or similar tool
send an email describing the problem to an administrator
Deciding what exactly to do seems to depend on the nature of the problem. If there's an actual bug in the
program - a defect that needs to be fixed - then one might do all three of the above. In this case, the end
user should likely be shown a generic "Sorry, we goofed" message, not a stack trace. It's usually
considered bad form to display a stack trace to a non-technical end user, or if exposing a stack trace may
129

Collected Java Practices

be a security risk.
If the exception does not represent a bug, then different behavior may be appropriate. For example, if a
problem with user input is detected and an exception is thrown as a result, then merely informing the user
of the problem might be all that is required.
See Also :
Logging messages
Avoid basic style errors

Be specific in throws clause


In the throws clause of a method header, be as specific as possible. Do not group together related
exceptions in a generic exception class - that would represent a loss of possibly important information.
An alternative is the exception translation practice, in which a low level exception is first translated into a
higher level exception before being thrown out of the method.
Example
Here, both IOException and FileNotFoundException are incorrectly lumped together as Exception .
import java.io.*;
import java.util.*;
public final class BadGenericThrow {
//..elided
/**
* BAD: This method throws a generic Exception, instead
* of FileNotFoundException and IOException.
*/
public void makeFile() throws Exception {
//create a Serializable List
List<String> quarks = new ArrayList<>();
quarks.add("up");
quarks.add("down");
quarks.add("strange");
quarks.add("charm");
quarks.add("top");
quarks.add("bottom");
//serialize the List
try (
ObjectOutputStream output = new ObjectOutputStream(
new FileOutputStream("quarks.ser")
)
){
output.writeObject(quarks);
}
}
}

See Also :
Exception translation
130

Collected Java Practices

Beware of unknown root causes


All exceptions are based on the Throwable class. By default, all Throwable s can have an underlying root
cause. The root cause may be set in the Throwable's constructor, or after construction by calling
initCause .
Having the root cause is very useful for troubleshooting. However, there is a case in which root causes
can cause a problem. When a Throwable is passed over the network, it must first be serialized, and then
reconstructed (deserialized) on the other end. If the root cause inside a Throwable object is not known to
the receiver on the other end, then what happens? The receiver will throw a NoClassDefFoundError . Of
course, this replaces the original exception with something unrelated.
One option is to define a "locked down" exception, which can hold only null as the root cause.
Example
This class is a checked exception which cannot take a root cause.
/**
A checked exception that cannot be given a root cause.
All calls to {@link #getCause()} will return <tt>null</tt>.
This class cannot be subclassed.
*/
public final class LockedDownException extends Exception {
/**
The sole constructor.
No root cause can be passed to this constructor.
*/
public LockedDownException(String aMessage){
super(aMessage);
}
/**
Always coerces the root cause to <tt>null</tt>.
<P>Even though the caller is allowed to call this method, it will never
have any effect. The caller is not allowed to set the root cause, neither
during construction, nor after construction.
*/
@Override public synchronized Throwable initCause(Throwable aRootCause) {
return super.initCause(null);
}
/** Simple test harness. */
public static void main(String... aArgs){
LockedDownException ex = new LockedDownException("Hello");
ex.initCause(new IllegalArgumentException("Test"));
System.out.println(ex.getCause()); //prints 'null'
}
}

See Also :
Data exception wrapping

131

Collected Java Practices

Checked versus unchecked exceptions


Unchecked exceptions:
represent defects in the program (bugs) - often invalid arguments passed to a non-private method.
To quote from The Java Programming Language, by Gosling, Arnold, and Holmes: "Unchecked
runtime exceptions represent conditions that, generally speaking, reflect errors in your program's
logic and cannot be reasonably recovered from at run time."
are subclasses of RuntimeException, and are usually implemented using
IllegalArgumentException , NullPointerException , or IllegalStateException
a method is not obliged to establish a policy for the unchecked exceptions thrown by its
implementation (and they almost always do not do so)
Checked exceptions:
represent invalid conditions in areas outside the immediate control of the program (invalid user
input, database problems, network outages, absent files)
are subclasses of Exception
a method is obliged to establish a policy for all checked exceptions thrown by its implementation
(either pass the checked exception further up the stack, or handle it somehow)
It's somewhat confusing, but note as well that RuntimeException (unchecked) is itself a subclass of
Exception (checked).
Example 1
Model Objects are the data-centric classes used to represent items in a particular domain. Model Object
constructors need to handle both arbitrary user input, and input from underlying database ResultSet s.
Model Object constructors should throw checked exceptions:
the program may have no direct control over user input. This is particularly true in web
applications. It seems safest for a Model Object to treat user input as having arbitrary, unvalidated
content.
it's not safe for an application to make any assumptions about the state of the database. The
database is an independent entity, and its data may be changed by various means, outside of any
particular application. For example, data load tools are commonly used to create an initial state for
a new database. Such data can easily violate the constraints built into a calling application. Thus,
the safest assumption is to treat database ResultSet s as having arbitrary, unvalidated content.
Here is an example Model Object, taken from the WEB4J example application. Its constructor throws
ModelCtorException (a checked exception) :
package hirondelle.fish.main.resto;
import
import
import
import
import
import
import
import
import

hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Id;
hirondelle.web4j.security.SafeText;
hirondelle.web4j.model.Decimal;
static hirondelle.web4j.model.Decimal.ZERO;
hirondelle.web4j.model.Check;
hirondelle.web4j.model.Validator;
static hirondelle.web4j.util.Consts.FAILS;

/** Model Object for a Restaurant. */


public final class Resto {

132

Collected Java Practices

/**
Full constructor.
@param aId underlying database internal identifier (optional) 1..50 characters
@param aName of the restaurant (required), 2..50 characters
@param aLocation street address of the restaurant (optional), 2..50 characters
@param aPrice of the fish and chips meal (optional) $0.00..$100.00
@param aComment on the restaurant in general (optional) 2..50 characters
*/
public Resto(
Id aId, SafeText aName, SafeText aLocation, Decimal aPrice, SafeText aComment
) throws ModelCtorException {
fId = aId;
fName = aName;
fLocation = aLocation;
fPrice = aPrice;
fComment = aComment;
validateState();
}
public
public
public
public
public

Id getId() { return fId; }


SafeText getName() { return fName; }
SafeText getLocation() { return fLocation;
Decimal getPrice() { return fPrice; }
SafeText getComment() { return fComment; }

@Override public String toString(){


return ModelUtil.toStringFor(this);
}
@Override public boolean equals(Object aThat){
Boolean result = ModelUtil.quickEquals(this, aThat);
if (result == null) {
Resto that = (Resto) aThat;
result = ModelUtil.equalsFor(
this.getSignificantFields(), that.getSignificantFields()
);
}
return result;
}
@Override public int hashCode(){
if (fHashCode == 0){
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE
private final Id fId;
private final SafeText fName;
private final SafeText fLocation;
private final Decimal fPrice;
private final SafeText fComment;
private int fHashCode;
private static final Decimal HUNDRED = Decimal.from("100");
private void validateState() throws ModelCtorException {
ModelCtorException ex = new ModelCtorException();
if (FAILS == Check.optional(fId, Check.range(1,50))) {
ex.add("Id is optional, 1..50 chars.");
}
if (FAILS == Check.required(fName, Check.range(2,50))) {
ex.add("Restaurant Name is required, 2..50 chars.");
}
if (FAILS == Check.optional(fLocation, Check.range(2,50))) {
ex.add("Location is optional, 2..50 chars.");
}
Validator[] priceChecks = {Check.range(ZERO, HUNDRED), Check.numDecimalsAlways(2)};
if (FAILS == Check.optional(fPrice, priceChecks)) {
ex.add("Price is optional, 0.00 to 100.00.");
}
if (FAILS == Check.optional(fComment, Check.range(2,50))) {
ex.add("Comment is optional, 2..50 chars.");
}
if ( ! ex.isEmpty() ) throw ex;
}
133

Collected Java Practices

private Object[] getSignificantFields(){


return new Object[] {fName, fLocation, fPrice, fComment};
}
}

Example 2
is a convenient utility class. It performs common validations on method arguments. If a validation
fails, then it throws an unchecked exception. It is suitable for checking the internal consistency of
program, but not for checking arbitrary user input.
Args

package hirondelle.web4j.util;
import java.util.regex.*;
/**
Utility methods for common argument validations.
<P>Replaces <tt>if</tt> statements at the start of a method with
more compact method calls.
<P>Example use case.
<P>Instead of :
<PRE>
public void doThis(String aText){
if (!Util.textHasContent(aText)){
throw new IllegalArgumentException();
}
//..main body elided
}
</PRE>
<P>One may instead write :
<PRE>
public void doThis(String aText){
Args.checkForContent(aText);
//..main body elided
}
</PRE>
*/
public final class Args {
/**
If <code>aText</code> does not satisfy {@link Util#textHasContent}, then
throw an <code>IllegalArgumentException</code>.
<P>Most text used in an application is meaningful only if it has visible content.
*/
public static void checkForContent(String aText){
if( ! Util.textHasContent(aText) ){
throw new IllegalArgumentException("Text has no visible content");
}
}
/**
If {@link Util#isInRange} returns <code>false</code>, then
throw an <code>IllegalArgumentException</code>.
@param aLow is less than or equal to <code>aHigh</code>.
*/
public static void checkForRange(int aNumber, int aLow, int aHigh) {
if ( ! Util.isInRange(aNumber, aLow, aHigh) ) {
throw new IllegalArgumentException(aNumber + " not in range " + aLow + ".." +
aHigh);
}
}
/**
If <tt>aNumber</tt> is less than <tt>1</tt>, then throw an
<tt>IllegalArgumentException</tt>.
*/
public static void checkForPositive(int aNumber) {
if (aNumber < 1) {
134

Collected Java Practices

throw new IllegalArgumentException(aNumber + " is less than 1");


}
}
/**
If {@link Util#matches} returns <tt>false</tt>, then
throw an <code>IllegalArgumentException</code>.
*/
public static void checkForMatch(Pattern aPattern, String aText){
if (! Util.matches(aPattern, aText)){
throw new IllegalArgumentException(
"Text " + Util.quote(aText) + " does not match '" +aPattern.pattern()+ "'"
);
}
}
/**
If <code>aObject</code> is null, then throw a <code>NullPointerException</code>.
<P>Use cases :
<pre>
doSomething( Football aBall ){
//1. call some method on the argument :
//if aBall is null, then exception is automatically thrown, so
//there is no need for an explicit check for null.
aBall.inflate();
//2. assign to a corresponding field (common in constructors):
//if aBall is null, no exception is immediately thrown, so
//an explicit check for null may be useful here
Args.checkForNull( aBall );
fBall = aBall;
//3. pass on to some other method as parameter :
//it may or may not be appropriate to have an explicit check
//for null here, according the needs of the problem
Args.checkForNull( aBall ); //??
fReferee.verify( aBall );
}
</pre>
*/
public static void checkForNull(Object aObject) {
if (aObject == null) {
throw new NullPointerException();
}
}
// PRIVATE
private Args(){
//empty - prevent construction
}
}

See Also :
Model Objects

Exception translation
Occasionally, it's appropriate to translate one type of exception into another.
The data layer, for example, can profit from this technique. Here, the data layer seeks to hide almost all
of its implementation details from other parts of the program. It even seeks to hide the basic persistence
mechanism - whether or not a database or an ad hoc file scheme is used, for example.
135

Collected Java Practices

However, every persistence style has specific exceptions - SQLException for databases, and
IOException for files, for example. If the rest of the program is to remain truly ignorant of the
persistence mechanism, then these exceptions cannot be allowed to propagate outside the data layer, and
must be translated into some higher level abstraction - DataAccessException , say.
See the data exception wrapping topic for an extended example.
See Also :
Be specific in throws clause
Data exception wrapping

Exceptions and control flow


When an exception occurs within a method, the control flow defined by Java can take several forms.
If an exception occurs outside a try..catch block, the exception simply propagates "up" to the caller.
If an exception occurs inside a try..catch block, then the control flow can take different forms. Here
are the cases, along with indicators for which code blocks are included in the control flow:

Case
try throws no exception
try throws a handled exception
try throws an unhandled exception

try
y
y
y

catch
y
-

finally
y
y
y

bottom
y
y
-

Here, "bottom" refers simply to any code which follows the finally block, as shown here :
final class Bottom {
void doStuff() {
try {
//..elided
}
catch( Exception ex ) {
//..elided
}
finally {
//..elided
}
//any code appearing here, after the finally
//block, is "bottom" code
}
}

There is a misconception - especially common among C programmers migrating to Java - that exceptions
can be used to define ordinary control flow. This is a misuse of the idea of exceptions, which are meant
only for defects or for items outside the direct control of the program.
See Also :

136

Collected Java Practices

Checked versus unchecked exceptions


Avoid basic style errors

Finally and catch


The finally block is used to ensure resources are recovered regardless of any problems that may occur.
There are several variations for using the finally block, according to how exceptions are handled. (See
the excellent book The Java Programming Language by Arnold, Gosling, and Holmes for related
information.)
If you're using JDK 7+, then most uses of the finally block can be eliminated, simply by using a trywith-resources statement. If a resource doesn't implement AutoCloseable , then a finally block will still
be needed, even in JDK 7+. Two examples would be:
resources related to threads
resources related to a Graphics object
JDK < 7, and resources that aren't AutoCloseable
The following examples are appropriate for older versions of the JDK. They are also appropriate in JDK
7+, but only for resources that don't implement AutoCloseable .
Style 1
If a method throws all exceptions, then it may use a finally with no catch :
import java.io.*;
/** Before JDK 7. */
public final class SimpleFinally {
public static void main(String... aArgs) throws IOException {
simpleFinally("C:\\Temp\\test.txt");
}
private static void simpleFinally(String aFileName) throws IOException {
//If this line throws an exception, then neither the try block
//nor the finally block will execute.
//That's a good thing, since reader would be null.
BufferedReader reader = new BufferedReader(new FileReader(aFileName));
try {
//Any exception in the try block will cause the finally block to execute
String line = null;
while ((line = reader.readLine()) != null) {
//process the line...
}
}
finally {
//The reader object will never be null here.
//This finally is only entered after the try block is
//entered. But, it's NOT POSSIBLE to enter the try block
//with a null reader object.
reader.close();
}
}
}

Style 2
If a method handles all of the checked exceptions that may be thrown by its implementation, then an
interesting variation is to nest a try..finally within a try..catch . This style is particularly useful
137

Collected Java Practices

when the finally block throws the same exceptions as the rest of the code (which is common with
java.io operations.) Although this style may seem slightly complex, it appears to be superior to
alternative styles:
import java.io.*;
import java.util.logging.*;
/** Before JDK 7. */
public final class NestedFinally {
public static void main(String... aArgs) {
nestedFinally("C:\\Temp\\test.txt");
}
private static void nestedFinally(String aFileName) {
try {
//If the constructor throws an exception, the finally block will NOT execute
BufferedReader reader = new BufferedReader(new FileReader(aFileName));
try {
String line = null;
while ((line = reader.readLine()) != null) {
//process the line...
}
}
finally {
//no need to check for null
//any exceptions thrown here will be caught by
//the outer catch block
reader.close();
}
}
catch(IOException ex){
fLogger.severe("Problem occured : " + ex.getMessage());
}
}
private static final Logger fLogger =
Logger.getLogger(NestedFinally.class.getPackage().getName())
;
}

Style 3
A more verbose style places a catch within the finally. This style is likely the least desirable, since it
has the most blocks:
import
import
import
import

java.io.BufferedReader;
java.io.FileReader;
java.io.IOException;
java.util.logging.Logger;

/** Before JDK 7. */


public final class CatchInsideFinally {
public static void main(String... aArgs) {
catchInsideFinally("C:\\Temp\\test.txt");
}
private static void catchInsideFinally(String aFileName) {
//Declared here to be visible from finally block
BufferedReader reader = null;
try {
//if this line fails, finally will be executed, and reader will be null
reader = new BufferedReader(new FileReader(aFileName));
String line = null;
while ( (line = reader.readLine()) != null ) {
//process the line...
}
}
catch(IOException ex){
fLogger.severe("Problem occured : " + ex.getMessage());
}
138

Collected Java Practices

finally {
try {
//need to check for null
if ( reader != null ) {
reader.close();
}
}
catch(IOException ex){
fLogger.severe("Problem occured. Cannot close reader : " + ex.getMessage());
}
}
}
private static final Logger fLogger =
Logger.getLogger(CatchInsideFinally.class.getPackage().getName())
;
}

See Also :
Always close streams
Recovering resources
Use finally to unlock

Javadoc all exceptions


All exceptions thrown by a method can be documented with the @throws javadoc comment (same as
@exception ). Some argue that an@throws should be included for every thrown exception, since it's the
only way to clearly state all requirements made upon the caller (the pre-conditions). However, others
disagree.
If all null object parameters passed to class methods cause a NullPointerException , then it's
acceptable to state this once in the class javadoc comment, instead of repeating it for every method.
Another alternative is to state in overview.html (javadoc's summary description of an application) that
all parameters are to be considered as non-null unless explicitly stated otherwise.
Example
This example has an unusually large number of pre-conditions. Remarks regarding
NullPointerException are placed in the class level javadoc comment.
Note that the unchecked SecurityException is thrown indirectly, through the calls to the methods of the
File class.
import java.io.*;
/**
* If a null object parameter is passed to any method, then a
* <tt>NullPointerException</tt> will be thrown.
*/
public final class WriteTextFile {
//..other methods elided
/**
* Change the contents of a text file in its entirety, overwriting any
* existing text.
*
* @param aFile is an existing file (not a directory) which can be written.
139

Collected Java Practices

* @param aContents is the text to be placed into aFile.


*
* @exception FileNotFoundException if aFile does not exist.
* @exception IOException if stream to aFile cannot be written to or closed.
*
* @exception IllegalArgumentException if aFile is a directory.
* @exception IllegalArgumentException if aFile cannot be written.
* @exception SecurityException if a SecurityManager exists and
* disallows read or write access to aFile.
*/
static public void setContents(File aFile, String aContents)
throws FileNotFoundException, IOException {
if (aFile == null) {
throw new NullPointerException("File must not be null.");
}
if (aContents == null) {
throw new NullPointerException("Contents must not be null.");
}
if (!aFile.exists()) {
throw new FileNotFoundException ("File does not exist: " + aFile);
}
if (!aFile.isFile()) {
throw new IllegalArgumentException("Must not be a directory: " + aFile);
}
if (!aFile.canWrite()) {
throw new IllegalArgumentException("File cannot be written: " + aFile);
}
try (Writer output = new BufferedWriter(new FileWriter(aFile))) {
output.write(aContents);
}
}
public static void main (String... aArguments) throws IOException {
File testFile = new File("C:\\Temp\\blah.txt");
setContents(testFile, "blah blah blah");
}
}

See Also :
Validate method arguments
Avoid @throws in javadoc

Pass all pertinent data to exceptions


When an exception occurs, it's important that all pertinent data be passed to the exception's constructor.
Such data is often critical for understanding and solving the problem, and can greatly reduce the time
needed to find a solution.
The this reference is sometimes useful for this purpose, since toString is implicitly called. In addition,
if you are defining exception classes yourself, you may even design your constructors to force the caller
to pass the pertinent data.
Uninformative stack traces are very frustrating for the maintainer, and have been known to inspire
unpleasant cursing.
Example
public final class RangeChecker {

140

Collected Java Practices

/**
* Return <tt>true</tt> only if <tt>aNumber</tt> is in the range
* <tt>aLow..aHigh</tt> (inclusive).
*
* @param <tt>aLow</tt> less than or equal to <tt>aHigh</tt>.
*/
static public boolean isInRange(int aNumber, int aLow, int aHigh){
if (aLow > aHigh) {
throw new IllegalArgumentException("Low:" + aLow + " greater than High:" +
aHigh);
}
return (aLow <= aNumber && aNumber <= aHigh);
}
}

Effective Java mentions IndexOutOfBoundsException as a counter-example. This exception does not


provide information regarding the relevant indexes, which would often be useful for solving the problem:
the offending index value
the minimum and maximum permitted index values

Stack trace as String


Converting a stack trace into a String is done with Throwable.printStackTrace(PrintWriter) .
If desired, a customized stack trace String can be defined by using the StackTraceElement class, as
shown below.
Example
import java.io.*;
/**
* Simple utilities to return the stack trace of an
* exception as a String.
*/
public final class StackTraceUtil {
public static String getStackTrace(Throwable aThrowable) {
Writer result = new StringWriter();
PrintWriter printWriter = new PrintWriter(result);
aThrowable.printStackTrace(printWriter);
return result.toString();
}
/**
* Defines a custom format for the stack trace as String.
*/
public static String getCustomStackTrace(Throwable aThrowable) {
//add the class name and any message passed to constructor
StringBuilder result = new StringBuilder( "BOO-BOO: " );
result.append(aThrowable.toString());
String NEW_LINE = System.getProperty("line.separator");
result.append(NEW_LINE);
//add each element of the stack trace
for (StackTraceElement element : aThrowable.getStackTrace()){
result.append(element);
result.append(NEW_LINE);
}
return result.toString();
}
/** Demonstrate output.

*/
141

Collected Java Practices

public static void main (String... aArguments){


Throwable throwable = new IllegalArgumentException("Blah");
System.out.println(getStackTrace(throwable));
System.out.println(getCustomStackTrace(throwable));
}
}

An example run of this class :


>java -cp . StackTraceUtil
java.lang.IllegalArgumentException: Blah
at StackTraceUtil.main(StackTraceUtil.java:42)
BOO-BOO: java.lang.IllegalArgumentException: Blah
StackTraceUtil.main(StackTraceUtil.java:42)

See Also :
Dump thread information

Use template for repeated try-catch


Java's try-catch blocks are particularly common when using APIs which give an important role to
checked exceptions (such as SQLException in JDBC). When using such an API, many published
examples simply repeat the same try-catch code structure whenever necessary. However, it's simple to
eliminate such code repetition using the template method pattern. The idea is to define the structure of the
try-catch block in one place, in an abstract base class (ABC). Such an ABC is then extended whenever
that particular try-catch block is needed. The concrete implementation of such an ABC will often have
simple, "straight line" code.
An extended example of this technique is given in the Use template for transactions topic.
See Also :
Template method
Use template for transactions

Always close streams


When you use the try-with-resources statement correctly, then you will never have to close streams
explicitly. (Indeed, that's the reason try-with-resources was added in the first place.)
However, you may see still see code that either doesn't use a modern JDK, or fails to take advantage of
try-with-resources.
JDK < 7
Streams represent resources which you must always clean up explicitly, by calling the close method.

142

Collected Java Practices

Some java.io classes (apparently just the output classes) include a flush method. When a close
method is called on a such a class, it automatically performs a flush . There is no need to explicitly call
flush before calling close .
One stream can be chained to another by passing it to the constructor of some second stream. When this
second stream is closed, then it automatically closes the original underlying stream as well.
If multiple streams are chained together, then closing the one which was the last to be constructed, and is
thus at the highest level of abstraction, will automatically close all the underlying streams. So, one only
has to call close on one stream in order to close (and flush, if applicable) an entire series of related
streams.
Example
import java.io.*;
import java.util.*;
import java.util.logging.*;
/** JDK before version 7. */
public class ExerciseSerializable {
public static void main(String... aArguments) {
//create a Serializable List
List<String> quarks = Arrays.asList(
"up", "down", "strange", "charm", "top", "bottom"
);
//serialize the List
//note the use of abstract base class references
try{
//use buffering
OutputStream file = new FileOutputStream("quarks.ser");
OutputStream buffer = new BufferedOutputStream(file);
ObjectOutput output = new ObjectOutputStream(buffer);
try{
output.writeObject(quarks);
}
finally{
output.close();
}
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform output.", ex);
}
//deserialize the quarks.ser file
//note the use of abstract base class references
try{
//use buffering
InputStream file = new FileInputStream("quarks.ser");
InputStream buffer = new BufferedInputStream(file);
ObjectInput input = new ObjectInputStream (buffer);
try{
//deserialize the List
List<String> recoveredQuarks = (List<String>)input.readObject();
//display its data
for(String quark: recoveredQuarks){
System.out.println("Recovered Quark: " + quark);
}
}
finally{
input.close();
}
}
catch(ClassNotFoundException ex){
fLogger.log(Level.SEVERE, "Cannot perform input. Class not found.", ex);
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform input.", ex);
}
}
143

Collected Java Practices

// PRIVATE
//Use Java's logging facilities to record exceptions.
//The behavior of the logger can be configured through a
//text file, or programmatically through the logging API.
private static final Logger fLogger =
Logger.getLogger(ExerciseSerializable.class.getPackage().getName())
;
}

See Also :
Finally and catch
Reading and writing text files
Recovering resources

Buffering usually appropriate


Unbuffered input/output classes operate only on one byte at a time, while buffered streams interact with
the disk in larger-sized chunks. Buffering input/output is usually recommended, since it's generally faster,
and it should likely be the default style.
With small files, buffering usually makes little or no difference. However, it can be hard to predict if a
file will always remain small.
You can run the following demo class on your host, to look for differences between buffered and
unbuffered input. With large files (~1 Meg), you might see a difference of 2-3 times. (In Java Platform
Performance by Wilson and Kesselman, an example using a 370K JPEG file has a gain in execution
speed of 83x!)
import java.io.*;
/** JDK 6- */
public final class BufferDemo {
public static void main (String... aArguments) {
File file = new File("C:\\Temp\\blah.txt");
verifyFile( file );
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
readWithBuffer(file);
stopwatch.stop();
System.out.println("With buffering: " + stopwatch);
stopwatch.start();
readWithoutBuffer(file);
stopwatch.stop();
System.out.println("Without buffering: " + stopwatch);
}
/**
* @param aFile is a file which already exists and can be read.
*/
static public void readWithBuffer(File aFile) {
try {
//use buffering, with default buffer size of 8K
Reader input = new BufferedReader(new FileReader(aFile));
try {
144

Collected Java Practices

int data = 0;
while ((data = input.read()) != -1){
//do nothing
}
}
finally {
input.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
}
/**
* @param aFile is an existing file which can be read.
*/
static public void readWithoutBuffer(File aFile) {
try {
Reader input = new FileReader(aFile);
try {
//do not use buffering
int data = 0;
while ((data = input.read()) != -1){
//do nothing
}
}
finally {
input.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
}
private static void verifyFile(File aFile) {
if (aFile == null) {
throw new IllegalArgumentException("File should not be null.");
}
if (!aFile.exists()) {
throw new IllegalArgumentException ("File does not exist: " + aFile);
}
if (!aFile.isFile()) {
throw new IllegalArgumentException("Should not be a directory: " + aFile);
}
if (!aFile.canWrite()) {
throw new IllegalArgumentException("File cannot be written: " + aFile);
}
}
}

See Also :
Time execution speed

Console input
The Console class allows the user to interact with a simple console application, using textual commands
that you define.
Here's a simple example of its use:
import java.util.Arrays;
import java.io.Console;

145

Collected Java Practices

/**
* Simple interactive console application.
* Uses the java.io.Console class of Java 6.
*/
public final class Console6 {
public static final
Console console =
//read user name,
String username =

void main(String... aArgs){


System.console();
using java.util.Formatter syntax :
console.readLine("User Name? ");

//read the password, without echoing the output


char[] password = console.readPassword("Password? ");
//verify user name and password using some mechanism (elided)
//the javadoc for the Console class recommends "zeroing-out" the password
//when finished verifying it :
Arrays.fill(password, ' ');
console.printf("Welcome, %1$s.", username);
console.printf(fNEW_LINE);
String className = console.readLine("Please enter a package-qualified class
name:");
Class theClass = null;
try {
theClass = Class.forName(className);
console.printf("The inheritance tree: %1$s", getInheritanceTree(theClass));
}
catch(ClassNotFoundException ex){
console.printf("Cannot find that class.");
}
//this version just exits, without asking the user for more input
console.printf("Bye.");
}
// PRIVATE
private static final String fNEW_LINE = System.getProperty("line.separator");
private static String getInheritanceTree(Class aClass){
StringBuilder superclasses = new StringBuilder();
superclasses.append(fNEW_LINE);
Class theClass = aClass;
while (theClass != null) {
superclasses.append(theClass);
superclasses.append(fNEW_LINE);
theClass = theClass.getSuperclass();
}
superclasses.append(fNEW_LINE);
return superclasses.toString();
}
}

An example run:
>java Console6
User Name? john
Password?
Welcome, john.
Please enter a package-qualified class name:java.util.ArrayList
The inheritance tree:
class java.util.ArrayList
class java.util.AbstractList
class java.util.AbstractCollection
class java.lang.Object
Bye.

JDK < 6
The Console class was added in Java 6. The following is an extended example of using an older version
of the JDK. Here, input is read from the console in a continuous loop. As well, it has separated the
146

Collected Java Practices

problem into several parts, such that some parts can be reused in other console applications.
As in the previous example, the user inputs a package-qualified class name, and the corresponding
inheritance tree is displayed.
An example run:
>java -cp . Console InheritanceInterpreter
Please enter a class name>as;k
Invalid. Example:"java.lang.String">java.lang.String
The inheritance tree:
class java.lang.String
class java.lang.Object
Please enter a class name>
Invalid. Example:"java.lang.String">
Invalid. Example:"java.lang.String">....
Invalid. Example:"java.lang.String">a;lskf
Invalid. Example:"java.lang.String">java.sql.SQLWarning
The inheritance tree:
class java.sql.SQLWarning
class java.sql.SQLException
class java.lang.Exception
class java.lang.Throwable
class java.lang.Object
Please enter a class name>java.util.GregorianCalendar
The inheritance tree:
class java.util.GregorianCalendar
class java.util.Calendar
class java.lang.Object
Please enter a class name>exit
Bye.
import
import
import
import
import
import

java.io.BufferedReader;
java.io.IOException;
java.io.InputStreamReader;
java.io.Reader;
java.util.ArrayList;
java.util.List;

/**
* Sends text back and forth between the command line and an
* Interpreter. JDK less than 6.
*/
public final class Console {
/**
* Build and launch a specific <code>Interpreter</code>, whose
* package-qualified name is passed in on the command line.
*/
public static void main(String... aArguments) {
try {
Class theClass = Class.forName(aArguments[0]);
Interpreter interpreter = (Interpreter)theClass.newInstance();
Console console = new Console(interpreter);
console.run();
}
catch (ClassNotFoundException ex){
System.err.println(ex + " Interpreter class must be in class path.");
}
catch(InstantiationException ex){
System.err.println(ex + " Interpreter class must be concrete.");
}
catch(IllegalAccessException ex){
147

Collected Java Practices

System.err.println(ex + " Interpreter class must have a no-arg constructor.");


}
}
public Console(Interpreter aInterpreter) {
if (aInterpreter == null) {
throw new IllegalArgumentException("Cannot be null.");
}
fInterpreter = aInterpreter;
}
/**
* Display a prompt, wait for a full line of input, and then parse
* the input using an Interpreter.
*
* Exit when <code>Interpreter.parseInput</code> returns true.
*/
public void run() {
display(fInterpreter.getHelloPrompt());
//pass each line of input to fInterpreter, and display
//fInterpreter's result
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
BufferedReader stdin = new BufferedReader(inputStreamReader);
boolean hasRequestedQuit = false;
String line = null;
List<Object> result = new ArrayList<Object>();
try {
while(!hasRequestedQuit){
line = stdin.readLine();
//note that "result" is passed as an "out" parameter
hasRequestedQuit = fInterpreter.parseInput(line, result);
display(result);
result.clear();
}
}
catch (IOException ex) {
System.err.println(ex);
}
finally {
display(fBYE);
shutdown(stdin);
}
}
// PRIVATE
private static final String fBYE = "Bye.";
private Interpreter fInterpreter;
/**
* Display some text to stdout.
* The result of toString() is used.
*/
private void display(Object aText){
System.out.print(aText.toString());
System.out.flush();
}
/**
* Display a List of objects as text in stdout, in the order returned
* by the iterator of aText.
*/
private void display(List<Object> aText) {
for(Object item : aText){
display(item);
}
}
private void shutdown(Reader aStdin){
try {
aStdin.close();
}
catch (IOException ex){
System.err.println(ex);
}
}
}

148

Collected Java Practices

import java.util.*;
/**
* Parse a line of text and return a result.
*/
public interface Interpreter {
/**
* @param aLine is non-null.
* @param aResult is a non-null, empty List which acts as an "out"
* parameter; when returned, aResult must contain a non-null, non-empty
* List of items which all have a <code>toString</code> method, to be used
* for displaying a result to the user.
*
* @return true if the user has requested to quit the Interpreter.
* @exception IllegalArgumentException if a param does not comply.
*/
boolean parseInput(String aLine, List<Object> aResult);
/**
* Return the text to be displayed upon start-up of the Interpreter.
*/
String getHelloPrompt();
}

import java.util.*;
/**
* Given a package-qualified class name, return the names of the classes in
* the inheritance tree.
*/
public final class InheritanceInterpreter implements Interpreter {
/**
* @param aLine is a non-null name of a class.
* @param aResult is a non-null, empty List which acts as an "out"
* parameter; when returned, aResult must contain a non-null, non-empty
* List of class names which form the inheritance tree of the input class.
*
* @return true if the user has requeseted to quit the Interpreter.
* @exception IllegalArgumentException if a param does not comply.
*/
public boolean parseInput (String aLine, final List aResult) {
if (aResult == null) {
throw new IllegalArgumentException("Result param cannot be null.");
}
if (!aResult.isEmpty()){
throw new IllegalArgumentException("Result param must be empty.");
}
if (aLine == null) {
throw new IllegalArgumentException("Line must not be null.");
}
boolean hasRequestedQuit = aLine.trim().equalsIgnoreCase(fQUIT) ||
aLine.trim().equalsIgnoreCase(fEXIT);
if (hasRequestedQuit) {
aResult.add(fNEW_LINE);
}
else {
try {
Class theClass = Class.forName(aLine);
StringBuilder superclasses = new StringBuilder();
superclasses.append(fHEADER);
superclasses.append(fNEW_LINE);
while (theClass != null) {
superclasses.append(theClass);
superclasses.append(fNEW_LINE);
theClass = theClass.getSuperclass();
}
aResult.add(superclasses);
aResult.add(fDEFAULT_PROMPT);
149

Collected Java Practices

}
catch (ClassNotFoundException ex){
//recover by asking the user for corrected input
aResult.clear();
aResult.add(fERROR_PROMPT);
}
}
assert !aResult.isEmpty(): "Result must be non-empty.";
return hasRequestedQuit;
}
/**
* Return the text to be displayed upon start-up of the Interpreter.
*/
public String getHelloPrompt() {
return fHELLO_PROMPT;
}
// PRIVATE
private static final String fHELLO_PROMPT = "Please enter a class name>";
private static final String fDEFAULT_PROMPT = "Please enter a class name>";
private static final String fERROR_PROMPT = "Invalid.
Example:\"java.lang.String\">";
private static final String fHEADER = "The inheritance tree:";
private static final String fQUIT = "quit";
private static final String fEXIT = "exit";
private static final String fNEW_LINE = System.getProperty("line.separator");
}

See Also :
Get size of object in memory
Construct Object using class name

Copy a file
In JDK 7+, copying a file is a simple operation, involving a single call to File.copy.
Example
import
import
import
import
import
import

java.io.IOException;
java.nio.file.CopyOption;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;
java.nio.file.StandardCopyOption;

/** JDK 7+. */


public final class CopyFilesNew {
public static void main(String... aArgs) throws IOException {
Path FROM = Paths.get("C:\\Temp\\from.txt");
Path TO = Paths.get("C:\\Temp\\to.txt");
//overwrite existing file, if exists
CopyOption[] options = new CopyOption[]{
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES
};
Files.copy(FROM, TO, options);
}
}

150

Collected Java Practices

In older JDKs, however, copying a file involves a lot more code. It can be done either with
FileChannels or with basic streams. The FileChannel technique is usually faster.
Here's an example showing both techniques.
import
import
import
import
import
import
import
import
import
import

java.io.BufferedInputStream;
java.io.BufferedOutputStream;
java.io.File;
java.io.FileInputStream;
java.io.FileNotFoundException;
java.io.FileOutputStream;
java.io.IOException;
java.io.InputStream;
java.io.OutputStream;
java.nio.channels.FileChannel;

/**
Copy files, using two techniques, FileChannels and streams.
Using FileChannels is usually faster than using streams.
JDK 6-.
*/
public final class CopyFiles {
/* Change these settings before running this class. */
/** The file to be copied. */
public static final String INPUT_FILE = "C:\\TEMP\\cottage.jpg";
/**
The name of the copy to be created by this class.
If this file doesn't exist, it will be created, along with any
needed parent directories.
*/
public static final String COPY_FILE_TO = "C:\\TEMP10\\cottage_2.jpg";
/** Run the example. */
public static void main(String... aArgs) throws IOException{
File source = new File(INPUT_FILE);
File target = new File(COPY_FILE_TO);
CopyFiles test = new CopyFiles();
test.copyWithChannels(source, target, false);
//test.copyWithStreams(source, target, false);
log("Done.");
}
/** This may fail for VERY large files. */
private void copyWithChannels(File aSourceFile, File aTargetFile, boolean aAppend) {
log("Copying files with channels.");
ensureTargetDirectoryExists(aTargetFile.getParentFile());
FileChannel inChannel = null;
FileChannel outChannel = null;
FileInputStream inStream = null;
FileOutputStream outStream = null;
try{
try {
inStream = new FileInputStream(aSourceFile);
inChannel = inStream.getChannel();
outStream = new FileOutputStream(aTargetFile, aAppend);
outChannel = outStream.getChannel();
long bytesTransferred = 0;
//defensive loop - there's usually only a single iteration :
while(bytesTransferred < inChannel.size()){
bytesTransferred += inChannel.transferTo(0, inChannel.size(), outChannel);
}
}
finally {
//being defensive about closing all channels and streams
if (inChannel != null) inChannel.close();
if (outChannel != null) outChannel.close();
if (inStream != null) inStream.close();
if (outStream != null) outStream.close();
}
}
catch (FileNotFoundException ex){
151

Collected Java Practices

log("File not found: " + ex);


}
catch (IOException ex){
log(ex);
}
}
private void copyWithStreams(File aSourceFile, File aTargetFile, boolean aAppend) {
log("Copying files with streams.");
ensureTargetDirectoryExists(aTargetFile.getParentFile());
InputStream inStream = null;
OutputStream outStream = null;
try{
try {
byte[] bucket = new byte[32*1024];
inStream = new BufferedInputStream(new FileInputStream(aSourceFile));
outStream = new BufferedOutputStream(new FileOutputStream(aTargetFile,
aAppend));
int bytesRead = 0;
while(bytesRead != -1){
bytesRead = inStream.read(bucket); //-1, 0, or more
if(bytesRead > 0){
outStream.write(bucket, 0, bytesRead);
}
}
}
finally {
if (inStream != null) inStream.close();
if (outStream != null) outStream.close();
}
}
catch (FileNotFoundException ex){
log("File not found: " + ex);
}
catch (IOException ex){
log(ex);
}
}
private void ensureTargetDirectoryExists(File aTargetDir){
if(!aTargetDir.exists()){
aTargetDir.mkdirs();
}
}
private static void log(Object aThing){
System.out.println(String.valueOf(aThing));
}
}

See Also :
Always close streams
Reading and writing binary files

Reading and writing binary files


In JDK 7, the most important classes for binary files are:
Paths and Path - file locations/names, but not their content.
Files - operations on file content.
the File.toPath method, which lets older code interact nicely

with the newer java.nio API.

In addition, the following classes are also commonly used with binary files, for both JDK 7 and earlier
152

Collected Java Practices

versions:
Input

Output

FileInputStream
BufferedInputStream
ByteArrayInputStream
DataInput

FileOutputStream
BufferedOutputStream
ByteArrayOutputStream
DataOutput

When reading and writing binary files:


it's almost always a good idea to use buffering (default buffer size is 8K)
it's often possible to use references to abstract base classes, instead of references to specific
concrete classes
there's always a need to pay attention to exceptions (in particular, IOException and
FileNotFoundException)
Example - Small Files
import
import
import
import

java.io.IOException;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;

/** JDK 7+. */


public class SmallBinaryFiles {
public static void main(String... aArgs) throws IOException{
SmallBinaryFiles binary = new SmallBinaryFiles();
byte[] bytes = binary.readSmallBinaryFile(FILE_NAME);
log("Small - size of file read in:" + bytes.length);
binary.writeSmallBinaryFile(bytes, OUTPUT_FILE_NAME);
}
final static String FILE_NAME = "C:\\Temp\\cottage.jpg";
final static String OUTPUT_FILE_NAME = "C:\\Temp\\cottage_output.jpg";
byte[] readSmallBinaryFile(String aFileName) throws IOException {
Path path = Paths.get(aFileName);
return Files.readAllBytes(path);
}
void writeSmallBinaryFile(byte[] aBytes, String aFileName) throws IOException {
Path path = Paths.get(aFileName);
Files.write(path, aBytes); //creates, overwrites
}
private static void log(Object aMsg){
System.out.println(String.valueOf(aMsg));
}
}

If JDK 7's try-with-resources isn't available to you, then, you need to be careful with the close method:
it usually needs to be called, or else resources will leak.
for streams that don't use the disk or network, such as the ByteArrayXXX streams, the close
operation is a no-operation. In these cases, you don't need to call close .
close will automatically flush the stream, if necessary.
calling close on a "wrapper" stream will automatically call close on its underlying stream.
closing a stream a second time is a no-operation.
Example - JDK < 7

153

Collected Java Practices

This example reads and writes binary data, moving it from disk to memory, and then back again.
import
import
import
import
import
import
import
import
import
import

java.io.BufferedInputStream;
java.io.BufferedOutputStream;
java.io.ByteArrayOutputStream;
java.io.File;
java.io.FileInputStream;
java.io.FileNotFoundException;
java.io.FileOutputStream;
java.io.IOException;
java.io.InputStream;
java.io.OutputStream;

/**
Converting binary data into different forms.
<P>Reads binary data into memory, and writes it back out.
(If your're actually copying a file, there are better ways to do this.)
<P>Buffering is used when reading and writing files, to minimize the number
of interactions with the disk.
*/
public final class BytesStreamsAndFiles {
/** Change these settings before running this class. */
private static final String INPUT_FILE_NAME = "C:\\TEMP\\cottage.jpg";
private static final String OUTPUT_FILE_NAME = "C:\\TEMP\\cottage_copy.jpg";
/** Run the example. */
public static void main(String... aArgs) {
BytesStreamsAndFiles test = new BytesStreamsAndFiles();
//read in the bytes
byte[] fileContents = test.read(INPUT_FILE_NAME);
//test.readAlternateImpl(INPUT_FILE_NAME);
//write it back out to a different file name
test.write(fileContents, OUTPUT_FILE_NAME);
}
/** Read the given binary file, and return its contents as a byte array.*/
byte[] read(String aInputFileName){
log("Reading in binary file named : " + aInputFileName);
File file = new File(aInputFileName);
log("File size: " + file.length());
byte[] result = new byte[(int)file.length()];
try {
InputStream input = null;
try {
int totalBytesRead = 0;
input = new BufferedInputStream(new FileInputStream(file));
while(totalBytesRead < result.length){
int bytesRemaining = result.length - totalBytesRead;
//input.read() returns -1, 0, or more :
int bytesRead = input.read(result, totalBytesRead, bytesRemaining);
if (bytesRead > 0){
totalBytesRead = totalBytesRead + bytesRead;
}
}
/*
the above style is a bit tricky: it places bytes into the 'result' array;
'result' is an output parameter;
the while loop usually has a single iteration only.
*/
log("Num bytes read: " + totalBytesRead);
}
finally {
log("Closing input stream.");
input.close();
}
}
catch (FileNotFoundException ex) {
log("File not found.");
}
catch (IOException ex) {
log(ex);
}
return result;
}
154

Collected Java Practices

/**
Write a byte array to the given file.
Writing binary data is significantly simpler than reading it.
*/
void write(byte[] aInput, String aOutputFileName){
log("Writing binary file...");
try {
OutputStream output = null;
try {
output = new BufferedOutputStream(new FileOutputStream(aOutputFileName));
output.write(aInput);
}
finally {
output.close();
}
}
catch(FileNotFoundException ex){
log("File not found.");
}
catch(IOException ex){
log(ex);
}
}
/** Read the given binary file, and return its contents as a byte array.*/
byte[] readAlternateImpl(String aInputFileName){
log("Reading in binary file named : " + aInputFileName);
File file = new File(aInputFileName);
log("File size: " + file.length());
byte[] result = null;
try {
InputStream input = new BufferedInputStream(new FileInputStream(file));
result = readAndClose(input);
}
catch (FileNotFoundException ex){
log(ex);
}
return result;
}
/**
Read an input stream, and return it as a byte array.
Sometimes the source of bytes is an input stream instead of a file.
This implementation closes aInput after it's read.
*/
byte[] readAndClose(InputStream aInput){
//carries the data from input to output :
byte[] bucket = new byte[32*1024];
ByteArrayOutputStream result = null;
try {
try {
//Use buffering? No. Buffering avoids costly access to disk or network;
//buffering to an in-memory stream makes no sense.
result = new ByteArrayOutputStream(bucket.length);
int bytesRead = 0;
while(bytesRead != -1){
//aInput.read() returns -1, 0, or more :
bytesRead = aInput.read(bucket);
if(bytesRead > 0){
result.write(bucket, 0, bytesRead);
}
}
}
finally {
aInput.close();
//result.close(); this is a no-operation for ByteArrayOutputStream
}
}
catch (IOException ex){
log(ex);
}
return result.toByteArray();
}
private static void log(Object aThing){
System.out.println(String.valueOf(aThing));
}
155

Collected Java Practices

See Also :
Always close streams
Reading and writing text files
Minimize ripple effects
Copy a file

Reading and writing Serializable objects


Reading and writing Serializable objects is very similar to reading and writing text:
it' almost always a good idea to use buffering (default size is 8K)
it's often possible to use abstract base class references, instead of references to concrete classes
there's always a need to pay attention to exceptions; in particular, IOException and
ClassNotFoundException

Example
Here, a List of String objects is serialized and then deserialized.
import java.io.*;
import java.util.*;
import java.util.logging.*;
/**
Uses buffering, and abstract base classes.
JDK 7+.
*/
public class ExerciseSerializableNew {
public static void main(String... aArguments) {
//create a Serializable List
List<String> quarks = Arrays.asList(
"up", "down", "strange", "charm", "top", "bottom"
);
//serialize the List
try (
OutputStream file = new FileOutputStream("quarks.ser");
OutputStream buffer = new BufferedOutputStream(file);
ObjectOutput output = new ObjectOutputStream(buffer);
){
output.writeObject(quarks);
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform output.", ex);
}
//deserialize the quarks.ser file
try(
InputStream file = new FileInputStream("quarks.ser");
InputStream buffer = new BufferedInputStream(file);
ObjectInput input = new ObjectInputStream (buffer);
){
//deserialize the List
List<String> recoveredQuarks = (List<String>)input.readObject();
//display its data
for(String quark: recoveredQuarks){
System.out.println("Recovered Quark: " + quark);
}
}
156

Collected Java Practices

catch(ClassNotFoundException ex){
fLogger.log(Level.SEVERE, "Cannot perform input. Class not found.", ex);
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform input.", ex);
}
}
// PRIVATE
private static final Logger fLogger =
Logger.getLogger(ExerciseSerializableNew.class.getPackage().getName())
;
}

Example - JDK 6If try-with-resources is not available (JDK 6-), then you must be careful with the close method:
it always needs to be called, or else resources will leak
it will automatically flush the stream, if necessary
calling close on a "wrapper" stream will automatically call close on its underlying stream
closing a stream a second time has no consequence
Example
Here's the same example as above, but using JDK 6-.
import java.io.*;
import java.util.*;
import java.util.logging.*;
/** JDK before version 7. */
public class ExerciseSerializable {
public static void main(String... aArguments) {
//create a Serializable List
List<String> quarks = Arrays.asList(
"up", "down", "strange", "charm", "top", "bottom"
);
//serialize the List
//note the use of abstract base class references
try{
//use buffering
OutputStream file = new FileOutputStream("quarks.ser");
OutputStream buffer = new BufferedOutputStream(file);
ObjectOutput output = new ObjectOutputStream(buffer);
try{
output.writeObject(quarks);
}
finally{
output.close();
}
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform output.", ex);
}
//deserialize the quarks.ser file
//note the use of abstract base class references
try{
//use buffering
InputStream file = new FileInputStream("quarks.ser");
InputStream buffer = new BufferedInputStream(file);
ObjectInput input = new ObjectInputStream (buffer);
try{
//deserialize the List
157

Collected Java Practices

List<String> recoveredQuarks = (List<String>)input.readObject();


//display its data
for(String quark: recoveredQuarks){
System.out.println("Recovered Quark: " + quark);
}
}
finally{
input.close();
}
}
catch(ClassNotFoundException ex){
fLogger.log(Level.SEVERE, "Cannot perform input. Class not found.", ex);
}
catch(IOException ex){
fLogger.log(Level.SEVERE, "Cannot perform input.", ex);
}
}
// PRIVATE
//Use Java's logging facilities to record exceptions.
//The behavior of the logger can be configured through a
//text file, or programmatically through the logging API.
private static final Logger fLogger =
Logger.getLogger(ExerciseSerializable.class.getPackage().getName())
;
}

See Also :
Always close streams
Finally and catch
Recovering resources

Reading and writing text files


In JDK 7, the most important classes for text files are:
Paths and Path - file locations/names, but not their content.
Files - operations on file content.
StandardCharsets and Charset (an older class), for encodings of text files.
the File.toPath method, which lets older code interact nicely with the newer

java.nio API.

In addition, the following classes are also commonly used with text files, for both JDK 7 and earlier
versions:
Scanner - allows reading files in a compact
BufferedReader - readLine
BufferedWriter - write + newLine

way

When reading and writing text files:


it's often a good idea to use buffering (default size is 8K)
there's always a need to pay attention to exceptions (in particular, IOException and
FileNotFoundException)
Character Encoding
158

Collected Java Practices

In order to correctly read and write text files, you need to understand that those read/write operations
always use an implicit character encoding to translate raw bytes - the 1s and 0s - into text. When a text
file is saved, the tool that saves it must always use a character encoding (UTF-8 is recommended).
There's a problem, however. The character encoding is not, in general, explicit: it's not saved as part of
the file itself. Thus, a program that consumes a text file should know beforehand what its encoding is. If
it doesn't, then the best it can do is make an assumption. Problems with encoding usually show up as
weird characters in a tool that has read the file.
The FileReader and FileWriter classes are a bit tricky, since they implicitly use the system's default
character encoding. If this default is not appropriate, the recommended alternatives are, for example:
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader in = new InputStreamReader(fis, "UTF-8");
FileOutputStream fos = new FileOutputStream("test.txt");
OutputStreamWriter out = new OutputStreamWriter(fos, "UTF-8");
Scanner scanner = new Scanner(file, "UTF-8");

Example 1 - JDK 7+
import
import
import
import
import
import
import
import
import
import
import

java.io.BufferedReader;
java.io.BufferedWriter;
java.io.IOException;
java.nio.charset.Charset;
java.nio.charset.StandardCharsets;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;
java.util.Arrays;
java.util.List;
java.util.Scanner;

public class ReadWriteTextFileJDK7 {


public static void main(String... aArgs) throws IOException{
ReadWriteTextFileJDK7 text = new ReadWriteTextFileJDK7();
//treat as a small file
List<String> lines = text.readSmallTextFile(FILE_NAME);
log(lines);
lines.add("This is a line added in code.");
text.writeSmallTextFile(lines, FILE_NAME);
//treat as a large file - use some buffering
text.readLargerTextFile(FILE_NAME);
lines = Arrays.asList("Down to the Waterline", "Water of Love");
text.writeLargerTextFile(OUTPUT_FILE_NAME, lines);
}
final static String FILE_NAME = "C:\\Temp\\input.txt";
final static String OUTPUT_FILE_NAME = "C:\\Temp\\output.txt";
final static Charset ENCODING = StandardCharsets.UTF_8;
//For smaller files
/**
Note: the javadoc of Files.readAllLines says it's intended for small
files. But its implementation uses buffering, so it's likely good
even for fairly large files.
*/
List<String> readSmallTextFile(String aFileName) throws IOException {
Path path = Paths.get(aFileName);
return Files.readAllLines(path, ENCODING);
}
void writeSmallTextFile(List<String> aLines, String aFileName) throws IOException {
Path path = Paths.get(aFileName);
Files.write(path, aLines, ENCODING);
}

159

Collected Java Practices

//For larger files


void readLargerTextFile(String aFileName) throws IOException {
Path path = Paths.get(aFileName);
try (Scanner scanner = new Scanner(path, ENCODING.name())){
while (scanner.hasNextLine()){
//process each line in some way
log(scanner.nextLine());
}
}
}
void readLargerTextFileAlternate(String aFileName) throws IOException {
Path path = Paths.get(aFileName);
try (BufferedReader reader = Files.newBufferedReader(path, ENCODING)){
String line = null;
while ((line = reader.readLine()) != null) {
//process each line in some way
log(line);
}
}
}
void writeLargerTextFile(String aFileName, List<String> aLines) throws IOException {
Path path = Paths.get(aFileName);
try (BufferedWriter writer = Files.newBufferedWriter(path, ENCODING)){
for(String line : aLines){
writer.write(line);
writer.newLine();
}
}
}
private static void log(Object aMsg){
System.out.println(String.valueOf(aMsg));
}
}

Example 2 - JDK 7+
This example demonstrates using Scanner to read a file containing lines of structured data. One Scanner
is used to read in each line, and a second Scanner is used to parse each line into a simple name-value
pair. The Scanner class is only used for reading, not for writing.
import
import
import
import
import
import

java.io.IOException;
java.nio.charset.Charset;
java.nio.charset.StandardCharsets;
java.nio.file.Path;
java.nio.file.Paths;
java.util.Scanner;

/** Assumes UTF-8 encoding. JDK 7+. */


public class ReadWithScanner {
public static void main(String... aArgs) throws IOException {
ReadWithScanner parser = new ReadWithScanner("C:\\Temp\\test.txt");
parser.processLineByLine();
log("Done.");
}
/**
Constructor.
@param aFileName full name of an existing, readable file.
*/
public ReadWithScanner(String aFileName){
fFilePath = Paths.get(aFileName);
}
/** Template method that calls {@link #processLine(String)}. */
public final void processLineByLine() throws IOException {
try (Scanner scanner = new Scanner(fFilePath, ENCODING.name())){
160

Collected Java Practices

while (scanner.hasNextLine()){
processLine(scanner.nextLine());
}
}
}
/**
Overridable method for processing lines in different ways.
<P>This simple default implementation expects simple name-value pairs, separated by
an
'=' sign. Examples of valid input:
<tt>height = 167cm</tt>
<tt>mass = 65kg</tt>
<tt>disposition = "grumpy"</tt>
<tt>this is the name = this is the value</tt>
*/
protected void processLine(String aLine){
//use a second Scanner to parse the content of each line
Scanner scanner = new Scanner(aLine);
scanner.useDelimiter("=");
if (scanner.hasNext()){
//assumes the line has a certain structure
String name = scanner.next();
String value = scanner.next();
log("Name is : " + quote(name.trim()) + ", and Value is : " +
quote(value.trim()));
}
else {
log("Empty or invalid line. Unable to process.");
}
}
// PRIVATE
private final Path fFilePath;
private final static Charset ENCODING = StandardCharsets.UTF_8;
private static void log(Object aObject){
System.out.println(String.valueOf(aObject));
}
private String quote(String aText){
String QUOTE = "'";
return QUOTE + aText + QUOTE;
}
}

Example run of this class:


Name is
Name is
Name is
Name is
Done.

:
:
:
:

'height', and Value is : '167cm'


'mass', and Value is : '65kg'
'disposition', and Value is : '"grumpy"'
'this is the name', and Value is : 'this is the value'

Example 3 - JDK < 7


The try-with-resources feature is not available prior to JDK 7. In this case, you need to exercise care with
respect to the close method:
it always needs to be called, or else resources will leak
it will automatically flush the stream, if necessary
calling close on a "wrapper" stream will automatically call close on its underlying stream
closing a stream a second time has no consequence
when called on a Scanner, the close operation only works if the item passed to its constructor
implements Closeable . Warning: if you pass a File to a Scanner, you will not be able to close it!
Try using a FileReader instead.
Here's a fairly compact example (for JDK 1.5) of reading and writing a text file, using an explicit
161

Collected Java Practices

encoding. If you remove all references to encoding from this class, it will still work -- the system's
default encoding will simply be used instead.
import java.io.*;
import java.util.Scanner;
/**
Read and write a file using an explicit encoding.
JDK 1.5.
Removing the encoding from this code will simply cause the
system's default encoding to be used instead.
*/
public final class ReadWriteTextFileWithEncoding {
/** Requires two arguments - the file name, and the encoding to use. */
public static void main(String... aArgs) throws IOException {
String fileName = aArgs[0];
String encoding = aArgs[1];
ReadWriteTextFileWithEncoding test = new ReadWriteTextFileWithEncoding(
fileName, encoding
);
test.write();
test.read();
}
/** Constructor. */
ReadWriteTextFileWithEncoding(String aFileName, String aEncoding){
fEncoding = aEncoding;
fFileName = aFileName;
}
/** Write fixed content to the given file. */
void write() throws IOException {
log("Writing to file named " + fFileName + ". Encoding: " + fEncoding);
Writer out = new OutputStreamWriter(new FileOutputStream(fFileName), fEncoding);
try {
out.write(FIXED_TEXT);
}
finally {
out.close();
}
}
/** Read the contents of the given file. */
void read() throws IOException {
log("Reading from file.");
StringBuilder text = new StringBuilder();
String NL = System.getProperty("line.separator");
Scanner scanner = new Scanner(new FileInputStream(fFileName), fEncoding);
try {
while (scanner.hasNextLine()){
text.append(scanner.nextLine() + NL);
}
}
finally{
scanner.close();
}
log("Text read in: " + text);
}
// PRIVATE
private final String fFileName;
private final String fEncoding;
private final String FIXED_TEXT = "But soft! what code in yonder program breaks?";
private void log(String aMessage){
System.out.println(aMessage);
}
}

Example 4 - JDK < 7


This example uses FileReader and FileWriter , which implicitly use the system's default encoding. It
162

Collected Java Practices

also uses buffering. To make this example compatible with JDK 1.4, just change StringBuilder to
StringBuffer :
import java.io.*;
/** JDK 6 or before. */
public class ReadWriteTextFile {
/**
* Fetch the entire contents of a text file, and return it in a String.
* This style of implementation does not throw Exceptions to the caller.
*
* @param aFile is a file which already exists and can be read.
*/
static public String getContents(File aFile) {
//...checks on aFile are elided
StringBuilder contents = new StringBuilder();
try {
//use buffering, reading one line at a time
//FileReader always assumes default encoding is OK!
BufferedReader input = new BufferedReader(new FileReader(aFile));
try {
String line = null; //not declared within while loop
/*
* readLine is a bit quirky :
* it returns the content of a line MINUS the newline.
* it returns null only for the END of the stream.
* it returns an empty String if two newlines appear in a row.
*/
while (( line = input.readLine()) != null){
contents.append(line);
contents.append(System.getProperty("line.separator"));
}
}
finally {
input.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
return contents.toString();
}
/**
* Change the contents of text file in its entirety, overwriting any
* existing text.
*
* This style of implementation throws all exceptions to the caller.
*
* @param aFile is an existing file which can be written to.
* @throws IllegalArgumentException if param does not comply.
* @throws FileNotFoundException if the file does not exist.
* @throws IOException if problem encountered during write.
*/
static public void setContents(File aFile, String aContents)
throws FileNotFoundException, IOException {
if (aFile == null) {
throw new IllegalArgumentException("File should not be null.");
}
if (!aFile.exists()) {
throw new FileNotFoundException ("File does not exist: " + aFile);
}
if (!aFile.isFile()) {
throw new IllegalArgumentException("Should not be a directory: " + aFile);
}
if (!aFile.canWrite()) {
throw new IllegalArgumentException("File cannot be written: " + aFile);
}
//use buffering
Writer output = new BufferedWriter(new FileWriter(aFile));
try {
//FileWriter always assumes default encoding is OK!
output.write( aContents );
163

Collected Java Practices

}
finally {
output.close();
}
}
/** Simple test harness.
*/
public static void main (String... aArguments) throws IOException {
File testFile = new File("C:\\Temp\\blah.txt");
System.out.println("Original file contents: " + getContents(testFile));
setContents(testFile, "The content of this file has been overwritten...");
System.out.println("New file contents: " + getContents(testFile));
}
}

See Also :
Always close streams
Parse text
Minimize ripple effects
Reading and writing binary files
Open file in native directory

Business identifiers as String


Many problem domains use numbers as business identifiers. Credit card numbers, bank account numbers,
and the like, are often simply that - numbers.
It should always be kept in mind, however, that such items are primarily identifiers, and their numeric
character is usually completely secondary. Their primary function is to identify items in the problem
domain, not to represent quantities of any sort.
For example, it almost never makes sense to operate on numeric business identifiers as true numbers adding two account numbers, or multiplying an account number by -1, are meaningless operations. That
is, one can strongly argue that modeling an account number as a Integer is inappropriate, simply
because it does not behave as an Integer.
Furthermore, new business rules can occasionally force numeric business identifiers to be abandoned in
favor of alphanumeric ones. It seems prudent to treat the content of such a business identifier as a
business rule, subject to change. As usual, a program should minimize the ripple effects of such changes.
In addition, Strings can contain leading zeros, while numeric fields will remove them.
Thus, it seems best to avoid the natural tendency to model numeric business identifiers as Integer, and
to use String instead.
The above remarks apply to business identifiers, not primary keys. Primary keys should almost always be
numeric. Business identifiers are visible to end users, while primary keys are usually intended only for
internal use by the database. Many database designers consider it a dubious practice to use a business
identifer as an actual primary key.
See Also :
164

Collected Java Practices

Remember the basics of database design

Connection pools
Database connections:
sometimes take a relatively long time to create.
are always limited in number (most medium- and low-end servers have a maximum of ~100
connections).
are resources which must be systematically released when no longer in use.
The last point is critical: connections must always be properly released when no longer needed. Failure to
do so will eventually cause the application to "hang", and fail to respond to user actions. All code in your
application regarding connections should be treated with special care.
To ensure that database connections are used as efficiently as possible, many applications make use of a
connection pool, which can sometimes improve performance in a distributed application. An application
will use a connection pool as follows:
upon startup, the application creates a pool of database connections (or, the application's
environment may already include a connection pool).
during normal operation, the application simply fetches a connection from the pool every time a
database operation is performed. This is often done in a "just-in-time" manner. That is, the
application does not keep a long-lived reference to its connection objects, but will rather fetch, use,
and then close each connection in a single method.
when the calling code is finished with the connection, it calls a close method, but the underlying
connection is not actually closed - rather, it's simply returned to the connection pool.
when the application shuts down, the connection pool it created upon startup (if any) is shut down
as well.
A database driver may contain a connection pool as part of its services, through its implementation of the
javax.sql.DataSource interface. DataSource objects are used with JNDI.
The popular Tomcat tool implements JNDI, and connection pooling is described in its documentation. It's
not difficult to use.
See Also :
Get database connection
Encapsulate connections

Consider data layer tools


Many applications store data in a relational database. The standard JDBC API is used to interact with
relational databases, but it's a bit low level. It's not hard to define a few helper classes that will
encapsulate the bulk of repeated JDBC code. But even so, many applications will benefit from using
tools that operate on top of the JDBC API.
Examples of such tools include:
165

Collected Java Practices

Java Data Objects (JDO)


Java Persistence API (JPA)
MyBatis
Hibernate
These tools generally fall into two categories:
treat code and objects as primary (Java Persistence API and Hibernate, for example), and SQL as
secondary. These are usually referred to as Object-Relational Mapping (ORM) tools.
treat SQL as primary (IBatis and web4j, for example), and code as secondary.
Although ORM tools are currently popular, some argue that they should be avoided. In brief:
their performance is usually poorer than approaches based on SQL
there's usually no easy way to debug and tune queries
they usually don't support all relational databases
learning how to use them well is non-trivial
they reinvent the wheel by defining query APIs, which more or less reproduce what already exists
in SQL (an existing, widely used, and well understood language for the manipulation of data)
they sometimes prevent you from defining your Model Objects as immutable, or otherwise leak
into your Model Object
As an example of the relative simplicity of tools which give primacy to SQL, and not objects, here's an
example of a Data Access Object (DAO) implemented with web4j. The actual SQL statements are placed
in .sql text files placed near the corresponding DAO (usually in the same directory).
FETCH_PREFERENCES {
SELECT Id, LoginName, ScreenName FROM Users WHERE LoginName=?
}
CHANGE_PREFERENCES {
UPDATE Users SET ScreenName=? WHERE LoginName=?
}

Entries in the above .sql file are referenced in code using SqlId objects, such as FETCH_PREFERENCES:
import
import
import
import
import

static hirondelle.predict.main.preferences.PreferencesAction.CHANGE_PREFERENCES;
static hirondelle.predict.main.preferences.PreferencesAction.FETCH_PREFERENCES;
hirondelle.web4j.database.DAOException;
hirondelle.web4j.database.Db;
hirondelle.web4j.security.SafeText;

/**
Data Access Object for user {@link Preferences}.
*/
public final class PreferencesDAO {
/** Fetch the {@link Preferences} for a given user. */
public Preferences fetch(SafeText aUserId) throws DAOException {
return Db.fetch(Preferences.class, FETCH_PREFERENCES, aUserId);
}
/** Change the {@link Preferences} for a given user. */
void change(Preferences aPreferences) throws DAOException {
Db.edit(
CHANGE_PREFERENCES,
aPreferences.getScreenName(),
aPreferences.getLoginName()
);
}
}

That's all there is to it. There's no mapping involved.

166

Collected Java Practices

See Also :
Simplify database operations

Consider using standard SQL


Almost all relational databases extend the standard set of SQL commands. If these extensions are used,
then portability rapidly degrades.
In some applications, such portability may not be a concern. However, if portability is important, then it's
clearly an advantage to use only the standard SQL commands. Mimer has published this SQL validator
which verifies compliance with different versions of the SQL standard.

See Also :
Keep SQL out of code

Consider wrapper classes for optional data


Optional data is everywhere. Databases almost always have a significant fraction of their fields as
optional (that is, as possiblyNULL ). In addition, the forms submitted in web applications can contain
absent parameters. All web applications must deal with such possibly- null data as part of normal
processing.
For modeling optional data, wrapper classes such asInteger,Boolean, and so on, seem to have a
significant advantage over primitives ( int ,boolean, and so on). This is because bothNULL database
fields and missing request parameters naturally map tonull object references. With primitives, there is
no such natural mapping.
For instance, how would you model a person's age as an optional number, with no reasonable default
value, using anint ? The only means is to use a magic value such as -1. This should likely be avoided, if
possible.
JDK 1.5 introduced a language feature called autoboxing, whereby primitives and wrappers are more or
less interchangeable. However, they are not entirely interchangeable, since wrapper objects can be null ,
while primitives cannot.
See Also :
Avoid null if possible

Data access objects


Data Access Objects (DAOs):
167

Collected Java Practices

can be used in a large percentage of applications - anywhere data storage is required.


hide all details of data storage from the rest of the application.
act as an intermediary between your application and the database. They move data back and forth
between Java objects and database records.
allow ripple effects from possible changes to the persistence mechanism to be confined to a
specific area.
For simple DAOs, various implementation styles are possible:
Style 1 - instance methods
MessageDAO dao = new MessageDAO();
List<Message> messages = dao.fetchRecentMessages();

This is the recommended style. Create an ordinary object in the usual way, and use its services.
Style 2 - variation on Style 1
List<Message> messages = new MessageDAO().fetchRecentMessages();

The DAO is created and used on the same line. This style seems a bit less legible than Style 1.
Style 3 - static methods
List<Message> messages = MessageDAO.fetchRecentMessages();

Probably the least desirable. One must exercise care that such classes are thread-safe. This issue usually
doesn't exist in Styles 1 and 2, if the DAO has no static members, is short-lived, and there is no
possibility of sharing data between threads.
Full DAO Design Pattern
It's common to describe Data Access Objects as an extended design pattern. That design pattern centers
on allowing wholesale changes to a datastore mechanism - for example, changing from a relational
database to using the file system, or some other means of data storage. It is only required when multiple
storage mechanisms must coexist during production, or when the details of how data is to be stored is
decided during deployment.
It must be stressed that most applications do not need the full DAO design pattern as usually described.
Most business applications are deployed with a very particular database in mind. In such cases, allowing
a deployer to configure various types of datastore seems entirely superfluous. If your application won't
benefit from it, then the full DAO pattern is clearly of dubious benefit. Rather, implementing each DAO
as a single, ordinary class seems more appropriate and effective.
See Also :
Data exception wrapping
Keep SQL out of code
Minimize ripple effects
Abstract Factory
Model Objects
Try pseudo-persistence for mock ups

Data exception wrapping


168

Collected Java Practices

Data can be stored in various ways, for example:


a relational database
text files
on the web (for example, fetching the weather forecast from a web site)
If the storage method changes, then the low level Exception objects thrown by the data access layer can
also change. For example, when the data store is moved from text files to a relational database,
IOException is replaced with SQLException.
In order to prevent such a change from propagating to higher layers, one can wrap such low level
Exception s in a generic "data exception" wrapper, designed exclusively to protect the higher layers from
such changes.
Throwable.getCause() can always be used to extract the original exception, if desired. (However, you
sometimes need to be careful that both client and server know about the class of an underlying
exception.)

Example
When building applications with the WEB4J tool, DAOException is the only checked exception that
should be emitted by an application's data access layer. All occurrences of checked exceptions are either
handled within the data layer, or, if they are thrown, translated into a DAOException.
In the following example, ConnectionSrc encapulates how an application creates or accesses
Connection s. When an SQLException or a NamingException occurs in its implementation, it is always
wrapped in a DAOException, and then re-thrown to the caller.
This example is particulary appropriate because of the NamingException . If the NamingException was
not translated into an DAOException, and thrown as is, then it would be visible directly to the caller. That
is, the caller would know that the data layer is implemented using JNDI - which is likely inappropriate.
import
import
import
import

java.util.*;
java.util.logging.*;
java.sql.Connection;
java.sql.SQLException;

import
import
import
import
import
import
import

javax.naming.Context;
javax.naming.InitialContext;
javax.naming.NamingException;
javax.sql.DataSource;
hirondelle.web4j.database.DAOException;
hirondelle.web4j.database.ConnectionSource;
hirondelle.web4j.util.Util;

/**
* Fetch a <tt>Connection</tt> from a pool managed by the container.
*/
public final class ConnectionSrc implements ConnectionSource {
//..elided
private Connection getConnectionByName(String aDbName) throws DAOException {
Connection result = null;
String dbConnString = (String)MAP_NAME_TO_CONN_STRING.get(aDbName);
if( ! Util.textHasContent(dbConnString) ){
throw new IllegalArgumentException("Unknown db name : " + Util.quote(aDbName));
}
try {
Context initialContext = new InitialContext();
if ( initialContext == null ) {
fLogger.severe("InitialContext is null. Db : " + Util.quote(dbConnString));
}
169

Collected Java Practices

DataSource datasource = (DataSource)initialContext.lookup(dbConnString);


if ( datasource == null ){
fLogger.severe("Datasource is null for : " + dbConnString);
}
result = datasource.getConnection();
}
catch (NamingException ex){
//EXCEPTION WRAPPING
throw new DAOException(
"Config error with JNDI and datasource, for db " + Util.quote(dbConnString),
ex
);
}
catch (SQLException ex ){
//EXCEPTION WRAPPING
throw new DAOException(
"Cannot get connection from datasource, for db " + Util.quote(dbConnString), ex
);
}
return result;
}
}

See Also :
Data access objects
Reduce database code duplication
Exception translation
A Web App Framework - WEB4J
Beware of unknown root causes

Data is king
In most organizations, the data is significantly more important than the applications which use it - data is
king. The fundamental reason is simply that data almost always outlives the applications that create it.
Data is quasi-permanent, whereas the applications that use the data usually change over time. For
example, it's common for an end-user application to be completely replaced after a certain number of
years, using a more modern set of tools. In this case, the database may:
remain untouched
be upgraded a more modern version of the same database
be changed to improve its design
be converted into an entirely different database altogether
Persistent data is just that - persistent - it stays around for a long, long time.
If data is king, then the most important tasks for a developer are those involving the database. Any effort
put into improving the database, or how it's used, will have a lasting, long-term benefit. In particular, the
application programmer should make sure that:
the database structure is robust, well-defined and (usually) normalized. If the application
programmer is not experienced in database design, then they should seek help.
the data is always validated before being entered into the database (this cannot be stressed enough)
actions that consist of more than one SQL statement are properly encapsulated in a transaction, and
rolled back correctly when a failure occurs. In general, the integrity of the data should always be
170

Collected Java Practices

preserved.
applications should never assume that they "own" the data. Databases are independent processes,
and are built to interact with many clients applications, not just one. For example, many databases
are loaded initially using a data load tool. When data is loaded with a script, it bypasses all
validation logic implemented in code. Thus, it's incorrect for a calling application to make
assumptions regarding the validity of the data.

Don't perform basic SQL tasks in code


Relational databases are a mature technology, and they should be used to do as much simple work as
possible.
For example, don't do the following in code, if they can be done in SQL instead:
ordering (ORDER BY)
filtering based on criteria (WHERE)
joining tables (WHERE, JOIN) -- see the code tables topic for a possible exception to this rule
summarizing (GROUP BY, COUNT, AVG, STDDEV)
Any corresponding task implemented by your own code would very likely:
be much less robust and efficient
take longer to implement
require more maintenance effort
To retain the order of a ResultSet as specified in ORDER BY, choose a collection which has a defined
iteration order, such as an ArrayList or a LinkedHashMap .
See Also :
Implementing compareTo
Choosing the right Collection
Reduce database code duplication
Implement code tables

Encapsulate connections
A database connection can be obtained in different ways. One should hide this design decision in a utility
class, such that changes to this decision will have minimal ripple effects. Another important benefit is
elimination of code repetition.
Example
This is taken from the WEB4J example application. Here, a Connection is obtained from a JNDI
DataSource configured on the server. An alternate implementation might obtain a Connection directly
from the JDBC driver. (Some modern drivers include a built-in connection pool.)
171

Collected Java Practices

This implementation is slightly unusual in that it can return a Connection for more than one database.
package hirondelle.web4j.config;
import
import
import
import
import
import
import
import
import
import
import
import

java.util.*;
java.util.logging.*;
java.sql.Connection;
java.sql.SQLException;
javax.naming.Context;
javax.naming.InitialContext;
javax.naming.NamingException;
javax.sql.DataSource;
hirondelle.web4j.database.DAOException;
hirondelle.web4j.database.ConnectionSource;
hirondelle.web4j.util.Util;
javax.servlet.ServletConfig;

/**
Implementation of {@link ConnectionSource}, required by WEB4J.
<P>This implementation uses a <tt>Connection</tt> pool managed by the container.
This class is non-final only since it is convenient for
{@link hirondelle.fish.test.doubles.FakeConnectionSrc}.
Only one method can be overridden - {@link #getConnectionByName(String)}.
*/
public class ConnectionSrc implements ConnectionSource {
/** Read in connection strings from <tt>web.xml</tt>. */
public final void init(ServletConfig aConfig){
fDefaultDbConnString = aConfig.getInitParameter(DEFAULT_CONN_STRING);
fAccessControlDbConnectionString =
aConfig.getInitParameter(ACCESS_CONTROL_CONN_STRING)
;
fTranslationDbConnectionString = aConfig.getInitParameter(TRANSLATION_CONN_STRING);
ensureAllSettingsPresent();
fMapNameToConnectionString = new LinkedHashMap<String, String>();
fMapNameToConnectionString.put(DEFAULT, fDefaultDbConnString);
fMapNameToConnectionString.put(ACCESS_CONTROL, fAccessControlDbConnectionString);
fMapNameToConnectionString.put(TRANSLATION, fTranslationDbConnectionString);
fLogger.config(
"Connection strings : " + Util.logOnePerLine(fMapNameToConnectionString)
);
}
/**
Return value contains only {@link #DEFAULT}, {@link #ACCESS_CONTROL},
and {@link #TRANSLATION}.
*/
public final Set<String> getDatabaseNames(){
return Collections.unmodifiableSet(fMapNameToConnectionString.keySet());
}
/**
Return a {@link Connection} for the default database.
*/
public final Connection getConnection() throws DAOException {
return getConnectionByName(DEFAULT);
}
/**
Return a {@link Connection} for the identified database.
@param aDatabaseName one of the values {@link #DEFAULT},
{@link #TRANSLATION}, or {@link #ACCESS_CONTROL}.
*/
public final Connection getConnection(String aDatabaseName) throws DAOException {
return getConnectionByName(aDatabaseName);
}
/**
Name used to identify the default database. The default database is
the main database, carrying core business data. It is the data that is
most often accessed.
*/
public static final String DEFAULT = "DEFAULT";
/** Name used to identify the access control database (users, roles, etc.).*/
172

Collected Java Practices

public static final String ACCESS_CONTROL = "ACCESS_CONTROL";


/** Name used to identify the translation database. */
public static final String TRANSLATION = "TRANSLATION";
/**
This method can be overridden by a subclass.
Such overrides are intended for testing.
*/
protected Connection getConnectionByName(String aDbName) throws DAOException {
Connection result = null;
String dbConnString = getConnectionString(aDbName);
if( ! Util.textHasContent(dbConnString) ){
throw new IllegalArgumentException(
"Unknown database name : " + Util.quote(aDbName)
);
}
try {
Context initialContext = new InitialContext();
if ( initialContext == null ) {
fLogger.severe(
"DataSource problem. InitialContext is null. Db : " +
Util.quote(dbConnString)
);
}
DataSource datasource = (DataSource)initialContext.lookup(dbConnString);
if ( datasource == null ){
fLogger.severe("Datasource is null for : " + dbConnString);
}
result = datasource.getConnection();
}
catch (NamingException ex){
throw new DAOException(
"Config error with JNDI and datasource, for db " + Util.quote(dbConnString),
ex
);
}
catch (SQLException ex ){
throw new DAOException(
"Cannot get JNDI connection from datasource, for db " +
Util.quote(dbConnString),
ex
);
}
return result;
}
/**
This item is protected, in order to make it visible to any subclasses created
for testing outside of the normal runtime environment.
*/
protected String getConnectionString(String aDbName){
return fMapNameToConnectionString.get(aDbName);
}
// PRIVATE
/**
Maps the database name passed to {@link #getConnection(String)} to the
actual connection string.
*/
private static Map<String, String> fMapNameToConnectionString;
private static final String DEFAULT_CONN_STRING = "DefaultDbConnectionString";
private static String fDefaultDbConnString;
private static final String ACCESS_CONTROL_CONN_STRING =
"AccessControlDbConnectionString"
;
private static String fAccessControlDbConnectionString;
private static final String TRANSLATION_CONN_STRING =
"TranslationDbConnectionString";
private static String fTranslationDbConnectionString;
private static final Logger fLogger = Util.getLogger(ConnectionSrc.class);
private static void ensureAllSettingsPresent(){
173

Collected Java Practices

if( ! Util.textHasContent(fDefaultDbConnString) ) {
logError(DEFAULT_CONN_STRING);
}
if( ! Util.textHasContent(fTranslationDbConnectionString) ) {
logError(TRANSLATION_CONN_STRING);
}
if ( ! Util.textHasContent(fAccessControlDbConnectionString) ) {
logError(ACCESS_CONTROL_CONN_STRING);
}
}
private static void logError(String aSettingName){
fLogger.severe("Web.xml missing init-param setting for " + aSettingName);
}
}

See Also :
Connection pools
A Web App Framework - WEB4J

Keep SQL out of code


Structured Query Language (SQL) is text. It's natural home is a text file of some sort. Storing SQL as
text, outside of compiled code, eases maintenance significantly:
it lets those who know SQL to contribute directly to a project, without having to know anything
else about all the other tools used in the project.
for developers new to a project, having SQL separate from code makes it significantly easier to
browse through SQL statements and learn the database structure.
it minimizes the ripple effects caused by changes to the database. For example, if a table name or
column name changes, the fix is usually an easy search and replace in a single text file.
if an older application needs to be migrated to a new framework, then the SQL is readily available
for porting to the new application.
if necessary, changes to SQL can even be made after deployment, by changing the text file and
doing a restart or a refresh. This can occasionally be useful. For example, if users ask for a change
in the sort order of some data, then the developer very likely has the option of implementing that
change without a redeployment (and maybe even without a restart, if the tools support a refresh).
However, some organizations would object to performing such changes directly in production.
adding support for a new relational database may become simply a matter of translating only those
statements that happen to be non-portable.
The Pragmatic Programmer, by Hunt and Thomas, emphasizes this type of metadata:
"It forces you to create a more robust, abstract design by deferring details - deferring them all the way
out of the program."
There's a possible objection to keeping SQL as text in a production environment: it can be examined for
vulnerabilities by a malicious user with access to the machine where it resides. In that case, there is the
option of translating the SQL-as-text (used in development) into some binary form (used in production).
Many SQL statements come in these three forms, all of which may be removed from compiled code and
174

Collected Java Practices

stored outside the program as text:


Fixed text:
SELECT * FROM Board ORDER BY Id;

Here, simply fetch a String from some textual source (such as a properties file), and pass it to the
constructor of a Statement or PreparedStatement. This form is likely relatively rare, since most SQL
statements seem to have at least one parameter.
Parameterized, with a single set of values :
INSERT INTO Board
(Id, Title, TimeZone, Language, CreationDate)
VALUES ('quark','High Energy Physics','GMT','english',NOW());

Here, fetch a String having '?' as place holders from some textual source :
INSERT INTO Board
(Id, Title, TimeZone, Language, CreationDate)
VALUES (?,?,?,?,NOW());

This String is then passed to the constructor of a PreparedStatement, and parameters are inserted using
the methods of the PreparedStatement class. Note that PreparedStatement has significant advantages
over an ordinary Statement .
Parameterized, with multiple sets of parameters :
INSERT INTO
VALUES
('RASB', 3,
('RASB', 2,
('RASB', 1,
('RASB', 4,

Vote
'Y',
'Y',
'Y',
'?',

NOW()),
NOW()),
NOW()),
NOW());

Using such a statement to add many records is advantageous, since it involves only one trip to the
database. However, the PreparedStatement parameter mechanism cannot do this in one statement, since
the number of added records is arbitrary. The usual style in this case is to use batched statements, in
which a number of individual INSERT statements are collected together in a batch, and then executed as
a unit.
Search Operations
There are cases in which it remains preferable to construct an SQL statement (or at least parts of it) in
code. This is particularly true for many search operations, where the end user enters various search
criteria into a form used to construct WHERE and ORDER BY clauses. It's often the case that a very large
number of possible WHERE and ORDER BY clauses can be built from such user input. Manually
enumerating all such possibilities is often undesirable. In such cases, building the SQL statement
dynamically from user input seems preferable.
When constructing statements dynamically, however, you must guard against SQL injection attacks.
Here's a simple technique for doing so:
start with a base SQL statement defined in a text file, as above.
build the dynamic parts of the SQL statement in code (usually the WHERE and ORDER BY clauses),
but, at this stage, do not insert the data parameter values yet. That is, build a String containing '?'
placeholders for data, in the usual form expected by a PreparedStatement.
175

Collected Java Practices

next, create a PreparedStatement with the given SQL string. This will verify that the SQL
statement is of valid form. As always, using PreparedStatement in this way will protect against
SQL injection attacks.
finally, inject the data for the '?' placeholders in the usual way.
With the above technique, user input of search criteria is used for two distinct purposes: first dynamically
building the core SQL with the desired WHERE and ORDER BY clauses (containing '?' placeholders), and
secondly populating the '?' placeholders with data in the usual way.
Example
Here's an example of a .sql file which contains the SQL statements related to a single feature. All
parameterized statements are expressed in a style suitable for a PreparedStatement, to avoid using any
quoting conventions. It's taken from one of the WEB4J example applications.
LIST_RESTOS {
SELECT Id, Name, Location, Price, Comment
FROM Resto
ORDER BY Name
}
FETCH_RESTO {
SELECT Id, Name, Location, Price, Comment
FROM Resto
WHERE Id =?
}
ADD_RESTO {
-- Id is an autoincrement field, populated automagically by the database.
INSERT INTO Resto (Name, Location, Price, Comment) VALUES (?,?,?,?)
}
CHANGE_RESTO {
UPDATE Resto SET Name=?, Location=?, Price=?, Comment=? WHERE Id=?
}
DELETE_RESTO {
DELETE FROM RESTO WHERE Id=?
}

In the case of web applications, such files are safely placed in the WEB-INF directory, where they are not
accessible to the user, and can be retrieved using ServletContext.getResourceAsStream .
The WEB4J framework places all SQL statements in one or more such .sql files, located anywhere
under the WEB-INF directory. (Please see the javadoc for its data layer for more information.)
See Also :
Data access objects
Reduce database code duplication
Consider using standard SQL
A Web App Framework - WEB4J
Prefer PreparedStatement

Prefer PreparedStatement
PreparedStatement

is usually preferred over Statement for these reasons:

in general, it's more secure. When a Statement is constructed dynamically from user input, it's
vulnerable to SQL injection attacks. PreparedStatement is less vulnerable in this way (see below).
176

Collected Java Practices

there is usually no need to worry about escaping special characters.


if repeated compilation is avoided, its performance is usually better.
In general, it seems safest to use a Statement only when the SQL is of fixed, known form, with no
parameters.
SQL Injection
If PreparedStatement is used correctly, then it does indeed provide complete protection against SQL
Injection attacks. However, if it's used incorrectly, then it's still wide open to such attacks.
The SQL statement passed to PreparedStatment is simply an unvalidated String, in the sense that there's
no checking for '?' values. If the String has been constructed using '?' placeholders for all dynamic data,
then it has indeed been constructed correctly.
But PreparedStatement has no built-in mechanism to prevent the inexperienced or inattentive
programmer from passing a String which, by mistake, does not always use a '?' placeholder where it
should. Such Strings are wide open to SQL Injection attacks.
See Also :
Keep SQL out of code
Beware of common hacks

Reduce database code duplication


If a naive design is followed, JDBC code rapidly becomes repetitious. JDBC is one area where applying
the "Don't Repeat Yourself" guideline found in The Pragmatic Programmer (Hunt and Thomas) has large
rewards. Reducing code repetition:
greatly increases the quality of the design
minimizes ripple effects when changes occur
decreases the size of the code base
The following topics present examples of removing code repetition when accessing the database:
Simplify database operations
Encapsulate connections
Use template for transactions

See Also :
Data exception wrapping

Remember the basics of database design


Remember the basics of database design, or normalization:

177

Collected Java Practices

each table has a primary key


the job of the primary key is to uniquely identify records, not to store business data; any use of
business data in a primary key is a dangerous practice, since any changes to such data will have
large ripple effects
a good deal of data normalization is summed up by the memorable phrase: "a table stores facts
about the key, the whole key, and nothing but the key"
relations between tables are implemented using foreign keys, where one table refers to the primary
key of another table
almost all relations are 1..N
1..1 relations are permitted, but need a reason to not be a 1..N relation (this reason is usually a
performance optimization)
cross references between tables A and B are implemented by a third table C, which contains two
foreign keys, one for each of the primary keys of tables A and B
Database performance:
Connection pools can improve performance
using PreparedStatement instead of Statement

can improve performance as well


fetching data with SELECT statements can often account for the great majority of activity (often
95% or more). For apps that focus on processing transactions, the relative number of SELECTs is
usually lower.

See Also :
Connection pools
Read-write locks
Business identifiers as String

Simplify database operations


Since fetch and edit operations are such common tasks in many applications, it's beneficial if these tasks
can be made as simple and compact as possible. Even when using tools on top of JDBC, there are very
often ways of simplifying code and removing repetition.
If you find that your data access code is becoming repetitive, look for ways to remove the repetition utility classes, template methods, and so on.
Example
The MemberDAO class is taken from one of the WEB4J example applications. This example is already
quite compact. The methods are short, and read at a high level. If you look closely, however, you can see
that one item is still repeated. Can you spot it? It's rather subtle. (See answer below.)
/** Data Access Object (DAO) for {@link Member} objects. */
public final class MemberDAO {
//elided...
List<Member> list() throws DAOException {
return Db.list(Member.class, MEMBER_LIST);
}
public Member fetch(Id aMemberId) throws DAOException {
return Db.fetch(Member.class, MEMBER_FETCH, aMemberId);
178

Collected Java Practices

}
Id add(Member aMember) throws DAOException, DuplicateException {
return Db.add(MEMBER_ADD, baseParamsFrom(aMember));
}
boolean change(Member aMember) throws DAOException, DuplicateException {
Object[] params = Db.addIdTo(baseParamsFrom(aMember), aMember.getId());
return Util.isSuccess(Db.edit(MEMBER_CHANGE, params));
}
int delete(Id aMemberId) throws DAOException {
return Db.delete(MEMBER_DELETE, aMemberId);
}
// PRIVATE
private Object[] baseParamsFrom(Member aMember){
return new Object[]{
aMember.getName(), aMember.getIsActive(), aMember.getDisposition().getId()
};
}
}

The answer is that the list and fetch methods repeat the same piece of data - the class literal
Member.class . Like any repeated literal, it should likely be placed in a private static final field.
Since that would be a common occurence, it would likely be beneficial to make a habit of doing so, and
of giving a conventional name to the field as well, such as MODEL_CLASS.
See Also :
Reduce database code duplication
Keep SQL out of code
Consider data layer tools
Encapsulate connections
A Web App Framework - WEB4J
Use template for transactions

Try pseudo-persistence for mock ups


It's possible to build Data Access Objects which mimic persistance, but whose data lasts only until the
application shuts down. One might call this "pseudo-persistance", and such objects "pseudo-DAOs".
These stub classes are easy to construct, and can be useful for quick mock ups. They usually require less
effort than providing true persistance. By following the style shown below, for example, a pseudo-DAO
could be constructed in 10 minutes.
This example pseudo-DAO implements pseudo-persistence for Person objects. The intent is to create a
pseudo-DAO whose methods will match the final, true DAO. Thus, the same class can evolve from a
mock up into a final implementation, without affecting the caller in any way. From the caller's point of
view, there should be little or no difference between the pseudo-DAO and the real DAO, including the
checked exceptions thrown by its methods.
The underlying technique is simply to store objects in a static member of the pseudo-DAO. In this case,
a Map is used.
package myapp.data;
179

Collected Java Practices

import java.util.*;
import myapp.business.Person;
/**
* Provides "pseudo-persistance", by storing objects as static data attached to this
* class. Such data is lost when the application is shut down, but is retained
* as long as the application is running.
*
* <P>The important thing is to match the desired style of the true DAO
* into which this pseudo-DAO may evolve.
*
* <P>Note that <tt>DataAccessException</tt> (a checked exception) can and should
* be declared in the throws clause, even when the underlying implementation for
* this pseudo-DAO never throws such an exception. Again, the idea is to provide
* a stub which, from the point of view of the caller, is as realistic as possible.
*/
public final class PersonDAO {
/**
* Add <tt>aPerson</tt> to the datastore, returning an indicator of
* success or failure.
*
* If <tt>aPerson</tt> already exists in the datastore, then return false.
*/
public boolean add(Person aPerson) throws DataAccessException {
boolean result = false;
synchronized(fPersons) {
if (! fPersons.containsKey(aPerson.getName())) {
fPersons.put(aPerson.getName(), aPerson);
result = true;
log("Persons after add: " + fPersons);
}
}
return result;
}
/**
* Fetch a person from the datastore, using <tt>aName</tt> as a unique
* identifier.
*
* <P>If no <tt>Person</tt> is found, return null.
*/
public Person fetch(String aName) throws DataAccessException {
Person result = null;
synchronized(fPersons){
if(fPersons.containsKey(aName)){
result = fPersons.get(aName);
}
log("Persons during fetch: " + fPersons);
}
return result;
}
/**
* Update the data associated with <tt>aPersons</tt>, returning an
* indicator of success or failure.
*
* <P>If <tt>aPerson</tt> is not found in the datastore, then return false.
*/
public boolean change(Person aPerson) throws DataAccessException {
boolean result = false;
synchronized(fPersons){
if (fPersons.containsKey(aPerson.getName())) {
fPersons.put(aPerson.getName(), aPerson); //replaces old entry
result = true;
log("Persons after change: " + fPersons);
}
}
return result;
}
/**
* Delete the <tt>Person</tt> identified uniquely by <tt>aName</tt>,
* returning an indicator of success or failure.
*
* <P>If <tt>aName</tt> is not found in the datastore, then return false.
*/
public boolean delete(String aName) throws DataAccessException {
180

Collected Java Practices

boolean result = false;


synchronized(fPersons){
if (fPersons.containsKey(aName)) {
fPersons.remove(aName);
result = true;
log("Persons after delete: " + fPersons);
}
}
return result;
}
// PRIVATE
/**
* Stores Person objects in a static field, attached to this class, which will
* remain available as long as this class remains loaded.
*/
private static Map<String, Person> fPersons = new LinkedHashMap<>();
private void log(String aText){
System.out.println(aText);
}
}

See Also :
Data access objects

Use template for transactions


Code related to transactions is bulky, and has significant logic attached to it. Such code should not be
repeated every time a transaction is required. Instead, define a template method which handles most of
the processing of the transaction.
Example
Tx

is a generic interface which can be applied to both:


local transactions - attached to a single JDBC connection
distributed transactions ( UserTransaction ) - not attached to a single connection, or even to a
single database

package hirondelle.web4j.database;
/**
Execute a database transaction.
<P>Should be applied only to operations involving more than one SQL statement.
*/
public interface Tx {
/**
Execute a database transaction, and return the number of edited records.
*/
int executeTx() throws DAOException;
}

181

Collected Java Practices

is an implementation of Tx for local transactions. (An implementation for distributed


transactions would be similar). It's an Abstract Base Class, and uses the template method design pattern.
To implement a transaction, callers subclass TxTemplate , and provide a concrete implementation of
executeMultipleSqls(Connection).
TxTemplate

import java.sql.*;
import java.util.logging.*;
import hirondelle.web4j.BuildImpl;
import hirondelle.web4j.util.Util;
import hirondelle.web4j.util.Consts;
/**
* Template for executing a local, non-distributed transaction versus a
* single database, using a single connection.
*
* <P>This abstract base class implements the template method design pattern.
*/
public abstract class TxTemplate implements Tx {
//..elided
/**
* <b>Template</b> method calls the abstract method {@link #executeMultipleSqls}.
* <P>Returns the same value as <tt>executeMultipleSqls</tt>.
*
* <P>A <tt>rollback</tt> is performed if <tt>executeMultipleSqls</tt> fails.
*/
public final int executeTx() throws DAOException {
int result = 0;
fLogger.fine(
"Editing within a local transaction, with isolation level : " +
fTxIsolationLevel
);
ConnectionSource connSource = BuildImpl.forConnectionSource();
if(Util.textHasContent(fDatabaseName)){
fConnection = connSource.getConnection(fDatabaseName);
}
else {
fConnection = connSource.getConnection();
}
try {
TxIsolationLevel.set(fTxIsolationLevel, fConnection);
startTx();
result = executeMultipleSqls(fConnection);
endTx(result);
}
catch(SQLException rootCause){
fLogger.fine("Transaction throws SQLException.");
rollbackTx();
String message =
"Cannot execute edit. ErrorId code : " + rootCause.getErrorCode() +
Consts.SPACE + rootCause
;
if (rootCause.getErrorCode() ==
DbConfig.getErrorCodeForDuplicateKey().intValue()){
throw new DuplicateException(message, rootCause);
}
throw new DAOException(message, rootCause);
}
catch (DAOException ex){
fLogger.fine("Transaction throws DAOException.");
rollbackTx();
throw ex;
}
finally {
DbUtil.logWarnings(fConnection);
DbUtil.close(fConnection);
}
fLogger.fine("Total number of edited records: " + result);
return result;
}

182

Collected Java Practices

/**
* Execute multiple SQL operations in a single local transaction.
*
* <P>This method returns the number of records edited.
*/
public abstract int executeMultipleSqls(
Connection aConnection
) throws SQLException, DAOException;
// PRIVATE
private Connection fConnection;
private String fDatabaseName;
private final TxIsolationLevel fTxIsolationLevel;
private static final boolean fOFF = false;
private static final boolean fON = true;
private static final Logger fLogger = Util.getLogger(TxTemplate.class);
private void startTx() throws SQLException {
fConnection.setAutoCommit(fOFF);
}
private void endTx(int aNumEdits) throws SQLException, DAOException {
if ( BUSINESS_RULE_FAILURE == aNumEdits ) {
fLogger.severe("Business rule failure occured. Cannot commit transaction.");
rollbackTx();
}
else {
fLogger.fine("Commiting transaction.");
fConnection.commit();
fConnection.setAutoCommit(fON);
}
}
private void rollbackTx() throws DAOException {
fLogger.severe("ROLLING BACK TRANSACTION.");
try {
fConnection.rollback();
}
catch(SQLException ex){
throw new DAOException("Cannot rollback transaction", ex);
}
}
}

Here's a concrete implementation named RoleDAO. It has a change method which updates the roles
attached to a user. The roles are stored in a cross-reference table. In this case, an update is implemented
as 'delete all old, then add all new'. Note the lack of try..catch blocks in this class.
final class RoleDAO {
//..elided
/**
* Update all roles attached to a user.
*
* <P>This implementation will treat all edits to user roles as
* '<tt>DELETE-ALL</tt>, then <tt>ADD-ALL</tt>' operations.
*/
boolean change(UserRole aUserRole) throws DAOException {
Tx update = new UpdateTransaction(aUserRole);
return Util.isSuccess(update.executeTx());
}
// PRIVATE //
/** Cannot be a {@link hirondelle.web4j.database.TxSimple}, since there is looping.
*/
private static final class UpdateTransaction extends TxTemplate {
UpdateTransaction(UserRole aUserRole){
super(ConnectionSrc.ACCESS_CONTROL);
183

Collected Java Practices

fUserRole = aUserRole;
}
public int executeMultipleSqls(
Connection aConnection
) throws SQLException, DAOException {
int result = 0;
//perform edits using a shared connection
result = result + DbTx.edit(aConnection, ROLES_DELETE, fUserRole.getUserName());
for(Id roleId : fUserRole.getRoles()){
result = result +
DbTx.edit(aConnection,ROLES_ADD,fUserRole.getUserName(),roleId);
}
return result;
}
private UserRole fUserRole;
}
}

See Also :
Template method
Simplify database operations
A Web App Framework - WEB4J
Use template for repeated try-catch

Choosing the right Collection


Here's a guide for selecting the proper implementation of a Set, List , or Map . It was compiled for Java
1.4. Many additions have been made to the Collections Framework since then (notably the Queue and
Deque interfaces, and various items in java.util.concurrent ). These later additions have been omitted
here, since this briefer summary should suffice for most cases.
The best general purpose or 'primary' implementations are likely ArrayList , LinkedHashMap , and
LinkedHashSet . Their overall performance is better, and you should use them unless you need a special
feature provided by another implementation. That special feature is usually ordering or sorting.
For convenience, "ordering" will here refer to the order of items returned by an Iterator , and "sorting"
will here refer to sorting items according to Comparable or Comparator .

Interface HasDuplicates?
Implementations
Historical
LinkedHashSet
Set
HashSet
TreeSet
no
...
...
...
ArrayList
List
LinkedList
Vector, Stack
yes
...
...
...
no duplicate
Hashtable,
LinkedHashMap
Map
HashMap
TreeMap Properties
...
...
keys
Principal features of non-primary implementations:
HashMap has slightly better performance than LinkedHashMap , but its iteration order is undefined
HashSet has slightly better performance than LinkedHashSet , but its iteration order is undefined
TreeSet is ordered and sorted, but slower
TreeMap is ordered and sorted, but slower
LinkedList has fast adding to the start of the list, and fast deletion from the interior via iteration
184

Collected Java Practices

Iteration order for above implementations:


HashSet - undefined
HashMap - undefined
LinkedHashSet - insertion order
LinkedHashMap - insertion order of keys (by default), or 'access order'
ArrayList - insertion order
LinkedList - insertion order
TreeSet - ascending order, according to Comparable / Comparator
TreeMap - ascending order of keys, according to Comparable / Comparator

For LinkedHashSet and LinkedHashMap , the re-insertion of an item does not affect insertion order.
For LinkedHashMap , 'access order' is from the least recent access to the most recent access. In this
context, only calls to get , put , and putAll constitute an access, and only calls to these methods affect
access order.
While being used in a Map or Set , these items must not change state (hence, it's recommended that these
items be immutable objects):
keys of a Map
items in a Set
Sorting requires either that:
the stored items implement Comparable
a Comparator for the stored objects be defined
To retain the order of a ResultSet as specified in an ORDER BY clause, insert the records into a List
or a LinkedHashMap .
See Also :
Use standard Collections
Implementing compareTo
Immutable objects
Prefer Collections over older classes

Encapsulate collections
In general, Collections are not immutable objects. As such, one must often exercise care that collection
fields are not unintentionally exposed to the caller.
One technique is to define a set of related methods which prevent the caller from directly using the
underlying collection, such as:
addThing(Thing)
removeThing(Thing)
getThings() - return an

unmodifiable Collection

Example 1
185

Collected Java Practices

import java.util.*;
public final class SoccerTeam {
public SoccerTeam(String aTeamName, String aHeadCoachName){
//..elided
}
public void addPlayer(Player aPlayer){
fPlayers.add(aPlayer);
}
public void removePlayer(Player aPlayer){
fPlayers.remove(aPlayer);
}
public Set<Player> getPlayers(){
return Collections.unmodifiableSet(fPlayers);
}
//..elided
// PRIVATE
private Set<Player> fPlayers = new LinkedHashSet<>();
private String fTeamName;
private String fHeadCoachName;
}

Example 2
is an example of exposing the collection directly to the caller. This is not necessarily an
incorrect design, but it's riskier, since the contents of the collection can be directly changed by both
BaseballTeam and its caller:
BaseballTeam

import java.util.*;
public final class BaseballTeam {
public BaseballTeam(String aTeamName, String aHeadCoachName){
//..elided
}
public void setPlayers(Set<Player> aPlayers){
fPlayers = aPlayers;
}
public Set<Player> getPlayers(){
return fPlayers;
}
//..elided
// PRIVATE
private Set<Player> fPlayers;
private String fTeamName;
private String fHeadCoachName;
}

See Also :
Defensive copying
Immutable objects

Iterate without an index


186

Collected Java Practices

Many programmers have a strong preference for using a for-each loop or an Iterator instead of a for loop.
The traditional for -loop always uses an explicit loop index - an integer that identifies each iteration. The
problem is that loop indexes have always been a fruitful source of error. For example, off-by-one errors
are very common in programming, and they are often related to these loop indexes. In addition, the
(somewhat unnatural) computer science tradition of starting loop indexes from 0 instead of 1 often
contributes to these sorts of bugs.
Since they are more error prone, for -loops with indexes should generally be avoided, in favor of the
more robust, compact, and elegant for-each loop.
Example
import java.util.*;
/** Different iteration styles. */
public class IterateNoIndex {
/**
* Iterating without an index is more compact and less
* error prone.
*/
public void withoutIndex(){
//for-each loop is usually preferred
List<String> trees = Arrays.asList("Maple", "Birch", "Poplar");
for(String tree: trees){
log(tree);
}
//Iterators are not as compact as a for-each loop
//but sometimes you need them: removing/replacing items,
//and 'parallel' iterations across two data structures
Iterator<String> iter = trees.iterator();
while (iter.hasNext()) {
log(iter.next());
}
}
/** Iterating with an index is more error prone. */
public void withIndex(){
//traditional for-loop
for(int idx=0; idx < 10; ++idx){
log("Iteration..." + idx);
}
}
// PRIVATE
private void log(String aMessage){
System.out.println(aMessage);
}
}

See Also :
Two ways of using Iterator
Use for-each liberally

Prefer Collections over older classes


187

Collected Java Practices

Most programmers prefer the Java Collections Framework to arrays, Vector, and Hashtable , for the
following reasons:
the intent of the Collections Framework is to replace the older, less comprehensive tools
synchronization can be easily controlled (while Hashtable and Vector are always synchronized)
immutability can be easily controlled
search and sort algorithms are ready-made
the type of a reference to a Collection can be generic, while underlying implementation can be
easily changed
arrays are of fixed size, and are more error prone - off-by-one errors with array indexes have
always been a fruitful source of error
Arrays.asList may be used to pass an array to a method expecting a Collection or a List
Collection.toArray may be used to convert a Collection to an Object[] or a T[]
Collections.enumeration and Collections.list may be used to convert between enumerations
and collections
Since JDK 1.5, a strong case can be made that Collections should almost always be preferred over arrays.
Prior to JDK 1.5, the main disadvantage of using collections is that they always held an Object, and
retrieval of their contents required casting. Casting cannot be checked at compile-time. Arrays, on the
other hand, have the advantage of always being "parameterized" to hold objects of a specific type (in all
versions of the JDK), and this is verified by the compiler. Thus, in older versions of the JDK, changing
from a collection to an array forces one particular kind of error to appear at compile-time instead of runtime.
One minor advantage of arrays is that they are slightly easier to initialize. However, using
Arrays.asList makes the difference trivial:
import java.util.*;
public final class ArrayInit {
/** Initialize arrays and Lists. */
public static void main(String... args){
//Array initializers are compact
String[] paintings = {"oil", "watercolour"};
//Build a List using Arrays.asList(T...)
//This works for any type, not just for String
List<String> languages = Arrays.asList(
"urdu", "hindi", "pali", "sanskrit"
);
//Build a List in a more verbose way
List<String> nicePlaces = new ArrayList<>();
nicePlaces.add("luxembourg gardens");
nicePlaces.add("sea side");
nicePlaces.add("large magellanic cloud");
}
}

See Also :
Choosing the right Collection
Avoid basic style errors

188

Collected Java Practices

Two ways of using Iterator


When the for-each loop is not available, and an explicit Iterator is needed, then iteration over a
collection may be done with a while loop or a for loop.
The two styles have different advantages:
the while loop is considerably more legible
the for loop minimizes the scope of the Iterator to the loop itself
Example
import java.util.*;
public final class LoopStyles {
public static void main(String... aArguments) {
List<String> flavours = new ArrayList<>();
flavours.add("chocolate");
flavours.add("strawberry");
flavours.add("vanilla");
useWhileLoop(flavours);
useForLoop(flavours);
}
private static void useWhileLoop(Collection<String> aFlavours) {
Iterator<String> flavoursIter = aFlavours.iterator();
while (flavoursIter.hasNext()){
System.out.println(flavoursIter.next());
}
}
/**
* Note that this for-loop does not use an integer index.
*/
private static void useForLoop(Collection<String> aFlavours) {
for (Iterator<String> flavoursIter = aFlavours.iterator();
flavoursIter.hasNext();){
System.out.println(flavoursIter.next());
}
}
}

See Also :
Iterate without an index
Use for-each liberally

Use for-each liberally


The for-each loop is used with both collections and arrays. It's intended to simplify the most common
form of iteration, where the iterator or index is used solely for iteration, and not for any other kind of
operation, such as removing or editing an item in the collection or array. When there is a choice, the
for-each loop should be preferred over the for loop, since it increases legibility.
Example
189

Collected Java Practices

Here are some examples of the for-each loop.


import java.util.*;
import java.math.BigDecimal;
public final class ForEachExamples {
public static void main(String... aArgs){
List<Number> numbers = new ArrayList<>();
numbers.add(new Integer(42));
numbers.add(new Integer(-30));
numbers.add(new BigDecimal("654.2"));
//typical for-each loop
//processes each item, without changing the collection or array.
for (Number number : numbers){
log(number);
}
//use with an array
String[] names = {"Ethan Hawke", "Julie Delpy"};
for(String name : names){
log("Name : " + name);
}
//removal of items requires an explicit iterator,
//so you can't use a for-each loop in this case
Collection<String> words = new ArrayList<>();
words.add("Il ne lui faut que deux choses: ");
words.add("le");
words.add("pain");
words.add("et");
words.add("le");
words.add("temps.");
words.add("- Alfred de Vigny.");
for(Iterator<String> iter = words.iterator(); iter.hasNext();){
if (iter.next().length() == 4){
iter.remove();
}
}
log("Edited words: " + words.toString());
//if used with a non-parameterized type (not recommended),
//then Object must be used, along with a cast
Collection stuff = new ArrayList();
stuff.add("blah");
for (Object thing : stuff){
String item = (String)thing;
log("Thing : " + item);
}
}
// PRIVATE
private static void log(Object aThing){
System.out.println(aThing);
}
}

See Also :
Iterate without an index
Two ways of using Iterator
Modernize old code

Use interface references to Collections


190

Collected Java Practices

In general, references to objects should be as generic as possible. The user of such a reference will be
protected from possible changes to the underlying implementation class. The ripple effects of such a
change are limited to the single line of code which creates the object, and will not propagate any further.
In the case of collections, this means habitually referring to collection objects using List , Map , Set ,
Queue , and Deque interface references.
Note as well that all of these except Map can be referred to using the even more generic Collection .
Example
import java.util.*;
public class Nucleus {
public static void main (String... arguments) {
Nucleus lithium = new Nucleus (3,4);
//note the generic Map reference is used here, not LinkedHashMap
Map<String, Integer> quarks = lithium.getQuarkSummary();
log("Number of up quarks in lithium nucleus: " + quarks.get("Up"));
log("Number of down quarks in lithium nucleus: " + quarks.get("Down"));
}
public Nucleus(int aNumProtons, int aNumNeutrons) {
fNumProtons = aNumProtons;
fNumNeutrons = aNumNeutrons;
}
/** Note get method is final. */
public final int getNumProtons() {
return fNumProtons;
}
/** Note get method is final. */
public final int getNumNeutrons() {
return fNumNeutrons;
}
/**
* This method returns a Map which summarizes how many quarks of each
* flavour are in the nucleus.
*
* @return a generic Map reference, instead of a LinkedHashMap; the
* user will be protected from the detail of what implementation of Map
* has been selected here.
*/
public final Map<String, Integer> getQuarkSummary() {
LinkedHashMap<String, Integer> result = new LinkedHashMap<>();
int numUp =
fNumProtons * fUP_QUARKS_PER_PROTON +
fNumNeutrons * fUP_QUARKS_PER_NEUTRON
;
int numDown =
fNumProtons * fDOWN_QUARKS_PER_PROTON +
fNumNeutrons * fDOWN_QUARKS_PER_NEUTRON
;
//this makes use of auto-boxing of ints into Integers:
result.put("Up", numUp);
result.put("Down", numDown);
return result;
}
//PRIVATE
private final int fNumProtons;
private final int fNumNeutrons;
private static final int fUP_QUARKS_PER_PROTON = 2;
private static final int fDOWN_QUARKS_PER_PROTON = 1;
private static final int fUP_QUARKS_PER_NEUTRON = 1;
private static final int fDOWN_QUARKS_PER_NEUTRON = 2;

191

Collected Java Practices

private static void log(String aMessage){


System.out.println(aMessage);
}
}

See Also :
Minimize ripple effects

Use standard Collections


In the Collections Framework, there are many implementation classes to choose from. Most of the time,
one of these "primary implementations" is the appropriate choice
ArrayList , for an implementation of the List interface
LinkedHashMap , for an implementation of the Map interface
LinkedHashSet , for an implementation of the Set interface
ArrayDeque , for an implementation of both the the Deque and Queue

interfaces

Before JDK 1.5, HashMap and HashSet were the preferred implementations of Map and Set . However, the
iterators returned by those classes have the somewhat bizarre property of having an undefined order. That
is, iterating over a HashMap or HashSet can return elements in a different order at different times. Their
iteration order is not guaranteed to be repeatable (and often isn't). Even though HashMap and HashSet
have slightly better overall performance, for most business applications it is likely best to avoid such
undefined behavior.
To retain the sorting of items being manipulated in a graphical user interface, TreeSet and TreeMap are
useful. They force a collection to maintain a sort order when the user adds and deletes items.
See Also :
Choosing the right Collection

Avoid clone
Avoid implementing clone .
is very tricky to implement correctly in all circumstances, nearly to the point of being
pathological
the importance of copying objects will always remain, since object fields often need to be
defensively copied
copy constructors and static factory methods provide an alternative to clone , and are much easier
to implement
clone

If you need to extend a superclass that implements clone , then your subclass must implement clone as
well. The quickest solution is for your subclass to simply throw an exception.

192

Collected Java Practices

Example
This example shows a superclass with a typical implementation of clone , and a subclass which has
disabled its clone method.
import java.util.Date;
public abstract class Fruit implements Cloneable {
public Fruit(String aColour, Date aBestBeforeDate) {
super();
fColour = aColour;
//defensive copy needed for this mutable object
fBestBeforeDate = new Date(aBestBeforeDate.getTime());
}
public abstract void ripen();
public String getColour() {
return fColour;
}
public Date getBestBeforeDate() {
//return defensive copy of this mutable object
return new Date(fBestBeforeDate.getTime());
}
/**
* Implement clone as follows
* <ul>
* <li>the class declaration "implements Cloneable" (not needed if already
* declared in superclass)
* <li>declare clone method as public
* <li>if the class is final, clone does not need to throw CloneNotSupportedException
* <li>call super.clone and cast to this class
* <li>as in defensive copying, ensure each mutable field has an independent copy
* constructed, to avoid sharing internal state between objects
* </ul>
*/
@Override public Object clone() throws CloneNotSupportedException {
//get initial bit-by-bit copy, which handles all immutable fields
Fruit result = (Fruit)super.clone();
//mutable fields need to be made independent of this object, for reasons
//similar to those for defensive copies - to prevent unwanted access to
//this object's internal state
result.fBestBeforeDate = new Date(this.fBestBeforeDate.getTime());
return result;
}
// PRIVATE
/**
* Strings are always immutable.
*/
private String fColour;
/**
* Date is a mutable object. In this class, this object field is to be treated
* as belonging entirely to this class, and no user of this class is
* to be able to directly access and change this field's state.
*/
private Date fBestBeforeDate;
}

Here is the subclass, with its clone method disabled.


import java.util.Date;
public final class Apple extends Fruit {
public Apple(String aColour, Date aBestBeforeDate) {
super(aColour, aBestBeforeDate);
}
193

Collected Java Practices

public void ripen() {


//empty implementation of abstract method
}
/**
* The Apple subclass does not support clone.
*/
@Override public final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}

See Also :
Copy constructors
Defensive copying
Factory methods

Implementing compareTo
The compareTo method is the sole member of the Comparable interface, and is not a member of Object.
However, it is quite similar in nature to equals and hashCode . It provides a means of fully ordering
objects.
Implementing Comparable allows:
calling Collections.sort and Collections.binarySearch
calling Arrays.sort and Arrays.binarySearch
using objects as keys in a TreeMap
using objects as elements in a TreeSet
The compareTo method needs to satisfy the following conditions. These conditions have the goal of
allowing objects to be fully sorted, much like the sorting of a database result set on all fields.
anticommutation : x.compareTo(y) is the opposite sign of y.compareTo(x)
exception symmetry : x.compareTo(y) throws exactly the same exceptions as y.compareTo(x)
transitivity : if x.compareTo(y)>0 and y.compareTo(z)>0, then x.compareTo(z)>0 (and same
for less than)
if x.compareTo(y)==0, then x.compareTo(z) has the same sign as y.compareTo(z)
consistency with equals is highly recommended, but not required : x.compareTo(y)==0, if and
only if x.equals(y) ; consistency with equals is required for ensuring sorted collections (such as
TreeSet ) are well-behaved.
One can greatly increase the performance of compareTo by comparing first on items which are most
likely to differ.
When a class extends a concrete Comparable class and adds a significant field, a correct implementation
of compareTo cannot be constructed. The only alternative is to use composition instead of inheritance. (A
similar situation holds true for equals. See Effective Java for more information.)
Compare the various types of fields as follows:
194

Collected Java Practices

numeric primitive : use < and >. There is an exception to this rule: float and double primitives
should be compared using Float.compare(float, float) and Double.compare(double, double). This
avoids problems associated with special border values. (Thanks to Roger Orr in the UK for
pointing this out.)
boolean primitive : use tests of the form (x && !y)
Object : use compareTo . (Note that possibly-null fields present a problem : while x.equals(null)
returns false , x.compareTo(null) will always throw a NullPointerException )
type-safe enumeration : use compareTo , like any Object
collection or array : Comparable does not seem to be intended for these kinds of fields. For
example, List , Map and Set do not implement Comparable . As well, some collections have no
definite order of iteration, so doing an element-by-element comparison cannot be meaningful in
those cases.
If the task is to perform a sort of items which are stored in a relational database, then it is usually much
preferred to let the database perform the sort using the ORDER BY clause, rather than in code.
An alternative to implementing Comparable is passing Comparator objects as parameters. Be aware that
if a Comparator compares only one of several significant fields, then the Comparator is very likely not
synchronized with equals.
All primitive wrapper classes implement Comparable . Note that Boolean did not implement Comparable
until version 1.5, however.
Example
import java.util.*;
import java.io.*;
public final class Account implements Comparable<Account> {
enum AccountType {CASH, MARGIN, RRSP};
public Account (
String aFirstName,
String aLastName,
int aAccountNumber,
int aBalance,
boolean aIsNewAccount,
AccountType aAccountType
) {
//..parameter validations elided
fFirstName = aFirstName;
fLastName = aLastName;
fAccountNumber = aAccountNumber;
fBalance = aBalance;
fIsNewAccount = aIsNewAccount;
fAccountType = aAccountType;
}
/**
* @param aThat is a non-null Account.
*
* @throws NullPointerException if aThat is null.
*/
@Override public int compareTo(Account aThat) {
final int BEFORE = -1;
final int EQUAL = 0;
final int AFTER = 1;
//this optimization is usually worthwhile, and can
//always be added
if (this == aThat) return EQUAL;
//primitive numbers follow this form
if (this.fAccountNumber < aThat.fAccountNumber) return BEFORE;
if (this.fAccountNumber > aThat.fAccountNumber) return AFTER;
//booleans follow this form
195

Collected Java Practices

if (!this.fIsNewAccount && aThat.fIsNewAccount) return BEFORE;


if (this.fIsNewAccount && !aThat.fIsNewAccount) return AFTER;
//objects, including type-safe enums, follow this form
//note that null objects will throw an exception here
int comparison = this.fAccountType.compareTo(aThat.fAccountType);
if (comparison != EQUAL) return comparison;
comparison = this.fLastName.compareTo(aThat.fLastName);
if (comparison != EQUAL) return comparison;
comparison = this.fFirstName.compareTo(aThat.fFirstName);
if (comparison != EQUAL) return comparison;
if (this.fBalance < aThat.fBalance) return BEFORE;
if (this.fBalance > aThat.fBalance) return AFTER;
//all comparisons have yielded equality
//verify that compareTo is consistent with equals (optional)
assert this.equals(aThat) : "compareTo inconsistent with equals.";
return EQUAL;
}
/**
* Define equality of state.
*/
@Override public boolean equals(Object aThat) {
if (this == aThat) return true;
if (!(aThat instanceof Account)) return false;
Account that = (Account)aThat;
return
( this.fAccountNumber == that.fAccountNumber ) &&
( this.fAccountType == that.fAccountType ) &&
( this.fBalance == that.fBalance ) &&
( this.fIsNewAccount == that.fIsNewAccount ) &&
( this.fFirstName.equals(that.fFirstName) ) &&
( this.fLastName.equals(that.fLastName) )
;
}
/**
* A class that overrides equals must also override hashCode.
*/
@Override public int hashCode() {
int result = HashCodeUtil.SEED;
result = HashCodeUtil.hash( result, fAccountNumber );
result = HashCodeUtil.hash( result, fAccountType );
result = HashCodeUtil.hash( result, fBalance );
result = HashCodeUtil.hash( result, fIsNewAccount );
result = HashCodeUtil.hash( result, fFirstName );
result = HashCodeUtil.hash( result, fLastName );
return result;
}
//PRIVATE
private
private
private
private
private

String fFirstName; //non-null


String fLastName; //non-null
int fAccountNumber;
int fBalance;
boolean fIsNewAccount;

/**
* Type of the account, expressed as a type-safe enumeration (non-null).
*/
private AccountType fAccountType;
/**
* Exercise compareTo.
*/
public static void main (String[] aArguments) {
//Note the difference in behaviour in equals and compareTo, for nulls:
String text = "blah";
Integer number = new Integer(10);
//x.equals(null) always returns false:
System.out.println("false: " + text.equals(null));
196

Collected Java Practices

System.out.println("false: " + number.equals(null) );


//x.compareTo(null) always throws NullPointerException:
//System.out.println( text.compareTo(null) );
//System.out.println( number.compareTo(null) );
Account flaubert = new Account(
"Gustave", "Flaubert", 1003, 0,true, AccountType.MARGIN
);
//all of these other versions of "flaubert" differ from the
//original in only one field
Account flaubert2 = new Account(
"Guy", "Flaubert", 1003, 0, true, AccountType.MARGIN
);
Account flaubert3 = new Account(
"Gustave", "de Maupassant", 1003, 0, true, AccountType.MARGIN
);
Account flaubert4 = new Account(
"Gustave", "Flaubert", 2004, 0, true, AccountType.MARGIN
);
Account flaubert5 = new Account(
"Gustave", "Flaubert", 1003, 1, true, AccountType.MARGIN
);
Account flaubert6 = new Account(
"Gustave", "Flaubert", 1003, 0, false, AccountType.MARGIN
);
Account flaubert7 = new Account(
"Gustave", "Flaubert", 1003, 0, true, AccountType.CASH
);
System.out.println( "0: " + flaubert.compareTo(flaubert) );
System.out.println( "first name +: " + flaubert2.compareTo(flaubert) );
//Note capital letters precede small letters
System.out.println( "last name +: " + flaubert3.compareTo(flaubert) );
System.out.println( "acct number +: " + flaubert4.compareTo(flaubert) );
System.out.println( "balance +: " + flaubert5.compareTo(flaubert) );
System.out.println( "is new -: " + flaubert6.compareTo(flaubert) );
System.out.println( "account type -: " + flaubert7.compareTo(flaubert) );
}
}

A sample run of this class gives:


>java -cp . Account
false: false
false: false
0: 0
first name +: 6
last name +: 30
acct number +: 1
balance +: 1
is new -: -1
account type -: -1

In the older JDK 1.4, there are two differences :


the type-safe version of theComparable interface cannot be used. Instead,Object appears, along
with a related cast operation
Boolean objects must be treated differently from other wrapper classes, sinceBoolean did not
implementComparable until JDK 1.5.
Example:
import java.util.*;
import java.io.*;
public final class AccountOld implements Comparable {

197

Collected Java Practices

public AccountOld (
String aFirstName,
String aLastName,
int aAccountNumber,
int aBalance,
boolean aIsNewAccount,
AccountType aAccountType
) {
//..parameter validations elided
fFirstName = aFirstName;
fLastName = aLastName;
fAccountNumber = aAccountNumber;
fBalance = aBalance;
fIsNewAccount = aIsNewAccount;
fAccountType = aAccountType;
}
/**
* @param aThat is a non-null AccountOld.
*
* @throws NullPointerException if aThat is null.
* @throws ClassCastException if aThat is not an AccountOld object.
*/
public int compareTo(Object aThat) {
final int BEFORE = -1;
final int EQUAL = 0;
final int AFTER = 1;
//this optimization is usually worthwhile, and can
//always be added
if ( this == aThat ) return EQUAL;
final AccountOld that = (AccountOld)aThat;
//primitive numbers follow this form
if (this.fAccountNumber < that.fAccountNumber) return BEFORE;
if (this.fAccountNumber > that.fAccountNumber) return AFTER;
//booleans follow this form
if (!this.fIsNewAccount && that.fIsNewAccount) return BEFORE;
if (this.fIsNewAccount && !that.fIsNewAccount) return AFTER;
//Objects, including type-safe enums, follow this form.
//Exception : Boolean implements Comparable in JDK 1.5, but not in 1.4
//Note that null objects will throw an exception here.
int comparison = this.fAccountType.compareTo(that.fAccountType);
if ( comparison != EQUAL ) return comparison;
comparison = this.fLastName.compareTo(that.fLastName);
if ( comparison != EQUAL ) return comparison;
comparison = this.fFirstName.compareTo(that.fFirstName);
if ( comparison != EQUAL ) return comparison;
if (this.fBalance < that.fBalance) return BEFORE;
if (this.fBalance > that.fBalance) return AFTER;
//all comparisons have yielded equality
//verify that compareTo is consistent with equals (optional)
assert this.equals(that) : "compareTo inconsistent with equals.";
return EQUAL;
}
/**
* Define equality of state.
*/
public boolean equals(Object aThat) {
if ( this == aThat ) return true;
if ( !(aThat instanceof Account) ) return false;
AccountOld that = (AccountOld)aThat;
return
( this.fAccountNumber == that.fAccountNumber ) &&
( this.fAccountType == that.fAccountType ) &&
( this.fBalance == that.fBalance ) &&
( this.fIsNewAccount == that.fIsNewAccount ) &&
( this.fFirstName.equals(that.fFirstName) ) &&
198

Collected Java Practices

( this.fLastName.equals(that.fLastName) );
}
/**
* A class that overrides equals must also override hashCode.
*/
public int hashCode() {
int result = HashCodeUtil.SEED;
result = HashCodeUtil.hash( result, fAccountNumber );
result = HashCodeUtil.hash( result, fAccountType );
result = HashCodeUtil.hash( result, fBalance );
result = HashCodeUtil.hash( result, fIsNewAccount );
result = HashCodeUtil.hash( result, fFirstName );
result = HashCodeUtil.hash( result, fLastName );
return result;
}
//PRIVATE
private
private
private
private
private

String fFirstName; //non-null


String fLastName; //non-null
int fAccountNumber;
int fBalance;
boolean fIsNewAccount;

/**
* Type of the account, expressed as a type-safe enumeration (non-null).
*/
private AccountType fAccountType;
/**
* Exercise compareTo.
*/
public static void main (String[] aArguments) {
//Note the difference in behaviour in equals and compareTo, for nulls:
String text = "blah";
Integer number = new Integer(10);
//x.equals(null) always returns false:
System.out.println("false: " + text.equals(null));
System.out.println("false: " + number.equals(null) );
//x.compareTo(null) always throws NullPointerException:
//System.out.println( text.compareTo(null) );
//System.out.println( number.compareTo(null) );
AccountOld flaubert = new AccountOld(
"Gustave", "Flaubert", 1003, 0, true, AccountType.MARGIN
);
//all of these other versions of "flaubert" differ from the
//original in only one field
AccountOld flaubert2 = new AccountOld(
"Guy", "Flaubert", 1003, 0, true, AccountType.MARGIN
);
AccountOld flaubert3 = new AccountOld(
"Gustave", "de Maupassant", 1003, 0, true, AccountType.MARGIN
);
AccountOld flaubert4 = new AccountOld(
"Gustave", "Flaubert", 2004, 0, true, AccountType.MARGIN
);
AccountOld flaubert5 = new AccountOld(
"Gustave", "Flaubert", 1003, 1, true, AccountType.MARGIN
);
AccountOld flaubert6 = new AccountOld(
"Gustave", "Flaubert", 1003, 0, false, AccountType.MARGIN
);
AccountOld flaubert7 = new AccountOld(
"Gustave", "Flaubert", 1003, 0, true, AccountType.CASH
);
System.out.println( "0: " + flaubert.compareTo(flaubert) );
System.out.println( "first name +: " + flaubert2.compareTo(flaubert) );
//Note capital letters precede small letters
System.out.println( "last name +: " + flaubert3.compareTo(flaubert) );
System.out.println( "acct number +: " + flaubert4.compareTo(flaubert) );
System.out.println( "balance +: " + flaubert5.compareTo(flaubert) );
System.out.println( "is new -: " + flaubert6.compareTo(flaubert) );
System.out.println( "account type -: " + flaubert7.compareTo(flaubert) );
}
199

Collected Java Practices

See Also :
Type-Safe Enumerations
Implementing equals
Implementing hashCode
Don't perform basic SQL tasks in code
Modernize old code

Implementing equals
All objects have both identity (the object's location in memory) and state (the object's data). The ==
operator always compares identity. The default implementation of equals compares identity as well.
Sometimes the default implementation of equals has the desired behaviour (as in a type-safe
enumeration, for example), but equals should usually compare state, not identity. This is particularly true
for "data-centric" classes which map to database records.
hashCode

and equals are closely related:

if you override equals, you must override hashCode .


hashCode must generate equal values for equal objects.
equals and hashCode must depend on the same set of "significant" fields. You must use the same
set of fields in both of these methods. You are not required to use all fields. For example, a
calculated field that depends on others should very likely be omitted from equals and hashCode .
Objects placed in a List , Set , or Map (as either a key or value) should have an appropriate definition of
equals. (See, for example, the javadoc for Collection.contains , Map.containsKey , and
Map.containsValue .)
If you extend a concrete class, and add a new field which contributes to equals, then it's not possible to
write a perfectly correct equals method for the new class. Instead, you should use composition instead of
inheritance. (See Effective Java by Joshua Bloch for more information.)
When implementing equals, fields are compared differently, according to their type:
object fields, including collections : use equals
type-safe enumerations : use either equals or == (they amount to the same thing, in this case)
possibly-null object fields : use both == and equals
array fields : use Arrays.equals
primitive fields other than float or double : use ==
float : convert to int using Float.floatToIntBits , then use ==
double : convert to long using Double.doubleToLongBits, then use ==
It's worth noting that if fields are implemented with wrapper classes ( Integer, Boolean, and so on), then
implementation of equals is simpler, since there is only one case: calling the equals method recursively.
(The compareTo method is also simplified in this case.)

200

Collected Java Practices

In an equals method, it's usually worthwhile to order field comparisons such that the most significant
comparisons are performed first. That is, fields most likely to differ should be evaluated first. This allows
the && "short-circuit" logical operator to minimize execution time.
Example 1
The above policies can be collected in a utility class:
/**
* Collected methods which allow easy implementation of <code>equals</code>.
*
* Example use case in a class called Car:
* <pre>
public boolean equals(Object aThat){
if ( this == aThat ) return true;
if ( !(aThat instanceof Car) ) return false;
Car that = (Car)aThat;
return
EqualsUtil.areEqual(this.fName, that.fName) &&
EqualsUtil.areEqual(this.fNumDoors, that.fNumDoors) &&
EqualsUtil.areEqual(this.fGasMileage, that.fGasMileage) &&
EqualsUtil.areEqual(this.fColor, that.fColor) &&
Arrays.equals(this.fMaintenanceChecks, that.fMaintenanceChecks); //array!
}
* </pre>
*
* <em>Arrays are not handled by this class</em>.
* This is because the <code>Arrays.equals</code> methods should be used for
* array fields.
*/
public final class EqualsUtil {
static public boolean areEqual(boolean aThis, boolean aThat){
//System.out.println("boolean");
return aThis == aThat;
}
static public boolean areEqual(char aThis, char aThat){
//System.out.println("char");
return aThis == aThat;
}
static public boolean areEqual(long aThis, long aThat){
/*
* Implementation Note
* Note that byte, short, and int are handled by this method, through
* implicit conversion.
*/
//System.out.println("long");
return aThis == aThat;
}
static public boolean areEqual(float aThis, float aThat){
//System.out.println("float");
return Float.floatToIntBits(aThis) == Float.floatToIntBits(aThat);
}
static public boolean areEqual(double aThis, double aThat){
//System.out.println("double");
return Double.doubleToLongBits(aThis) == Double.doubleToLongBits(aThat);
}
/**
* Possibly-null object field.
*
* Includes type-safe enumerations and collections, but does not include
* arrays. See class comment.
*/
static public boolean areEqual(Object aThis, Object aThat){
//System.out.println("Object");
return aThis == null ? aThat == null : aThis.equals(aThat);
}
}

201

Collected Java Practices

Car

is a class which uses EqualsUtil to implement its equals method:

import java.util.*;
public final class Car {
public Car (
String aName, int aNumDoors, List<String> aOptions,
double aGasMileage, String aColor, Date[] aMaintenanceChecks
){
fName = aName;
fNumDoors = aNumDoors;
fOptions = new ArrayList<>(aOptions);
fGasMileage = aGasMileage;
fColor = aColor;
fMaintenanceChecks = new Date[aMaintenanceChecks.length];
for (int idx=0; idx < aMaintenanceChecks.length; ++idx) {
fMaintenanceChecks[idx] = new Date( aMaintenanceChecks[idx].getTime() );
}
}
@Override public boolean equals(Object aThat) {
//check for self-comparison
if ( this == aThat ) return true;
//use instanceof instead of getClass here for two reasons
//1. if need be, it can match any supertype, and not just one class;
//2. it renders an explict check for "that == null" redundant, since
//it does the check for null already - "null instanceof [type]" always
//returns false. (See Effective Java by Joshua Bloch.)
if ( !(aThat instanceof Car) ) return false;
//Alternative to the above line :
//if ( aThat == null || aThat.getClass() != this.getClass() ) return false;
//cast to native object is now safe
Car that = (Car)aThat;
//now a proper field-by-field evaluation can be made
return
EqualsUtil.areEqual(this.fName, that.fName) &&
EqualsUtil.areEqual(this.fNumDoors, that.fNumDoors) &&
EqualsUtil.areEqual(this.fOptions, that.fOptions) &&
EqualsUtil.areEqual(this.fGasMileage, that.fGasMileage) &&
EqualsUtil.areEqual(this.fColor, that.fColor) &&
Arrays.equals(this.fMaintenanceChecks, that.fMaintenanceChecks);
}
//..other methods elided
// PRIVATE
/**
* The following fields are chosen to exercise most of the different
* cases.
*/
private String fName;
private int fNumDoors;
private List<String> fOptions;
private double fGasMileage;
private String fColor; //treat as possibly-null
private Date[] fMaintenanceChecks;
/**
* Exercise the equals method.
*/
public static void main (String... aArguments) {
List<String> options = new ArrayList<String>();
options.add("sunroof");
Date[] dates = new Date[1];
dates[0] = new Date();
//Create a bunch of Cars; only one and two should be equal
Car one = new Car("Nissan", 2, options, 46.3, "Green", dates);
//two is equal to one
Car two = new Car("Nissan", 2, options, 46.3, "Green", dates);
202

Collected Java Practices

//three has a differs in fName only


Car three = new Car("Pontiac", 2, options, 46.3, "Green", dates);
//four differs in fNumDoors only
Car four = new Car("Nissan", 4, options, 46.3, "Green", dates);
//five differs in fOptions only
List<String> optionsTwo = new ArrayList<String>();
optionsTwo.add("air conditioning");
Car five = new Car("Nissan", 2, optionsTwo, 46.3, "Green", dates);
//six differs in fGasMileage only
Car six = new Car("Nissan", 2, options, 22.1, "Green", dates);
//seven differs in fColor only
Car seven = new Car("Nissan", 2, options, 46.3, "Fuchsia", dates);
//eight differs in fMaintenanceChecks only
Date[] datesTwo = new Date[1];
datesTwo[0] = new Date(1000000);
Car eight = new Car("Nissan", 2, options, 46.3, "Green", datesTwo);
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(
System.out.println(

"one
"one
"two
"one
"one
"one
"one
"one
"one
"one

=
=
=
=
=
=
=
=
=
=

one: " + one.equals(one) );


two: " + one.equals(two) );
one: " + two.equals(one) );
three: " + one.equals(three) );
four: " + one.equals(four) );
five: " + one.equals(five) );
six: " + one.equals(six) );
seven: " + one.equals(seven) );
eight: " + one.equals(eight) );
null: " + one.equals(null) );

}
}

An example run of this class demonstrates that only objects one and two are equal:
one
one
two
one
one
one
one
one
one
one

=
=
=
=
=
=
=
=
=
=

one: true
two: true
one: true
three: false
four: false
five: false
six: false
seven: false
eight: false
null: false

Example 2
The WEB4J tool has a utility class for implementing equals. The following is an example of a Model
Object implemented with WEB4J.
Items to note regarding this equals method:
it reads at a slightly higher level
it's easier to remember, and easier to write
it does not use multiple return statements
the fields used in equals and hashCode are clearly 'in sync' (as they must be), since they both call
the same method named getSignificantFields()

package hirondelle.fish.main.discussion;

203

Collected Java Practices

import
import
import
import
import
import

java.util.*;
hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Check;
hirondelle.web4j.security.SafeText;
static hirondelle.web4j.util.Consts.FAILS;

/**
Comment posted by a possibly-anonymous user.
*/
public final class Comment {
/**
Constructor.
@param aUserName identifies the logged in user posting the comment.
@param aBody the comment, must have content.
@param aDate date and time when the message was posted.
*/
public Comment (
SafeText aUserName, SafeText aBody, Date aDate
) throws ModelCtorException {
fUserName = aUserName;
fBody = aBody;
fDate = aDate.getTime();
validateState();
}
/** Return the logged in user name passed to the constructor. */
public SafeText getUserName() {
return fUserName;
}
/** Return the body of the message passed to the constructor.
public SafeText getBody() {
return fBody;
}

*/

/**
Return a <a href="http://www.javapractices.com/Topic15.cjp">defensive copy</a>
of the date passed to the constructor.
<P>The caller may change the state of the returned value, without affecting
the internals of this <tt>Comment</tt>. Such copying is needed since
a {@link Date} is a mutable object.
*/
public Date getDate() {
// the returned object is independent of fDate
return new Date(fDate);
}
/** Intended for debugging only. */
@Override public String toString() {
return ModelUtil.toStringFor(this);
}
@Override public boolean equals( Object aThat ) {
Boolean result = ModelUtil.quickEquals(this, aThat);
if ( result == null ){
Comment that = (Comment) aThat;
result = ModelUtil.equalsFor(
this.getSignificantFields(), that.getSignificantFields()
);
}
return result;
}
@Override public int hashCode() {
if ( fHashCode == 0 ) {
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE //
private final SafeText fUserName;
private final SafeText fBody;
/** Long is used here instead of Date in order to ensure immutability.*/
204

Collected Java Practices

private final long fDate;


private int fHashCode;
private Object[] getSignificantFields(){
return new Object[] {fUserName, fBody, new Date(fDate)};
}
private void validateState() throws ModelCtorException {
ModelCtorException ex = new ModelCtorException();
if( FAILS == Check.required(fUserName) ) {
ex.add("User name must have content.");
}
if ( FAILS == Check.required(fBody) ) {
ex.add("Comment body must have content.");
}
if ( ! ex.isEmpty() ) throw ex;
}
}

See Also :
Implementing compareTo
Implementing hashCode
Beware of instanceof operator
Multiple return statements
Use boxing with care

Implementing hashCode
Implementing hashCode :
if a class overrides equals, it must override hashCode
when they are both overridden, equals and hashCode must use the same set of fields
if two objects are equal, then their hashCode values must be equal as well
if the object is immutable, then hashCode is a candidate for caching and lazy initialization
It's a popular misconception that hashCode provides a unique identifier for an object. It does not.
Example 1
The following utility class allows simple construction of an effective hashCode method. It is based on the
recommendations of Effective Java, by Joshua Bloch.
import java.lang.reflect.Array;
/**
* Collected methods which allow easy implementation of <tt>hashCode</tt>.
*
* Example use case:
* <pre>
* public int hashCode(){
*
int result = HashCodeUtil.SEED;
*
//collect the contributions of various fields
*
result = HashCodeUtil.hash(result, fPrimitive);
*
result = HashCodeUtil.hash(result, fObject);
*
result = HashCodeUtil.hash(result, fArray);
*
return result;
* }
* </pre>
205

Collected Java Practices

*/
public final class HashCodeUtil {
/**
* An initial value for a <tt>hashCode</tt>, to which is added contributions
* from fields. Using a non-zero value decreases collisons of <tt>hashCode</tt>
* values.
*/
public static final int SEED = 23;
/** booleans. */
public static int hash(int aSeed, boolean aBoolean) {
log("boolean...");
return firstTerm( aSeed ) + (aBoolean ? 1 : 0);
}
/*** chars. */
public static int hash(int aSeed, char aChar) {
log("char...");
return firstTerm(aSeed) + (int)aChar;
}
/** ints. */
public static int hash(int aSeed , int aInt) {
/*
* Implementation Note
* Note that byte and short are handled by this method, through
* implicit conversion.
*/
log("int...");
return firstTerm(aSeed) + aInt;
}
/** longs. */
public static int hash(int aSeed , long aLong) {
log("long...");
return firstTerm(aSeed) + (int)(aLong ^ (aLong >>> 32));
}
/** floats. */
public static int hash(int aSeed , float aFloat) {
return hash(aSeed, Float.floatToIntBits(aFloat));
}
/** doubles. */
public static int hash(int aSeed , double aDouble) {
return hash( aSeed, Double.doubleToLongBits(aDouble) );
}
/**
* <tt>aObject</tt> is a possibly-null object field, and possibly an array.
*
* If <tt>aObject</tt> is an array, then each element may be a primitive
* or a possibly-null object.
*/
public static int hash(int aSeed , Object aObject) {
int result = aSeed;
if (aObject == null){
result = hash(result, 0);
}
else if (!isArray(aObject)){
result = hash(result, aObject.hashCode());
}
else {
int length = Array.getLength(aObject);
for (int idx = 0; idx < length; ++idx) {
Object item = Array.get(aObject, idx);
//if an item in the array references the array itself, prevent infinite
looping
if(! (item == aObject))
//recursive call!
result = hash(result, item);
}
}
return result;
}
// PRIVATE
private static final int fODD PRIME NUMBER = 37;
206

Collected Java Practices

private static int firstTerm(int aSeed){


return fODD_PRIME_NUMBER * aSeed;
}
private static boolean isArray(Object aObject){
return aObject.getClass().isArray();
}
private static void log(String aMessage){
System.out.println(aMessage);
}
}

Here's an example of its use. When the logging statements are uncommented in HashCodeUtil, the reuse
of the boolean, char , int and long versions of hash is demonstrated:

boolean...
char...
int...
long...
long...
int...
int...
int...
int...
int...
hashCode value: -608077094
import java.util.*;
public final class ApartmentBuilding {
public ApartmentBuilding(
boolean aIsDecrepit,
char aRating,
int aNumApartments,
long aNumTenants,
double aPowerUsage,
float aWaterUsage,
byte aNumFloors,
String aName,
List<String> aOptions,
Date[] aMaintenanceChecks
){
fIsDecrepit = aIsDecrepit;
fRating = aRating;
fNumApartments = aNumApartments;
fNumTenants = aNumTenants;
fPowerUsage = aPowerUsage;
fWaterUsage = aWaterUsage;
fNumFloors = aNumFloors;
fName = aName;
fOptions = aOptions;
fMaintenanceChecks = aMaintenanceChecks;
}
@Override public boolean equals(Object that) {
if (this == that) return true;
if (!(that instanceof ApartmentBuilding)) return false;
ApartmentBuilding thatBuilding = (ApartmentBuilding)that;
return hasEqualState(thatBuilding);
}
@Override public int hashCode() {
//this style of lazy initialization is
//suitable only if the object is immutable
207

Collected Java Practices

if (fHashCode == 0) {
int result = HashCodeUtil.SEED;
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
result = HashCodeUtil.hash(result,
fHashCode = result;
}
return fHashCode;

fIsDecrepit);
fRating);
fNumApartments);
fNumTenants);
fPowerUsage);
fWaterUsage);
fNumFloors);
fName);
fOptions);
fMaintenanceChecks);

}
//..elided..
// PRIVATE
/**
* The following fields are chosen to exercise most of the different
* cases.
*/
private boolean fIsDecrepit;
private char fRating;
private int fNumApartments;
private long fNumTenants;
private double fPowerUsage;
private float fWaterUsage;
private byte fNumFloors;
private String fName; //possibly null, say
private List<String> fOptions; //never null
private Date[] fMaintenanceChecks; //never null
private int fHashCode;
/**
* Here, for two ApartmentBuildings to be equal, all fields must be equal.
*/
private boolean hasEqualState(ApartmentBuilding that) {
//note the different treatment for possibly-null fields
return
( this.fName==null ? that.fName==null : this.fName.equals(that.fName) ) &&
( this.fIsDecrepit == that.fIsDecrepit )&&
( this.fRating == that.fRating )&&
( this.fNumApartments == that.fNumApartments ) &&
( this.fNumTenants == that.fNumTenants ) &&
( this.fPowerUsage == that.fPowerUsage ) &&
( this.fWaterUsage == that.fWaterUsage ) &&
( this.fNumFloors == that.fNumFloors ) &&
( this.fOptions.equals(that.fOptions) )&&
( Arrays.equals(this.fMaintenanceChecks, that.fMaintenanceChecks) )
;
}
/** Exercise hashcode. */
public static void main (String [] aArguments) {
List<String> options = new ArrayList<>();
options.add("pool");
Date[] maintenanceDates = new Date[1];
maintenanceDates[0] = new Date();
byte numFloors = 8;
ApartmentBuilding building = new ApartmentBuilding (
false, 'B', 12, 396L,
5.2, 6.3f, numFloors, "Palisades",
options, maintenanceDates
);
System.out.println("hashCode value: " + building.hashCode());
}
}

208

Collected Java Practices

Example 2
The WEB4J tool defines a utility class for implementing hashCode . Here is an example of a Model
Object implemented with that utility.
Items to note:
the hashCode value is calculated only once, and only if it is needed. This is only possible since this
is an immutable object.
calling the getSignificantFields() method ensures hashCode and equals remain 'in sync'

package hirondelle.fish.main.discussion;
import
import
import
import
import
import

java.util.*;
hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Check;
hirondelle.web4j.security.SafeText;
static hirondelle.web4j.util.Consts.FAILS;

/**
Comment posted by a possibly-anonymous user.
*/
public final class Comment {
/**
Constructor.
@param aUserName identifies the logged in user posting the comment.
@param aBody the comment, must have content.
@param aDate date and time when the message was posted.
*/
public Comment (
SafeText aUserName, SafeText aBody, Date aDate
) throws ModelCtorException {
fUserName = aUserName;
fBody = aBody;
fDate = aDate.getTime();
validateState();
}
/** Return the logged in user name passed to the constructor. */
public SafeText getUserName() {
return fUserName;
}
/** Return the body of the message passed to the constructor.
public SafeText getBody() {
return fBody;
}

*/

/**
Return a <a href="http://www.javapractices.com/Topic15.cjp">defensive copy</a>
of the date passed to the constructor.
<P>The caller may change the state of the returned value, without affecting
the internals of this <tt>Comment</tt>. Such copying is needed since
a {@link Date} is a mutable object.
*/
public Date getDate() {
// the returned object is independent of fDate
return new Date(fDate);
}
/** Intended for debugging only. */
@Override public String toString() {
return ModelUtil.toStringFor(this);
}
@Override public boolean equals( Object aThat ) {
Boolean result = ModelUtil.quickEquals(this, aThat);
( result ==
){
209

Collected Java Practices

if

null
Comment that = (Comment) aThat;
result = ModelUtil.equalsFor(
this.getSignificantFields(), that.getSignificantFields()
);

}
return result;
}
@Override public int hashCode() {
if ( fHashCode == 0 ) {
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE //
private final SafeText fUserName;
private final SafeText fBody;
/** Long is used here instead of Date in order to ensure immutability.*/
private final long fDate;
private int fHashCode;
private Object[] getSignificantFields(){
return new Object[] {fUserName, fBody, new Date(fDate)};
}
private void validateState() throws ModelCtorException {
ModelCtorException ex = new ModelCtorException();
if( FAILS == Check.required(fUserName) ) {
ex.add("User name must have content.");
}
if ( FAILS == Check.required(fBody) ) {
ex.add("Comment body must have content.");
}
if ( ! ex.isEmpty() ) throw ex;
}
}

See Also :
Implementing equals
Immutable objects
Lazy initialization

Implementing toString
The toString method is widely implemented. It provides a simple, convenient mechanism for debugging
classes during development. It's also widely used for logging, and for passing informative error messages
to Exception constructors and assertions. When used in these informal ways, the exact format of
toString is not part of the contract of the method, and callers should not rely on the exact format of the
returned String.
The toString method may occasionally be used more formally, however. An example is a simple
mechanism for translating an object into a well-defined textual form ( toString ) and back again
( valueOf). In this case, it's particularly important to specify the exact form of such text in javadoc.
When implementing toString , StringBuilder can be used instead of the + concatenation operator,
since the StringBuilder.append operation is slightly faster.
210

Collected Java Practices

Example 1
import java.util.*;
public final class Truck {
/** Simple test harness. */
public static void main(String... aArgs){
Truck planetKiller = new Truck();
System.out.println(planetKiller);
}
/**
* Intended only for debugging.
*
* <P>Here, the contents of every field are placed into the result, with
* one field per line.
*/
@Override public String toString() {
StringBuilder result = new StringBuilder();
String NEW_LINE = System.getProperty("line.separator");
result.append(this.getClass().getName() + " Object {" + NEW_LINE);
result.append(" Name: " + fName + NEW_LINE);
result.append(" Number of doors: " + fNumDoors + NEW_LINE);
result.append(" Year manufactured: " + fYearManufactured + NEW_LINE );
result.append(" Color: " + fColor + NEW_LINE);
//Note that Collections and Maps also override toString
result.append(" Options: " + fOptions + NEW_LINE);
result.append("}");
return result.toString();
}
//..other methods elided
// PRIVATE //
private
private
private
private
private

String fName = "Dodge";


int fNumDoors = 2;
Date fYearManufactured = new Date();
String fColor = "Fuchsia";
List<String> fOptions = Arrays.asList("Air Conditioning");

Example output:
Truck Object {
Name: Dodge
Number of doors: 2
Year manufactured: Wed Aug 29 15:49:10 ADT 2007
Color: Fuchsia
Options: [Air Conditioning]
}

Example 2
This implementation uses reflection to inspect both field names and field values. Note that superclass
fields do not contribute to this implementation.
import java.util.*;
import java.lang.reflect.Field;
public final class Van {
/**
* Build a Van object and display its textual representation.
*
* Note that the Collection classes have
* their own implementation of <code>toString</code>, as exemplified here
* by the List field holding the Options.
*/
211

Collected Java Practices

public static void main (String... arguments) {


List<String> options = new ArrayList<>();
options.add("Air Conditioning");
options.add("Leather Interior");
Van van = new Van( "Dodge", 4, new Date(), "Blue", options);
System.out.println(van);
}
public Van(
String aName,
int aNumDoors,
Date aYearManufactured,
String aColor,
List<String> aOptions
){
fName = aName;
fNumDoors = aNumDoors;
fYearManufactured = aYearManufactured;
fColor = aColor;
fOptions = aOptions;
}
//..other methods elided
/**
* Intended only for debugging.
*
* <P>Here, a generic implementation uses reflection to print
* names and values of all fields <em>declared in this class</em>. Note that
* superclass fields are left out of this implementation.
*
* <p>The format of the presentation could be standardized by using
* a MessageFormat object with a standard pattern.
*/
@Override public String toString() {
StringBuilder result = new StringBuilder();
String newLine = System.getProperty("line.separator");
result.append(this.getClass().getName());
result.append(" Object {");
result.append(newLine);
//determine fields declared in this class only (no fields of superclass)
Field[] fields = this.getClass().getDeclaredFields();
//print field names paired with their values
for (Field field : fields) {
result.append(" ");
try {
result.append(field.getName());
result.append(": ");
//requires access to private field:
result.append(field.get(this));
}
catch (IllegalAccessException ex) {
System.out.println(ex);
}
result.append(newLine);
}
result.append("}");
return result.toString();
}
// PRIVATE
private String fName;
private int fNumDoors;
private Date fYearManufactured;
private String fColor;
private List<String> fOptions;
}

Example output:

212

Collected Java Practices

Van Object {
fName: Dodge
fNumDoors: 4
fYearManufactured: Thu Sep 30 19:16:14 EDT 2004
fColor: Blue
fOptions: [Air Conditioning, Leather Interior]
}

Example 3
The WEB4J tool provides a utility method for implementing toString in a single line of code. Its
implementation uses reflection.
import
import
import
import
import
import
import

java.math.BigDecimal;
hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Id;
hirondelle.web4j.model.Check;
hirondelle.web4j.util.Util;
hirondelle.web4j.model.Validator;

/** Model Object for a Restaurant. */


public final class Resto {
/**
* Full constructor.
*
* @param aId underlying database internal identifier (optional) 1..50 characters
* @param aName of the restaurant (required), 2..50 characters
* @param aLocation street address of the restaurant (optional), 2..50 characters
* @param aPrice of the fish and chips meal (optional) $0.00..$100.00
* @param aComment on the restaurant in general (optional) 2..50 characters
*/
public Resto(
Id aId, String aName, String aLocation, BigDecimal aPrice, String aComment
) throws ModelCtorException {
fId = aId;
fName = Util.trimPossiblyNull(aName);
fLocation = Util.trimPossiblyNull(aLocation);
fPrice = aPrice;
fComment = Util.trimPossiblyNull(aComment);
validateState();
}
public
public
public
public
public

Id getId() { return fId; }


String getName() { return fName; }
String getLocation() { return fLocation;
BigDecimal getPrice() { return fPrice; }
String getComment() { return fComment; }

/** Intended for debugging only. */


@Override public String toString() {
return ModelUtil.toStringFor(this);
}
//..elided
//PRIVATE//
private final
private final
private final
private final
private final

Id fId;
String fName;
String fLocation;
BigDecimal fPrice;
String fComment;

//..elided
}

Example output:
213

Collected Java Practices

hirondelle.fish.main.resto.Resto {
Name: Cedars Eatery
Location: Water and Prince
Id: 6
Comment: Big portions
Price: 7.89
}

Example 4
is a type safe enumeration. The return value of toString is used in the normal operation of
the program - not just for logging. Note that it forms a pair with the valueFrom(String) method, since
one formats the object into a String, and the other parses a String into an object.
QuoteField

package hirondelle.stocks.table;
/**
* Enumeration for the fields of the
* {@link hirondelle.stocks.quotes.Quote} class.
*
* Advantages to using this class as part of a table model :
* <ul>
* <li> can parse text which maps table columns to fields
* <li> can be used for column names
* <li> length of <tt>QuoteField.values()</tt> gives the column count
* </ul>
*/
public enum QuoteField {
Stock("Stock"),
Price("Price"),
Change("Change"),
PercentChange("%Change"),
Profit("Profit"),
PercentProfit("%Profit");
/**
* Return a text representation of the <tt>QuoteField</tt>.
*
* Return values : <tt>Stock, Price, Change, %Change, Profit, %Profit</tt>.
* @return value contains only letters, and possibly a percent sign.
*/
@Override public String toString() {
return fName;
}
/**
* Parse text into a <tt>QuoteField</tt>.
*
* <P>The text is matched according to the value of {@link #toString()},
* not from the symbolic name of the enumerated item.
*/
public static QuoteField valueFrom(String aText){
for (QuoteField quoteField: values()){
if(quoteField.toString().equals(aText)) {
return quoteField;
}
}
throw new IllegalArgumentException("Cannot parse into a QuoteField: " + aText);
}
private final String fName;
/**
* @param aName only letters and percent sign are valid characters.
*/
private QuoteField(String aName) {
fName = aName;
}
}

214

Collected Java Practices

See Also :
String concatenation does not scale
Factory methods
Arrays as String

Never rely on finalize


You cannot rely on finalize to always reclaim resources. Instead, if an object has resources which need
to be recovered, it must define a method for that very purpose - for example a close method, or a
dispose method. The caller is then required to explicitly call that method when finished with the object,
to ensure any related resources are recovered.
With JDK 7+, the try-with-resources feature can be used to reclaim resources for any object that
implements AutoCloseable .
For an extensive discussion, please see the "Avoid Finalizers" topic in Effective Java.
The System.runFinalizersOnExit(boolean) method is deprecated.
See Also :
Recovering resources

Arrays as String
Logging the contents of aCollection is simple, sinceAbstractCollection.toString is always
available. For an array, however, the default toString method is not very informative, and doesn't
include the array contents.
To provide more useful representations of arrays, various toString methods (and the deepToString
method) were added to the Arrays class in JDK 1.5. Those methods can be used when available, as in:
Arrays.toString(myArray);
Arrays.deepToString(myObjectArray); //recursive

If you need an alternative, you can simply convert the array to a collection, as in
Arrays.asList(myArray).toString();

Finally, here is a third option, using a utility class which:


uses a slightly different style from that used in Arrays
works with versions of the JDK prior to 1.5.
returns text in the same format as AbstractCollection.toString
handles arrays of all primitive types, and arrays of Objects as well
handles arrays of arrays by using recursion

215

Collected Java Practices

package myapp.business;
import java.lang.reflect.Array;
/**
* Convenience method for producing a simple textual
* representation of an array.
*
* <P>The format of the returned <code>String</code> is the same as
* <code>AbstractCollection.toString</code>:
* <ul>
* <li>non-empty array: [blah, blah]
* <li>empty array: []
* <li>null array: null
* </ul>
*
* @author Jerome Lacoste
* @author www.javapractices.com
*/
public final class ArrayToString {
/**
* <code>aArray</code> is a possibly-null array whose elements are
* primitives or objects; arrays of arrays are also valid, in which case
* <code>aArray</code> is rendered in a nested, recursive fashion.
*/
public static String get(Object aArray){
if (aArray == null) return fNULL;
checkObjectIsArray(aArray);
StringBuilder result = new StringBuilder(fSTART_CHAR);
int length = Array.getLength(aArray);
for (int idx = 0 ; idx < length ; ++idx) {
Object item = Array.get(aArray, idx);
if (isNonNullArray(item)){
//recursive call!
result.append(get(item));
}
else{
result.append(item);
}
if (! isLastItem(idx, length)) {
result.append(fSEPARATOR);
}
}
result.append(fEND_CHAR);
return result.toString();
}
// PRIVATE
private static
private static
private static
private static

final
final
final
final

String
String
String
String

fSTART_CHAR = "[";
fEND_CHAR = "]";
fSEPARATOR = ", ";
fNULL = "null";

private static void checkObjectIsArray(Object aArray){


if (! aArray.getClass().isArray()) {
throw new IllegalArgumentException("Object is not an array.");
}
}
private static boolean isNonNullArray(Object aItem){
return aItem != null && aItem.getClass().isArray();
}
private static boolean isLastItem(int aIdx, int aLength){
return (aIdx == aLength - 1);
}
/** Test harness. */
public static void main(String... args) {
boolean[] booleans = {true, false, false};
char[] chars = {'B', 'P', 'H'};
byte[] bytes = {3};
short[] shorts = {5,6};
int[] ints = {7,8,9,10};
long[] longs = {100,101,102};
216

Collected Java Practices

float[] floats = { 99.9f, 63.2f};


double[] doubles = { 212.2, 16.236, 42.2};
String[] strings = {"blah", "blah", "blah"};
java.util.Date[] dates = { new java.util.Date(), new java.util.Date() };
System.out.println("booleans: " + get(booleans));
System.out.println("chars: " + get(chars));
System.out.println("bytes: " + get(bytes));
System.out.println("shorts: " + get(shorts));
System.out.println("ints: " + get(ints));
System.out.println("longs: " + get(longs));
System.out.println("floats: " + get(floats));
System.out.println("double: " + get(doubles));
System.out.println("strings: " + get(strings));
System.out.println("dates: " + get(dates));
int[] nullInts = null;
int[] emptyInts = {};
String[] emptyStrings = {"", ""};
String[] nullStrings = {null, null};
System.out.println("null ints: " + get(nullInts));
System.out.println("empty ints: " + get(emptyInts));
System.out.println("empty Strings: " + get(emptyStrings));
System.out.println("null Strings: " + get(nullStrings));
String[] arrayA = {"A", "a"};
String[] arrayB = {"B", "b"};
String[][] arrayOfArrays = {arrayA, arrayB};
System.out.println("array Of Arrays: " + get(arrayOfArrays));
}
}

An example run of this class:


>java -cp . myapp.business.ArrayToString
booleans: [true, false, false]
chars: [B, P, H]
bytes: [3]
shorts: [5, 6]
ints: [7, 8, 9, 10]
longs: [100, 101, 102]
floats: [99.9, 63.2]
double: [212.2, 16.236, 42.2]
strings: [blah, blah, blah]
dates: [Wed Jun 18 09:55:08 EDT 2003, Wed Jun 18 09:55:08 EDT 2003]
null ints: null
empty ints: []
empty Strings: [, ]
null Strings: [null, null]
array Of Arrays: [[A, a], [B, b]]

See Also :
Implementing toString

Clipboard copy and paste


Here's a demonstration of how to move text to and from the Clipboard .
In Java, there are actually two kinds of Clipboard - system and local. The following example uses the
217

Collected Java Practices

system Clipboard . It corresponds to the usual idea of sharing data between otherwise independent
applications running on the same computer.
Local Clipboard s are visible only within a single Java application. They are created by simply passing a
name to the Clipboard constructor.
import
import
import
import
import
import
import
import

java.awt.datatransfer.Clipboard;
java.awt.datatransfer.ClipboardOwner;
java.awt.datatransfer.Transferable;
java.awt.datatransfer.StringSelection;
java.awt.datatransfer.DataFlavor;
java.awt.datatransfer.UnsupportedFlavorException;
java.awt.Toolkit;
java.io.*;

public final class TextTransfer implements ClipboardOwner {


public static void main(String... aArguments ){
TextTransfer textTransfer = new TextTransfer();
//display what is currently on the clipboard
System.out.println("Clipboard contains:" + textTransfer.getClipboardContents());
//change the contents and then re-display
textTransfer.setClipboardContents("blah, blah, blah");
System.out.println("Clipboard contains:" + textTransfer.getClipboardContents());
}
/**
* Empty implementation of the ClipboardOwner interface.
*/
@Override public void lostOwnership(Clipboard aClipboard, Transferable aContents){
//do nothing
}
/**
* Place a String on the clipboard, and make this class the
* owner of the Clipboard's contents.
*/
public void setClipboardContents(String aString){
StringSelection stringSelection = new StringSelection(aString);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, this);
}
/**
* Get the String residing on the clipboard.
*
* @return any text found on the Clipboard; if none found, return an
* empty String.
*/
public String getClipboardContents() {
String result = "";
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
//odd: the Object param of getContents is not currently used
Transferable contents = clipboard.getContents(null);
boolean hasTransferableText =
(contents != null) &&
contents.isDataFlavorSupported(DataFlavor.stringFlavor)
;
if (hasTransferableText) {
try {
result = (String)contents.getTransferData(DataFlavor.stringFlavor);
}
catch (UnsupportedFlavorException | IOException ex){
System.out.println(ex);
ex.printStackTrace();
}
}
return result;
}
}

218

Collected Java Practices

An example run of this class:


>java -cp . TextTransfer
Clipboard contains:Java is in Indonesia.
Clipboard contains:blah, blah, blah
>

Command line operations


Most Java programmers use an Integrated Development Environment (IDE) such as IntelliJ, NetBeans, or
Eclipse. Modern IDEs are usually pleasant to use, and go a long way in simplifying things for a typical
programmer.
However, being familiar with Java's basic command line tools is still very useful. It not only gives you a
better understanding of what's happening, it's essential in situations where you need to use the command
line tools directly (scripts, tools, and so on).
The most commonly used tools are:
javac - the java compiler
java - launching programs on the Java Runtime Environment
javadoc - generating documentation for your project
Here are some examples of running command line tools. It assumes a project with the following directory
structure:
PROJECT_HOME
-> lib (jar files)
-> src (java code)
-> hirondelle (top-level package; no .java files)
-> ante (.java files)
-> deluvian (.java files)

Compiling With javac


PROJECT_HOME>javac -cp lib\* src\hirondelle\ante\*.java src\hirondelle\ante\deluvian\*.java

This compiles in place, and creates .class files beside .java files. If you want to place generated class files
elsewhere, use the -d option to put them into an existing directory:
PROJECT_HOME>javac -cp lib\* -d build src\hirondelle\ante\*.java src\hirondelle\ante\deluvian\*.java

Also, notice that all jars in a directory can be succinctly referenced using 'lib\*'. However, referencing the
source files that you need to compile is a different story: all of your packages need to be listed one by
one. That is, you can't simply specify a single root directory for your source files. This is a rather serious
defect of javac.
If your jars are in various directories, then the classpath is a list delimited by semi-colons:
-cp lib\*;C:\abc\one.jar;C:\xyz\two.jar

Running With java


Any class having a main method may be run as a Java program. The main method must have the form:

219

Collected Java Practices

public static void main(String... aArgs){...}

An application is run by referencing a fully-qualified class name, but without the .class suffix. Assuming
that the classes have been compiled in place, our example application can be run using:
PROJECT_HOME>java -cp lib\*;src hirondelle.ante.Launcher

Passing 3 arguments to the program:


PROJECT_HOME>java -cp lib\*;src hirondelle.ante.Launcher arg1 arg2 "Arg Three"

Setting a System property value with -D:


PROJECT_HOME>java -Dblah=whatever -cp lib\*;src hirondelle.ante.Launcher

Set the initial and maximum size of the object allocation pool to 5 Meg and 100 Meg, respectively:
PROJECT_HOME>java -cp lib\*;src -Xms5m -Xmx100m hirondelle.ante.Launcher

Run a jar file whose manifest specifies a Main-Class attribute:


C:\@build\dist>java -jar example-app-1.1.jar

The javaw command is the same as the java command, with the only difference being that javaw has no
associated console window.

Using javadoc
For files under the src directory, the following command will javadoc all packages starting with
'hirondelle.ante', and output the result to C:\@build\javadoc:
javadoc -sourcepath src -subpackages hirondelle.ante -classpath lib\* -d C:\@build\javadoc

The javadoc tool has many options. You may find these especially useful:
-linksource: links your javadoc to a static snapshot of the underlying source code (very nice).
-windowtitle, -header and -footer: often useful for showing the name and version of your
application.
-link: link to the JDK's javadoc as well
-noqualifier: remove tedious package prefixes from JDK classes
define the minimum class scope to be processed, using one of: -private, -package, -protected, public.
Here's a second example, with more options:
javadoc
-sourcepath src
-subpackages hirondelle.ante
-package
-classpath lib\*
-d C:\@build\javadoc
-linksource
-link http://docs.oracle.com/javase/7/docs/api/
-noqualifier java.*:javax.*
-windowtitle "My App 1.0"
-header "<b>My App 1.0</b>"
-footer "<a href='http://www.blah.com'>My App</a>"

Javadoc can also be controlled using the following items, placed beside your source code:
package-info.java - Each package can include a package-info.java file. It contains package-level
documentation. Despite its extension, a package-info.java file is not a Java class file.
doc-files subdirectories - each package can include a 'doc-files' subdirectory, containing items that
can be linked from javadoc (images, html files, and so on) using ordinary hypertext links.
overview.html - a single file describing general aspects of your project. (This file doesn't
necessarily reside in your source tree, but it makes sense to place it there, in analogy with the
package-info.java files.)

220

Collected Java Practices

Compare and sort Strings


Comparing and sorting Strings is a bit tricky, and should be done with some care. This is particularly true
when the text is displayed to the end user, or when working with localized text.
There are two fundamentally different ways of comparing strings:
simple Unicode ordering - used by String
localized ordering (the kind expected by an end user) - used by Collator
This causes problems, because:
there are only occasional mismatches between the two styles
it's easy to forget to apply the distinction, when needed
Commonly used String methods such as:
String.equalsIgnoreCase(String)
String.compareTo(String)

can be dangerous to use, depending on the context. The reason is that programmers tend to apply them to
tasks they really aren't meant for, simply out of habit.
The fundamental difference is that localized comparison depends on Locale, while String is largely
ignorant of Locale. Here is a quote from The Java Programming Language by Arnold, Gosling, and
Holmes:
"You should be aware that internationalization and localization issues of full Unicode strings are not
addressed with [String] methods. For example, when you're comparing two strings to determine which is
'greater', characters in strings are compared numerically by their Unicode values, not by their localized
notion of order."
The only robust way of doing localized comparison or sorting of Strings, in the manner expected by an
end user, is to use a Collator , not the methods of the String class.
Example 1 - Unicode Ordering
Here is an example of simple Unicode ordering of Strings. Note the use of
String.CASE_INSENSITIVE_ORDER, an implementation of Comparator.
Reminder - the following items are important with any form of comparison or sorting:
the Comparator and Comparable interfaces
the various sort methods of Collections and Arrays

import java.util.*;
/** Sorting Strings in Unicode order. */
public final class SortStringsNoLocale {
public static void main(String... aArgs){
List<String> insects = Arrays.asList("Wasp", "ant", "", "Bee");
log("Original:");
log(insects);
log("Sorted:");
221

Collected Java Practices

sortList(insects);
log(insects);
log("");
Map<String,String> capitals = new LinkedHashMap<>();
capitals.put("finland", "Helsinki");
capitals.put("United States", "Washington");
capitals.put("Mongolia", "Ulan Bator");
capitals.put("Canada", "Ottawa");
log("Original:");
log(capitals);
log("Sorted:");
log(sortMapByKey(capitals));
}
private static void sortList(List<String> aItems){
Collections.sort(aItems, String.CASE_INSENSITIVE_ORDER);
}
private static void log(Object aObject){
System.out.println(String.valueOf(aObject));
}
private static Map<String, String> sortMapByKey(Map<String, String> aItems){
TreeMap<String, String> result =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
;
result.putAll(aItems);
return result;
}
}

The class outputs the following:


Original:
[Wasp, ant, , Bee]
Sorted:
[, ant, Bee, Wasp]
Original:
{finland=Helsinki, United States=Washington, Mongolia=Ulan Bator, Canada=Ottawa}
Sorted:
{Canada=Ottawa, finland=Helsinki, Mongolia=Ulan Bator, United States=Washington}

Example 2 - Localized Ordering


Here's an example of using a Collator to perform localized sorting and comparison of Strings. Note the
importance of Collator 'strength' for fine-tuning the comparison. To ignore case, for example, either
PRIMARY or SECONDARY strength can be used.
package hirondelle.jp.util;
import java.text.Collator;
import java.util.*;
/**
Use Collator to sort and compare text.
*/
public final class SimpleCollator {
/** Simple harness to exercise the code. */
public static void main (String... aArguments) {
//This data is based on an example in Java Class Libraries,
//by Chan, Lee, and Kramer
List<String> words = Arrays.asList(
"bc", "bc", "bc", "bc", "Abc", "abc", "ABC"
);
log("Different 'Collation Strength' values give different sort results: ");
log(words + " - Original Data");
sort(words, Strength.Primary);
sort(words, Strength.Secondary);
sort(words, Strength.Tertiary);

222

Collected Java Practices

log(EMPTY_LINE);
log("Case kicks in only with Tertiary Collation Strength : ");
List<String> wordsForCase = Arrays.asList("cache", "CACHE", "Cache");
log(wordsForCase + " - Original Data");
sort(wordsForCase, Strength.Primary);
sort(wordsForCase, Strength.Secondary);
sort(wordsForCase, Strength.Tertiary);
log(EMPTY_LINE);
log("Accents kick in with Secondary Collation Strength.");
log("Compare with no accents present: ");
compare("abc", "ABC", Strength.Primary);
compare("abc", "ABC", Strength.Secondary);
compare("abc", "ABC", Strength.Tertiary);
log(EMPTY_LINE);
log("Compare with accents present: ");
compare("abc", "BC", Strength.Primary);
compare("abc", "BC", Strength.Secondary);
compare("abc", "BC", Strength.Tertiary);
}
// PRIVATE //
private static final String EMPTY_LINE = "";
private static final Locale TEST_LOCALE = Locale.FRANCE;
/** Transform some Collator 'int' consts into an equivalent enum. */
private enum Strength {
Primary(Collator.PRIMARY), //base char
Secondary(Collator.SECONDARY), //base char + accent
Tertiary(Collator.TERTIARY), // base char + accent + case
Identical(Collator.IDENTICAL); //base char + accent + case + bits
int getStrength() { return fStrength; }
private int fStrength;
private Strength(int aStrength){
fStrength = aStrength;
}
}
private static void sort(List<String> aWords, Strength aStrength){
Collator collator = Collator.getInstance(TEST_LOCALE);
collator.setStrength(aStrength.getStrength());
Collections.sort(aWords, collator);
log(aWords.toString() + " " + aStrength);
}
private static void compare(String aThis, String aThat, Strength aStrength){
Collator collator = Collator.getInstance(TEST_LOCALE);
collator.setStrength(aStrength.getStrength());
int comparison = collator.compare(aThis, aThat);
if ( comparison == 0 ) {
log("Collator sees them as the same : " + aThis + ", " + aThat + " - " +
aStrength);
}
else {
log("Collator sees them as DIFFERENT : " + aThis + ", " + aThat + " - " +
aStrength);
}
}
private static void log(String aMessage){
System.out.println(aMessage);
}
}

This class outputs the following:


Different 'Collation
[bc, bc, bc, bc,
[bc, bc, bc, bc,
[Abc, abc, ABC, bc,
[abc, Abc, ABC, bc,

Strength'
Abc, abc,
Abc, abc,
bc, bc,
bc, bc,

values give different sort results:


ABC] - Original Data
ABC] Primary
bc] Secondary
bc] Tertiary

Case kicks in only with Tertiary Collation Strength


[cache, CACHE, Cache] - Original Data
[cache, CACHE, Cache] Primary
[cache, CACHE, Cache] Secondary
223

Collected Java Practices

[cache, Cache, CACHE] Tertiary


Accents kick in with Secondary Collation Strength.
Compare with no accents present:
Collator sees them as the same : abc, ABC - Primary
Collator sees them as the same : abc, ABC - Secondary
Collator sees them as DIFFERENT: abc, ABC - Tertiary
Compare with accents present:
Collator sees them as the same : abc, BC - Primary
Collator sees them as DIFFERENT: abc, BC - Secondary
Collator sees them as DIFFERENT: abc, BC - Tertiary

See Also :
Implementing compareTo
Determine if Strings are equal
Choosing the right Collection

Copy an array
There are several ways to copy an array:
use the various copyOf and copyOfRange methods of the Arrays class - probably the simplest
method
use System.arraycopy - useful when copying parts of an array
call its clone method, and do a cast - the simplest style, but only a shallow clone is performed
use a for loop - more than one line, and needs a loop index
Example
This example class demonstrates:
relative performance of the various methods (in many cases the differences in speed will not be of
practical benefit).
how clone is a shallow copy, and leads to independent storage only for primitive, one dimensional
arrays.

import java.util.*;
public final class ArrayCopier {
public static void main (String... aArguments) {
String action = aArguments[0];
int numIterations = 0;
if (aArguments.length == 2) {
numIterations = Integer.parseInt(aArguments[1]);
}
if ("performance".equals(action)) {
demoPerformance(numIterations);
}
else if ("storage".equals(action)) {
demoIndependanceOfStorage();
}
}
/**
224

Collected Java Practices

* Display the time it takes to copy an array in various ways.


*/
private static void demoPerformance(int aNumIterations){
Stopwatch stopwatch = new Stopwatch();
int[] numbers = {1,2,3,4,5,6,7,8,9,10};
stopwatch.start();
copyUsingClone(numbers, aNumIterations);
stopwatch.stop();
log("Using clone: " + stopwatch);
stopwatch.start();
copyUsingArraycopy(numbers, aNumIterations);
stopwatch.stop();
log("Using System.arraycopy: " + stopwatch);
stopwatch.start();
copyUsingArraysCopyOf(numbers, aNumIterations);
stopwatch.stop();
log("Using Arrays.copyOf: " + stopwatch);
stopwatch.start();
copyUsingForLoop(numbers, aNumIterations);
stopwatch.stop();
log("Using for loop: " + stopwatch);
}
private static void copyUsingClone(int[] aArray , int aNumIterations) {
for(int idx = 0 ; idx < aNumIterations; ++idx) {
int[] copy = (int[])aArray.clone();
}
}
private static void copyUsingArraycopy(int[] aArray , int aNumIterations) {
for(int idx = 0 ; idx < aNumIterations; ++idx) {
int [] copy = new int[aArray.length];
System.arraycopy( aArray, 0, copy, 0, aArray.length );
}
}
private static void copyUsingArraysCopyOf( int[] aArray , int aNumIterations) {
for(int idx = 0 ; idx < aNumIterations; ++idx) {
int[] copy = Arrays.copyOf(aArray, aArray.length);
}
}
private static void copyUsingForLoop( int[] aArray , int aNumIterations) {
for(int iterIdx = 0 ; iterIdx < aNumIterations; ++iterIdx) {
int [] copy = new int[aArray.length];
for (int idx = 0; idx < aArray.length; ++idx) {
copy[idx] = aArray[idx];
}
}
}
private static void log(String aMessage){
System.out.println(aMessage);
}
/**
* (The for-loop and System.arraycopy styles clearly have independent
* storage, and are not exercised in this method.)
*/
private static void demoIndependanceOfStorage() {
//a clone of a one-dimensional array has independent storage
int[] numbers = {1,1,1,1};
int[] numbersClone = (int[])numbers.clone();
//set 0th element to 0, and compare
numbersClone[0] = 0;
log("Altered clone has NOT affected original:");
225

Collected Java Practices

log("numbersClone[0]: " + numbersClone[0]);


log("numbers[0]: " + numbers[0]);
//the clone of a multi-dimensional array does *not* have
//independant storage
int[][] matrix = { {1,1}, {1,1} };
int[][] matrixClone = (int[][])matrix.clone();
//set 0-0th element to 0, and compare
matrixClone[0][0] = 0;
log("Altered clone has affected original:");
log("matrixClone element 0-0:" + matrixClone[0][0]);
log("matrix element 0-0: " + matrix[0][0]);
//the clone of an array of objects as well is only shallow
Date[] dates = {new Date()};
log("Original date: " + dates[0]);
Date[] datesClone = (Date[])dates.clone();
datesClone[0].setTime(0);
log("Altered clone has affected original:");
log("datesClone[0]:" + datesClone[0]);
log("dates[0]: " + dates[0]);
}
}

seems to have slightly better performance. Differences between the various styles are
small, however, and would often be regarded as a micro-optimization. (As usual, such judgements
depend on the context).

System.arraycopy

>java -cp . -Xint ArrayCopier performance 250000


Using
Using
Using
Using

clone: 108.168 ms
System.arraycopy: 125.334 ms
Arrays.copyOf: 190.490 ms
for loop: 392.026 ms

The above example use the -Xint option to turn off the Just In Time compiler. Here, bytecodes are
interpreted at runtime, but never compiled by the HotSpot compiler into native code. This provides a
uniform environment for executing tests of relative execution time, since there is no "warm-up" period.
Example run demonstrating independence of storage, or lack thereof:
>java -cp . ArrayCopier storage
Altered clone has NOT affected original:
numbersClone[0]: 0
numbers[0]: 1
Altered clone has affected original:
matrixClone element 0-0:0
matrix element 0-0: 0
Original date: Mon Sep 30 15:47:58 EDT 2002
Altered clone has affected original:
datesClone[0]:Wed Dec 31 19:00:00 EST 1969
dates[0]: Wed Dec 31 19:00:00 EST 1969

See Also :
Time execution speed

Determine if Strings are equal


226

Collected Java Practices

To determine if two String objects match exactly, you should almost always use the equals method, and
not the == operator.
The equals method compares the actual content of the Strings, using the underlying Unicode
representation, while == compares only the identity of the objects, using their address in memory (which
is usually not desired).
Example
/**
* Illustrates the incorrectness of using == for typical
* String comparisons.
*/
public final class EqualStrings {
/**
* Pass in the String "blah" on the command line in
* order to exercise the code.
*/
public static void main(String... arguments) {
String text = (arguments.length == 1 ? arguments[0] : null);
testUsingEquals(text);
testUsingOperator(text);
}
// PRIVATE
private static final String BLAH = "blah";
private static void testUsingEquals(String aString) {
if (BLAH.equals(aString)) {
log("Equals: YES, the user entered \"blah\".");
}
else {
log("Equals: NO, the user did not enter \"blah\".");
}
}
private static void testUsingOperator(String aString) {
if (aString == BLAH) {
log("== operator: YES, the user entered \"blah\".");
}
else {
log("== operator: NO, the user did not enter \"blah\".");
}
}
private static void log(String aMessage){
System.out.println(aMessage);
}
}

An example run of this class:


>java -cp . EqualStrings blah
Equals: YES, the user entered "blah".
== operator: NO, the user did not enter "blah".

Note that x.equals(y) will generate a NullPointerException if x is null , but will not generate such an
exception if only y is null . In the above example, for instance, BLAH.equals(aString) is used instead
of aString.equals(BLAH) , since aString may be null .
It's true that the intern method may be used to allow meaningful comparisons using ==. (See The Java
Programming Language by Arnold, Gosling, and Holmes for further information.) The intern technique
is meant as a performance optimization, and should usually be avoided since it's more complex.

227

Collected Java Practices

Examine bytecode
Examination of the bytecode of a class is not a common task. However, if you're interested, it's possible
to view bytecode using the javap tool included in the JDK. Here's an example of its use, where it shows
the effect on bytecode of setting fields explicitly to their default initial values:
>javap -c -classpath . Quark
Compiled from Quark.java
public final class Quark extends java.lang.Object {
public Quark(java.lang.String,double);
}
Method Quark(java.lang.String,double)
0 aload_0
1 invokespecial #1 <Method java.lang.Object()>
4 aload_0
5 aconst_null
6 putfield #2 <Field java.lang.String fName>
9 aload_0
10 dconst_0
11 putfield #3 <Field double fMass>
14 aload_0
15 aload_1
16 putfield #2 <Field java.lang.String fName>
19 aload_0
20 dload_2
21 putfield #3 <Field double fMass>
24 return

Here is the source class itself :


public final class Quark {
public Quark(String aName, double aMass){
fName = aName;
fMass = aMass;
}
//PRIVATE
//WITHOUT redundant initialization to default values
//private String fName;
//private double fMass;
//WITH redundant initialization to default values
private String fName = null;
private double fMass = 0.0d;
}

Bytecode instructions are defined by the JVM specification.


See Also :
Initializing fields to 0-false-null is redundant

228

Collected Java Practices

Fetch web page and header


Here's an example of programmatically fetching the HTML content of a web page as simple text.
This could be used, for example, to fetch stock prices or the weather forecast from the web - the raw
HTML is first fetched, then the desired content is extracted and presented in some customized manner.
Of course, if the web site publishes a true API, and serves structured data using JSON, XML, or similar,
then that should be used instead of fetching hypertext.
import java.io.*;
import java.net.*;
import java.util.Scanner;
/** Fetches the HTML content of a web page (or HTTP header) as a String. */
public final class WebPageFetcher {
/**
* Demo harness.
*
* <ul>
* <li>aArgs[0] : an HTTP URL
* <li>aArgs[1] : (header | content)
* </ul>
*/
public static void main(String... aArgs) throws MalformedURLException {
String url = aArgs[0];
String option = aArgs[1];
WebPageFetcher fetcher = new WebPageFetcher(url);
if (HEADER.equalsIgnoreCase(option)) {
log(fetcher.getPageHeader());
}
else if (CONTENT.equalsIgnoreCase(option)) {
log(fetcher.getPageContent());
}
else {
log("Unknown option.");
}
}
public WebPageFetcher(URL aURL){
if (! HTTP.equals(aURL.getProtocol())) {
throw new IllegalArgumentException("URL is not for HTTP Protocol: " + aURL);
}
fURL = aURL;
}
public WebPageFetcher(String aUrlName) throws MalformedURLException {
this(new URL(aUrlName));
}
/** Fetch the HTML content of the page as simple text.*/
public String getPageContent() {
String result = null;
URLConnection connection = null;
try {
connection = fURL.openConnection();
Scanner scanner = new Scanner(connection.getInputStream());
scanner.useDelimiter(END_OF_INPUT);
result = scanner.next();
}
catch (IOException ex) {
log("Cannot open connection to " + fURL.toString());
}
return result;
}
/** Fetch HTML headers as simple text. */
public String getPageHeader(){
StringBuilder result = new StringBuilder();
URLConnection connection = null;
229

Collected Java Practices

try {
connection = fURL.openConnection();
}
catch (IOException ex) {
log("Cannot open connection to URL: " + fURL);
}
//not all headers come in key-value pairs - sometimes the key is
//null or an empty String
int headerIdx = 0;
String headerKey = null;
String headerValue = null;
while ( (headerValue = connection.getHeaderField(headerIdx)) != null ) {
headerKey = connection.getHeaderFieldKey(headerIdx);
if (headerKey != null && headerKey.length()>0) {
result.append(headerKey);
result.append(" : ");
}
result.append(headerValue);
result.append(NEWLINE);
headerIdx++;
}
return result.toString();
}
// PRIVATE
private URL fURL;
private
private
private
private
private

static
static
static
static
static

final
final
final
final
final

String
String
String
String
String

HTTP = "http";
HEADER = "header";
CONTENT = "content";
END_OF_INPUT = "\\Z";
NEWLINE = System.getProperty("line.separator");

private static void log(Object aObject){


System.out.println(aObject);
}
}

An example run, fetching an HTTP header from google.com:


>java -cp . WebPageFetcher http://www.google.com/ header
HTTP/1.1 200 OK
Cache-Control : private
Content-Type : text/html; charset=ISO-8859-1
Server : GWS/2.1
Transfer-Encoding : chunked
Date : Wed, 29 Aug 2007 13:21:40 GMT

Generate random numbers


There are two principal means of generating random (really pseudo-random) numbers:
the Random class generates random integers, doubles, longs and so on, in various ranges.
the static method Math.random generates doubles between 0 (inclusive) and 1 (exclusive).
To generate random integers:
do not use Math.random (it produces doubles, not integers)
use the Random class to generate random integers between 0 and N.
To generate a series of random numbers as a unit, you need to use a single Random object - do not create
a new Random object for each new random number.

230

Collected Java Practices

Other alternatives are:


SecureRandom , a cryptographically strong subclass of Random
ThreadLocalRandom , intended for multi-threaded cases

Here are some examples using Random.


Example 1
import java.util.Random;
/** Generate 10 random integers in the range 0..99. */
public final class RandomInteger {
public static final void main(String... aArgs){
log("Generating 10 random integers in range 0..99.");
//note a single Random object is reused here
Random randomGenerator = new Random();
for (int idx = 1; idx <= 10; ++idx){
int randomInt = randomGenerator.nextInt(100);
log("Generated : " + randomInt);
}
log("Done.");
}
private static void log(String aMessage){
System.out.println(aMessage);
}
}

Example run of this class:


Generating 10 random integers in range 0..99.
Generated : 44
Generated : 81
Generated : 69
Generated : 31
Generated : 10
Generated : 64
Generated : 74
Generated : 57
Generated : 56
Generated : 93
Done.

Example 2
This example generates random integers in a specific range.
import java.util.Random;
/** Generate random integers in a certain range. */
public final class RandomRange {
public static final void main(String... aArgs){
log("Generating random integers in the range 1..10.");
int START = 1;
int END = 10;
Random random = new Random();
for (int idx = 1; idx <= 10; ++idx){
showRandomInteger(START, END, random);
}
log("Done.");
}
private static void showRandomInteger(int aStart, int aEnd, Random aRandom){
231

Collected Java Practices

if (aStart > aEnd) {


throw new IllegalArgumentException("Start cannot exceed End.");
}
//get the range, casting to long to avoid overflow problems
long range = (long)aEnd - (long)aStart + 1;
// compute a fraction of the range, 0 <= frac < range
long fraction = (long)(range * aRandom.nextDouble());
int randomNumber = (int)(fraction + aStart);
log("Generated : " + randomNumber);
}
private static void log(String aMessage){
System.out.println(aMessage);
}
}

An example run of this class:


Generating random integers in the range 1..10.
Generated : 9
Generated : 3
Generated : 3
Generated : 9
Generated : 4
Generated : 1
Generated : 3
Generated : 9
Generated : 10
Generated : 10
Done.

Example 3
This example generates random floating point numbers in a Gaussian (normal) distribution.
import java.util.Random;
/**
Generate pseudo-random floating point values, with an
approximately Gaussian (normal) distribution.
Many physical measurements have an approximately Gaussian
distribution; this provides a way of simulating such values.
*/
public final class RandomGaussian {
public static void main(String... aArgs){
RandomGaussian gaussian = new RandomGaussian();
double MEAN = 100.0f;
double VARIANCE = 5.0f;
for (int idx = 1; idx <= 10; ++idx){
log("Generated : " + gaussian.getGaussian(MEAN, VARIANCE));
}
}
private Random fRandom = new Random();
private double getGaussian(double aMean, double aVariance){
return aMean + fRandom.nextGaussian() * aVariance;
}
private static void log(Object aMsg){
System.out.println(String.valueOf(aMsg));
}
}

An example run of this class:


Generated : 99.38221153454624
Generated : 100.95717075067498
232

Collected Java Practices

Generated
Generated
Generated
Generated
Generated
Generated
Generated
Generated

:
:
:
:
:
:
:
:

106.78740794978813
105.57315286730545
97.35077643206589
92.56233774920052
98.29311772993057
102.04954815575822
104.88458607780176
97.11126014402141

Get database connection


There are two sources of database connections - either a DataSource or a DriverManager .
If available, JNDI and the DataSource interface should be used to get a Connection instead of
DriverManager . The JNDI style is typical when using an application server or a web container. (For
example, the popular Tomcat product includes JNDI services and connection pools.)
Always remember that database connections need to be properly released!
Options for specifying the connection parameters include:
server configuration settings (likely the most common style)
direct user input for user name and password
a properties file, web.xml file, or ResourceBundle to keep parameters out of compiled code
the jdbc.drivers property of the System class
Example
Here's a reminder of the basics of getting a Connection .
import java.sql.*;
import javax.naming.*;
import javax.sql.*;
final class GetConnection {
/** Uses JNDI and Datasource (preferred style).
*/
Connection getJNDIConnection(){
String DATASOURCE_CONTEXT = "java:comp/env/jdbc/blah";
Connection result = null;
try {
Context initialContext = new InitialContext();
if ( initialContext == null){
log("JNDI problem. Cannot get InitialContext.");
}
DataSource datasource = (DataSource)initialContext.lookup(DATASOURCE_CONTEXT);
if (datasource != null) {
result = datasource.getConnection();
}
else {
log("Failed to lookup datasource.");
}
}
catch ( NamingException ex ) {
log("Cannot get connection: " + ex);
}
catch(SQLException ex){
log("Cannot get connection: " + ex);
}
return result;
}
233

Collected Java Practices

/** Uses DriverManager. */


Connection getSimpleConnection() {
//See your driver documentation for the proper format of this string :
String DB_CONN_STRING = "jdbc:mysql://localhost:3306/airplanes";
//Provided by your driver documentation. In this case, a MySql driver is used :
String DRIVER_CLASS_NAME = "org.gjt.mm.mysql.Driver";
String USER_NAME = "juliex";
String PASSWORD = "ui893djf";
Connection result = null;
try {
Class.forName(DRIVER_CLASS_NAME).newInstance();
}
catch (Exception ex){
log("Check classpath. Cannot load db driver: " + DRIVER_CLASS_NAME);
}
try {
result = DriverManager.getConnection(DB_CONN_STRING, USER_NAME, PASSWORD);
}
catch (SQLException e){
log( "Driver loaded, but cannot connect to db: " + DB_CONN_STRING);
}
return result;
}
private static void log(Object aObject){
System.out.println(aObject);
}
}

See Also :
Recovering resources
Connection pools

Get size of object in memory


Occasionally, you may be interested in measuring (or estimating) the size of an object in memory. Here's
a utility which can help with that task, if the object's class has a no-argument constructor. It follows the
style used by Java Platform Performance, by Wilson and Kesselman. Given a class name, it will build a
number of objects using the no-argument constructor, and measure the effect on JVM memory use. Thus,
it measures the size of 'empty' objects containing no data.
To measure the size of a particular object containing data, a similar technique can easily be used:
measure JVM memory use before and after building the object.
In addition, JDK 1.5 has added an Instrumentation interface, which includes a method named
getObjectSize method. However, this method seems to be intended for tool makers.
/**
* Measures the approximate size of an object in memory, given a Class which
* has a no-argument constructor.
*/
public final class ObjectSizer {
/**
* First and only argument is the package-qualified name of a class
* which has a no-argument constructor.
*/
234

Collected Java Practices

public static void main(String... aArguments){


Class theClass = null;
try {
theClass = Class.forName(aArguments[0]);
}
catch (Exception ex) {
log("Cannot build a Class object: " + aArguments[0]);
log("Use a package-qualified name, and check classpath.");
}
ObjectSizer sizer = new ObjectSizer();
long size = sizer.getObjectSize(theClass);
log("Approximate size of " + theClass + " objects :" + size);
}
/**
* Return the approximate size in bytes, and return zero if the class
* has no default constructor.
*
* @param aClass refers to a class which has a no-argument constructor.
*/
public long getObjectSize(Class aClass){
long result = 0;
//if the class does not have a no-argument constructor, then
//inform the user and return 0.
try {
aClass.getConstructor(new Class[]{});
}
catch (NoSuchMethodException ex) {
log(aClass + " does not have a no-argument constructor.");
return result;
}
//this array will simply hold a bunch of references, such that
//the objects cannot be garbage-collected
Object[] objects = new Object[fSAMPLE_SIZE];
//build a bunch of identical objects
try {
Object throwAway = aClass.newInstance();
long startMemoryUse = getMemoryUse();
for (int idx=0; idx < objects.length ; ++idx) {
objects[idx] = aClass.newInstance();
}
long endMemoryUse = getMemoryUse();
float approximateSize = (endMemoryUse - startMemoryUse)/100f;
result = Math.round(approximateSize);
}
catch (Exception ex) {
log("Cannot create object using " + aClass);
}
return result;
}
// PRIVATE
private static int fSAMPLE_SIZE = 100;
private static long fSLEEP_INTERVAL = 100;
private static void log(String aMessage){
System.out.println(aMessage);
}
private long getMemoryUse(){
putOutTheGarbage();
long totalMemory = Runtime.getRuntime().totalMemory();
putOutTheGarbage();
long freeMemory = Runtime.getRuntime().freeMemory();
return (totalMemory - freeMemory);
}
private void putOutTheGarbage() {
collectGarbage();
collectGarbage();
}
private void collectGarbage() {
235

Collected Java Practices

try {
System.gc();
Thread.currentThread().sleep(fSLEEP_INTERVAL);
System.runFinalization();
Thread.currentThread().sleep(fSLEEP_INTERVAL);
}
catch (InterruptedException ex){
ex.printStackTrace();
}
}
}

ObjectSizer

can be easily placed in an interactive console application.

An example run gives:


>java -cp . Console ObjectSizeInterpreter
Please enter a class name>java.blah
Invalid. Example:"java.lang.String">java.util.ArrayList
Approximate size of class java.util.ArrayList objects in bytes: 80
Please enter a class name>java.util.Vector
Approximate size of class java.util.Vector objects in bytes: 80
Please enter a class name>java.util.HashMap
Approximate size of class java.util.HashMap objects in bytes: 104
Please enter a class name>java.lang.Long
class java.lang.Long does not have a no-argument constructor.
Please enter a class name>exit
Bye.

Here is the Interpreter class:


import java.util.*;
import java.text.MessageFormat;
/**
* Given a package-qualified class name, return the approximate size of
* the object in bytes.
*/
public final class ObjectSizeInterpreter implements Interpreter {
/**
* @param aLine is a non-null, package-qualified name of a class.
* @param aResult is a non-null, empty List which acts as an "out"
* parameter; when returned, aResult must contain a non-null, non-empty
* List containing a description of the size of the object.
*
* @return true only if the user has requested to quit the Interpreter.
* @exception IllegalArgumentException if a param does not comply.
*/
public boolean parseInput(String aLine, final List<Object> aResult) {
if (aResult == null) {
throw new IllegalArgumentException("Result param cannot be null.");
}
if (!aResult.isEmpty()){
throw new IllegalArgumentException("Result param must be empty.");
}
if (aLine == null) {
throw new IllegalArgumentException("Line must not be null.");
}
boolean hasRequestedQuit =
aLine.trim().equalsIgnoreCase(fQUIT) ||
aLine.trim().equalsIgnoreCase(fEXIT)
;
if (hasRequestedQuit) {
//display only a blank line
236

Collected Java Practices

aResult.add(fNEW_LINE);
}
else {
try {
Class theClass = Class.forName(aLine);
ObjectSizer sizer = new ObjectSizer();
long size = sizer.getObjectSize(theClass);
if (size > 0){
Object[] insertedData = {theClass, new Long(size)};
MessageFormat sizeMessage = new MessageFormat(fPATTERN);
String message = sizeMessage.format(insertedData);
aResult.add(message);
aResult.add(fNEW_LINE);
}
aResult.add(fDEFAULT_PROMPT);
}
catch (ClassNotFoundException ex){
//recover by asking the user for corrected input
aResult.clear();
aResult.add(fERROR_PROMPT);
}
}
if (aResult.isEmpty()) {
throw new IllegalStateException("Result must be non-empty.");
}
return hasRequestedQuit;
}
/**
* Return the text to be displayed upon start-up of the Interpreter.
*/
public String getHelloPrompt() {
return fHELLO_PROMPT;
}
// PRIVATE
private static final String fHELLO_PROMPT = "Please enter a class name>";
private static final String fDEFAULT_PROMPT = "Please enter a class name>";
private static final String fERROR_PROMPT = "Invalid.
Example:\"java.lang.String\">";
private static final String fPATTERN = "Approximate size of {0} objects in bytes:
{1}";
private static final String fQUIT = "quit";
private static final String fEXIT = "exit";
private static final String fNEW_LINE = System.getProperty("line.separator");
}

See Also :
Measure application performance
Console input

Implement code tables


Most applications use code tables. A reasonable definition of a code table might be:
A code table is a list of closely related items, each of which has minimal substructure.
Examples:
a list of countries (or other geographical category)
the list of credit cards accepted by a web site - Mastercard, Visa, etc.
237

Collected Java Practices

the number of items shown to the user in a search result - 10, 50, 100
Code tables are often presented in drop down lists, as in:
Number of Results:

10

Flavor of Ice Cream:

Chocolate

(Radio buttons or other presentation styles may also be appropriate, according to the needs of each case.)
Code Table Structure
Most applications use a relational database. Code tables usually represent the simplest kinds of tables that
you can model in a database. Here are some example code tables, as defined by SQL CREATE TABLE
statements:
Number of items shown in a search result:
CREATE TABLE NumResults (
Id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
NumItems TINYINT UNSIGNED UNIQUE NOT NULL,
PRIMARY KEY (Id)
) TYPE=InnoDB;

List of countries supported by an application:


CREATE TABLE Country (
Id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
Text VARCHAR(50)UNIQUE NOT NULL,
ShortText VARCHAR(2)UNIQUE NOT NULL,
PRIMARY KEY (Id)
) TYPE=InnoDB;

The same list of countries, but with a field added to control sort order:
CREATE TABLE Country (
Id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
OrderWith MEDIUMINT UNIQUE UNSIGNED NOT NULL,
Text VARCHAR(50)UNIQUE NOT NULL,
ShortText VARCHAR(2)UNIQUE NOT NULL,
PRIMARY KEY (Id)
) TYPE=InnoDB;

It's often useful to picture each row in a code table as representing a set of aliases for a single idea. One
alias can be more appropriate than another, according to the context in which it's used. For example, in a
report which tries to squeeze as much information as possible onto pages of fixed width, short
abbreviations are often useful. When presenting a drop-down list to an end user, it may be more desirable
to show a longer description, instead of an abbreviation.
So, you may decide to represent an item in a code table using what amounts to several aliases for the
same item:
a numeric id
the 'regular' text, usually seen by the user
a short abbreviation used when appropriate
some other alias appropriate to a given case
For this idea to make sense, each of the above fields would need a UNIQUE constraint, and each would
need to be non-null.
Code Table Evolution
As shown above, code tables don't have a specific, definitive structure. You aren't locked into a specific
238

Collected Java Practices

style. A single application often has many code tables, but those code tables don't necessarily share the
exact same form. Also, it's not uncommon for a code table to start its life in a simple form, and then later
grow into something more elaborate. In this sense, code tables are roughly similar to Java's enumeration
types, which can start out being very simple. Since enumerations are also classes, you can add more
structure to them later, if needed.
In-Memory Caching
Code tables often represent relatively static data. Since the data doesn't change very often, it usually
makes sense to consider reading in all code tables once upon startup. Then, each time a code table is
needed, the in-memory representations of the code tables are referenced directly, instead of repeatedly
going back to the database. This usually improves application performance.
In-Memory Joins
If code tables are cached in memory after startup, then you will often be able to move logic formerly
implemented by a database JOIN operation into in-memory operations in Java instead. (This is an
exception to the rule of not performing database tasks in code.) There are two advantages to this:
it will likely improve performance.
your SQL statements will be simpler, since the JOIN s can be left out
For illustration, consider an application that models a team sport, in which each player on a team is
assigned a specific position of some sort (bowler, pitcher, fullback, or whatever). In this case, the Team
table has a foreign key into the Position code table. To retrieve the list of players on a team, you might
have an explicit join to the Position code table, as in :
SELECT
Name, Number, PositionName
FROM
Team JOIN Position ON PositionFK = Position.Id
WHERE
Team.Id = ?

Alternatively, the JOIN might be dropped in favor of returning the 'raw' PositionFK identifier, instead of
the PositionName text, as in :
SELECT
Name, Number, PositionFK
FROM
Team
WHERE
Team.Id = ?

Of course, the PositionFK identifier would need to be translated into text (in Java-land) before presenting
the result to the user.
Id For Code, Text For User
The fact that a code table item is essentially a collection of aliases for a single idea can be put to use in
the following way. In Java-land, it's best to identify items using internal, static codes, instead of the text
visible to the user. Forms in web applications are a good example of this:
<select name="Flavor">
<option value='1'>Chocolate</option>
<option value='2'>Strawberry</option>
<option value='3'>Vanilla</option>
</select>

Here, the text ('Chocolate') is shown to the user in a drop-down, but the value submitted to the server is
actually a numeric id ('1'), as controlled by the value attribute of the option tag. This separates two
things nicely. The text may change for many reasons (change the spelling, add translations into another
language), but such changes will not ripple into Java, since Java-land uses numeric codes, not the text.
(Some object to exposing database primary keys to the end user like this. But in this case, it doesn't seem
239

Collected Java Practices

to do any harm.)
Sorting
Sorting of code tables can be tricky. If the sort is alphabetical, then that's simple to implement. However,
sorts aren't always alphabetical. Sometimes they depend on arbitrary rules. For example, if an application
is used only in Australia, a list of countries having Australia and New Zealand at the beginning may be
required. In such cases, one option is to define a column in the underlying code table which explicitly
defines the sort order.
The sort order can also be affected by whether an application is multilingual. If a code table is sorted
alphabetically in each language, then the order of presentation to the user will usually not be the same in
all languages.
Monster Code Tables
Some applications put all of their code tables into a single "monster" code table. The monster code table
has two primary keys -- one to identify the code table, and one to identify the value within the code
table. This seems to be an inferior design:
it lumps together items which aren't logically related to each other. It's almost always a mistake to
put implementation details before meaning. That's a bad sign.
foreign keys to code tables are no longer possible. Since foreign keys are the heart and soul of a
relational database, this is a major drawback.
since some foreign keys are absent, the clarity of a database's structure (and related SQL
statements) is significantly reduced.
when a single code table has special needs, then it usually can't fit into the structure of the monster
code table.
code tables can't evolve independently and still remain in the monster code table.
JOINs to monster code tables are usually doubled. What used to be a single join becomes 2 joins -one join to define the code table, another to define the value within the code table.
in large SQL statements, the large number of joins can rapidly become annoying.

See Also :
Don't perform basic SQL tasks in code

Internationalization
Some applications need to use more than one language in the user interface. Changing a program to
allow for this is called "internationalization", while the actual translation is called "localization".
Translation is usually applied only to user interface elements (menus, labels, and so on), and not to
business data stored in the database.
Most implementations of multilingual applications use ResourceBundle and Locale. However, there are
some problems with this style, so one might consider alternatives to ResourceBundle.
Here's a simple illustration of the basic ResourceBundle mechanism:
BankMachine is a user of a ResourceBundle , whose Locale is set by the end user.
ListResourceBundle , but PropertyResourceBundle is likely used more often.

240

This illustration uses

Collected Java Practices

import java.util.ResourceBundle;
import java.util.Locale;
public final class BankMachine {
/**
* Run first a French version, then an English version.
*/
public static void main(String[] aArguments) {
BankMachine bankMachine = new BankMachine();
bankMachine.selectLanguage(fFRENCH);
bankMachine.showDisplay();
bankMachine.selectLanguage(fENGLISH);
bankMachine.showDisplay();
}
/**
* The user's first action is to select a language.
*/
public void selectLanguage(String aLanguage){
if (aLanguage.equals(fENGLISH)) {
fText = ResourceBundle.getBundle(fBUNDLE_NAME, Locale.ENGLISH);
}
else if (aLanguage.equals(fFRENCH)) {
fText = ResourceBundle.getBundle(fBUNDLE_NAME, Locale.FRENCH);
}
else {
throw new IllegalStateException("Unknown language");
}
}
public void showDisplay() {
//use the bundle to get the proper version of a string
//note that the variable names - Text.Hello, etc - reflect the content,
//so these method calls clearly indicate what is being displayed
//to the user
System.out.println(fText.getString(Text.Hello));
System.out.println(fText.getString(Text.PleaseSelectAction));
}
// PRIVATE
private static String fENGLISH = Text.English;
private static String fFRENCH = Text.French;
private static String fBUNDLE_NAME = "Text";
/**
* Default is English.
* Note that this default is perfectly suitable for any initial user
* selection of language, since the Strings representing the languages
* themselves have only one representation, which are defined in the
* default bundle.
*/
private ResourceBundle fText = ResourceBundle.getBundle(fBUNDLE_NAME,
Locale.ENGLISH);
}

An example run of BankMachine gives:


Bonjour
Veuillez choisir une action
Hello
Please select an action

The default ResourceBundle contains both constants and the default representations of all text:
import java.util.*;
public final class Text extends ListResourceBundle {
public final Object[][] getContents() {
return fContents;
241

Collected Java Practices

}
/**
* These constants are used by the caller to identify what text is
* needed; which version of the text is actually returned is determined
* by the ResourceBundle mechanism.
*
* Using variable names which reflect the content clarifies the
* intent of the code in the user of this class.
*/
public static final String Hello = "Hello";
public static final String PleaseSelectAction = "PleaseSelectAction";
/**
* Language names presented as a list of choices to the user
* are special, since they are always presented in the native language.
* Thus, this default bundle defines the
* unique representations of all language names, and no other
* bundle provides a translation for them.
*/
public static final String English = "English";
public static final String French = "Francais";
// PRIVATE
private static final Object[][] fContents = {
{Text.Hello, "Hello"},
{Text.PleaseSelectAction, "Please select an action"} ,
{Text.English, "English"}, //never defined elsewhere
{Text.French, "Francais"}, //never defined elsewhere
};
}

The other ResourceBundle classes do not define constants, just the translations :
import java.util.*;
public final class Text_fr extends ListResourceBundle {
public final Object[][] getContents() {
return fContents;
}
//No constants are defined here
// PRIVATE
private static final Object[][] fContents = {
{Text.Hello, "Bonjour"},
{Text.PleaseSelectAction, "Veuillez choisir une action"}
};
}

See Also :
Try alternatives to ResourceBundle

Logging messages
Java's logging facility (see Oracle's overview and API) has two parts: a configuration file, and an API for
using logging services. It is a good tool, and is perfectly fine for simple and moderate logging needs. (For
complex logging needs, you might consider the log4j tool as an alternative.)
242

Collected Java Practices

Log entries can be sent to these destinations, as either simple text or as XML:
the console
a file
a stream
memory
a TCP socket on a remote host
The Level class defines seven levels of logging enlightenment:
FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE
ALL and OFF are defined values as well

Here is one style of using these Level s in code, which may be modified as desired:
upon startup, use CONFIG to log configuration parameters
during normal operation, use INFO to log high-level "heartbeat" information
when bugs or critical conditions occur, use SEVERE
debugging information might default to FINE , with FINER and FINEST used occasionally, according
to taste.
There is flexibility in how logging levels can be changed at runtime, without the need for a restart:
simply change the configuration file and call LogManager.readConfiguration .
or, change the level in the body of your code, using the logging API ; for example, one might
automatically increase the logging level in response to unexpected events
Levels are attached to these items :
an originating logging request (from a single line of code)
a Logger (usually attached to the package containing the above line of code)
a Handler (attached to an application)
The flow of execution for a particular logging request usually proceeds as follows:
logging request of some level is made to logger attached to current package
if the request level is too low for that package's logger {
discard it
}
otherwise {
cycle through all handlers {
if the request level is too low for that handler {
discard it
}
otherwise {
log the request
}
}
}

Here is an example of a logging configuration file:


# Properties file which configures the operation of the JDK
# logging facility.
# The system will look for this config file, first using
243

Collected Java Practices

#
#
#
#
#
#
#
#

a System property specified at startup:


>java -Djava.util.logging.config.file=myLoggingConfigFilePath
If this property is not specified, then the config file is
retrieved from its default location at:
JDK_HOME/jre/lib/logging.properties

# Global logging properties.


# -----------------------------------------# The set of handlers to be loaded upon startup.
# Comma-separated list of class names.
# (? LogManager docs say no comma here, but JDK example has comma.)
handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# Loggers and Handlers may override this level
.level=INFO
# Loggers
# -----------------------------------------# Loggers are usually attached to packages.
# Here, the level for each package is specified.
# The global level is used by default, so levels
# specified here simply act as an override.
myapp.ui.level=ALL
myapp.business.level=CONFIG
myapp.data.level=SEVERE
# Handlers
# ----------------------------------------# --- ConsoleHandler --# Override of global logging level
java.util.logging.ConsoleHandler.level=SEVERE
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
# --- FileHandler --# Override of global logging level
java.util.logging.FileHandler.level=ALL
# Naming style for the output file:
# (The output file is placed in the directory
# defined by the "user.home" System property.)
java.util.logging.FileHandler.pattern=%h/java%u.log
# Limiting size of output file in bytes:
java.util.logging.FileHandler.limit=50000
# Number of output files to cycle through, by appending an
# integer to the base file name:
java.util.logging.FileHandler.count=1
# Style of output (Simple or XML):
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter

An application should likely centralize the naming policy for Loggers, since switching naming styles
becomes a simple edit in one method, instead of a large number of edits spread throughout the
244

Collected Java Practices

application. Reasons to alter the naming policy might include :


in a shared environment, adding an application name to the Logger name can help distinguish one
application's entries from others
adding application version information might be beneficial for debugging
There's a common complaint with the JDK logging config file: a Handler cannot be restricted to a
specific Logger. That is, Handlers defined in the config file are global, in the sense that they will be
attached to all Loggers. This is particularly annoying in a server environment, since different applications
will, by default, output logging statements into the same file. This behavior can be easily changed, but
only by using the JDK logging API. Many would prefer to do this directly in the config file.
Here's an example of using the logging API:
package myapp.business;
import java.util.logging.*;
/**
* Demonstrate Java's logging facilities, in conjunction
* with a logging config file.
*/
public final class SimpleLogger {
public static void main(String... args) {
SimpleLogger thing = new SimpleLogger();
thing.doSomething();
}
public void doSomething() {
//Log messages, one for each level
//The actual logging output depends on the configured
//level for this package. Calls to "inapplicable"
//messages are inexpensive.
fLogger.finest("this is finest");
fLogger.finer("this is finer");
fLogger.fine("this is fine");
fLogger.config("this is config");
fLogger.info("this is info");
fLogger.warning("this is a warning");
fLogger.severe("this is severe");
//In the above style, the name of the class and
//method which has generated a message is placed
//in the output on a best-efforts basis only.
//To ensure that this information is always
//included, use the following "precise log"
//style instead :
fLogger.logp(Level.INFO, this.getClass().toString(), "doSomething", "blah");
//For the very common task of logging exceptions, there is a
//method which takes a Throwable :
Throwable ex = new IllegalArgumentException("Some exception text");
fLogger.log(Level.SEVERE, "Some message", ex);
//There are convenience methods for exiting and
//entering a method, which are at Level.FINER :
fLogger.exiting(this.getClass().toString(), "doSomething");
//Display user.home directory, if desired.
//(This is the directory where the log files are generated.)
//System.out.println("user.home dir: " + System.getProperty("user.home") );
}
// PRIVATE
//This style has no hard-coded literals, and requires the logger
//to be non-static.
private final Logger
fLogger=Logger.getLogger(this.getClass().getPackage().getName());
//This style lets the logger be static, but hard-codes a class literal.
//private static final Logger fLogger =
245

Collected Java Practices

// Logger.getLogger(SimpleLogger.class.getPackage().getName())
//;
//This style uses a hard-coded literal and should likely be avoided:
//private static final Logger fLogger = Logger.getLogger("myapp.business");
}

Example Run 1 , using the config file defined above:


>java -Djava.util.logging.config.file=C:\Temp\logging.properties
-cp . myapp.business.SimpleLogger
ConsoleHandler

is configured to show only SEVERE messages :

Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething


SEVERE: this is severe
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
at myapp.business.SimpleLogger.main(SimpleLogger.java:13)

While the FileHandler shows ALL that is sent to it :


Jan 8, 2003 10:43:46 AM myapp.business.SimpleLogger doSomething
CONFIG: this is config
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
INFO: this is info
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
WARNING: this is a warning
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:43:47 AM class myapp.business.SimpleLogger doSomething
INFO: blah
Jan 8, 2003 10:43:47 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
java.lang.IllegalArgumentException: Some exception text
at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
at myapp.business.SimpleLogger.main(SimpleLogger.java:13)

Example Run 2, showing the ConsoleHandler output, using the logging.properties file which ships
with the JDK, and which logs only INFO level and above:
>java -cp . myapp.business.SimpleLogger
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
INFO: this is info
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
WARNING: this is a warning
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
SEVERE: this is severe
Jan 8, 2003 10:52:31 AM class myapp.business.SimpleLogger doSomething
INFO: blah
Jan 8, 2003 10:52:31 AM myapp.business.SimpleLogger doSomething
SEVERE: Some message
246

Collected Java Practices

java.lang.IllegalArgumentException: Some exception text


at myapp.business.SimpleLogger.doSomething(SimpleLogger.java:39)
at myapp.business.SimpleLogger.main(SimpleLogger.java:13)

Measure application performance


Measure your application's size in memory and its execution speed.
Measuring performance throughout construction can help to identify major performance problems as
quickly as possible, which is almost always beneficial. If a major problem becomes apparent, then some
immediate redesign will likely be necessary. If no major problem is apparent, then, if needed, minor
optimizations can be performed later, when construction is nearly complete. (Many would argue that
worrying about minor optimizations during construction is not advisable, since optimizations can
sometimes make code more obscure.)
The excellent JConsole tool comes with JSE 6, and can be used to manage and monitor a local or remote
Java Virtual Machine.
The Visual VM tool is also available with modern JDK's. It's full-featured, and very helpful for finding
memory leaks. Another tool that now comes with the JDK is Java Mission Control.
If the above tools aren't available, then you are stuck with more primitive means of measuring an
application's size in memory. They depend on your platform. For example:
NT: Task Manager, look for java.exe or javaw.exe
Unix: get the process id "ps -e | grep java ", pass it to pmap , as in "pmap -x 6598 ", and look
for total KB entry in the Private column
As well, the JDK itself comes with basic profiling tools. For example:
Runtime.freeMemory()
Runtime.totalMemory()
java -Xprof MyClass
java -Xrunhprof:[options] MyClass

Other tools include vmstat for Unix hosts, and perfmon for Windows hosts.
See Also :
Get size of object in memory
Time execution speed
Measure web app performance

Modernize old code


Java has been around since 1995. There is a large amount of code that has been written for older versions
of the JDK. Such code will still run, but there are many advantages to modernizing such code, when you
get the chance, to take advantage of more modern libraries and language features.
In particular, the Java 5 release (JDK 1.5) marked a significant change, and noticeably altered (and
247

Collected Java Practices

improved) the character of typical Java code. When updating old code to more recent versions of Java,
it's helpful to use a quick checklist of things to look out for. Here's a list of such items related to the Java
5 release:
Use @Override liberally
The @Override standard annotation identifies methods that override a superclass method. It should be
used liberally to indicate your intent to override. It's also used when implementing methods defined by an
interface.
Avoid raw types
Raw types should almost always be avoided in favor of parameterized types.
Use for-each loops
The enhanced for loop (also called the for-each loop) should be used whenever available. It's more
compact, concise, and clear.
Replace constants with enumerations
Using public static final constants to represent sets of related items should be avoided in favor of
the enumerations now supported by Java. In addition, you should consider replacing any "roll-your-own"
implementations of type-safe enumerations with the new language construct.
Replace StringBuffer with StringBuilder
The older StringBuffer class is thread-safe, while the new StringBuilder is not. However, it's almost
always used in a context in which thread safety is superfluous. Hence, the extra cost of synchronization is
paid without benefit. Although the performance improvement in moving from StringBuffer to
StringBuilder may only be very slight (or perhaps not even measurable), it's generally considered better
form to prefer StringBuilder .
Use sequence parameters when appropriate
Sequence parameters (varargs) let you replace Object[] parameters (containing 0..N items) appearing at
the end of a parameter list with an alternate form more convenient for the caller. For example,
public static void main(String[] aArgs){}

can now be replaced with:


public static void main(String... aArgs){}

Be careful with Comparable


The Comparable interface has been made generic. For example,
class Anatomy implements Comparable{
public int compareTo(Object aThat){}
}

should now be replaced with :


class Anatomy implements Comparable<Anatomy>{
public int compareTo(Anatomy aThat){}
}

In addition, JDK 7 also has some new tools which improve the character of typical Java code:
Use 'diamond' syntax for generic declarations
Declarations of the form:
List<String> blah = new ArrayList<String>();
Map<String, String> blah = new LinkedHashMap<String, String>();

248

Collected Java Practices

can now be replaced with a more concise syntax:


List<String> blah = new ArrayList<>();
Map<String, String> blah = new LinkedHashMap<>();

Use try-with-resources
The try-with-resources feature lets you eliminate most finally blocks in your code.
Use Path and Files for basic input/output
The new java.nio package is a large improvement over the older File API.
Example
Here's an example of code written using Java 5 features:
import java.util.*;
public final class Office {
/**
* Use sequence parameter (varargs) for main method.
*
* Use a sequence parameter whenever array parameter appears at
* the END of the parameter list, and represents 0..N items.
*/
public static void main(String... aArgs){
//Use parameterized type 'List<String>', not the raw type 'List'
List<String> employees = Arrays.asList("Tom", "Fiorella", "Pedro");
Office office = new Office(AirConditioning.OFF, employees);
System.out.println(office);
//prefer the for-each style of loop
for(String workingStiff: employees){
System.out.println(workingStiff);
}
}
/**
* Preferred : use enumerations, not int or String constants.
*/
enum AirConditioning {OFF, LOW, MEDIUM, HIGH}
/*
* Definitely NOT the preferred style :
*/
public static final int OFF = 1;
public static final int LOW = 2;
public static final int MEDIUM = 3;
public static final int HIGH = 4;
Office(AirConditioning aAirConditioning, List<String> aEmployees){
fAirConditioning = aAirConditioning;
fEmployees = aEmployees; //(no defensive copy here)
}
AirConditioning getAirConditioning(){
return fAirConditioning;
}
List<String> getEmployees(){
return fEmployees;
}
/*
* Get used to typing @Override for toString, equals, and hashCode :
*/
@Override public String toString(){
//..elided
}
@Override public boolean equals(Object aThat){
//..elided
}
249

Collected Java Practices

@Override public int hashCode(){


//..elided
}
// PRIVATE //
private final List<String> fEmployees;
private final AirConditioning fAirConditioning;
}

See Also :
Type-Safe Enumerations
Implementing compareTo
Know the core libraries
Overloading can be tricky
Use for-each liberally
Use @Override liberally
Avoid raw types
Prefer modern libraries for concurrency

Open file in native directory


A directory containing Java .class files can also contain any other kind of file as well. If a text file or a
binary file is only used by a single package, then the most natural home for it is often the very same
directory/package that uses it. This will tend to increase modularization and coherence. (See the packageby-feature topic as well.)
Such files might be used for:
configuration information
splash screen images or icons
SQL statements
documentation or help files
At runtime, the most convenient way of accessing such files is with these methods:
Class.getResoureAsStream
Class.getResource

These methods use the current classloader in order to find files, so they have the most flexibility. The
following example reads in a text file placed in the same directory as the running class. Note that the
code references the file in the simplest possible way, using its simple file name, without any
qualification. As long as the text file retains the same name, and is in the same directory as the class, this
code will work.
import
import
import
import
import
import
import
import

java.io.BufferedReader;
java.io.IOException;
java.io.InputStream;
java.io.InputStreamReader;
java.net.URI;
java.net.URISyntaxException;
java.net.URL;
java.nio.charset.Charset;
250

Collected Java Practices

import
import
import
import
import
import

java.nio.charset.StandardCharsets;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;
java.util.List;
java.util.Scanner;

/** Read a text file that's in the same directory as this (.class) file. */
public final class ReadNeighbouringTextFile {
public static void main(String... aArgs) throws IOException {
log("Classpath: " + System.getProperty("java.class.path"));
log("user.dir: " + System.getProperty("user.dir"));
ReadNeighbouringTextFile read = new ReadNeighbouringTextFile();
read.readFileViaStream();
read.readFileasViaUrl();
//read.readFileViaPath();
}
void readFileViaStream() throws IOException {
log("Via stream...");
try (
//uses the class loader search mechanism:
InputStream input = this.getClass().getResourceAsStream("test.txt");
InputStreamReader isr = new InputStreamReader(input, ENCODING);
BufferedReader reader = new BufferedReader(isr);
){
String line = null;
while ((line = reader.readLine()) != null) {
//process the line in some way
log(line);
}
}
}
void readFileasViaUrl() throws IOException{
log("Via URL...");
//uses the class loader search mechanism:
URL url = this.getClass().getResource("test.txt");
URI uri = null;
try {
uri = url.toURI();
}
catch(URISyntaxException ex){
//in practice this will be very rare
ex.printStackTrace();
}
Path path = Paths.get(uri);
//now that you have the path, it's just regular text file processing
//this gets the whole content at once:
List<String> lines = Files.readAllLines(path, ENCODING);
log("Number of lines in the file: " + lines.size());
//OR, use this style, to process each line one at a time
try (Scanner scanner = new Scanner(path, ENCODING.name())){
while (scanner.hasNextLine()){
//process each line in some way
log(scanner.nextLine());
}
}
}
/**
Here, relative Path objects don't know about the file system in the same way that a

classloader does. It only knows about the 'user.dir' directory, the base
directory of the runtime; this style is much less flexible, and is not
recommended.
*/
void readFileViaPath() throws IOException{
log("Via path (not recommended)...");
//Succeeds: absolute reference
//Path path = Paths.get("C:\\myproj\\test-api\\bin\\test.txt");
/*
* Relative reference.
*
* Fails when the file is beside the .class file.
251

Collected Java Practices

* Succeeds only when the test.txt file is in the 'user.dir' directory.


*
* This means that relative paths don't use the classpath; they
* only use the 'user.dir' System property.
*/
Path path = Paths.get("test.txt");
log("Path: " + path);
log("Absolute: " + path.isAbsolute());
List<String> lines = Files.readAllLines(path, ENCODING);
log("Number of lines in the file: " + lines.size());
}
private final static Charset ENCODING = StandardCharsets.UTF_8;
private static void log(Object aMsg){
System.out.println(String.valueOf(aMsg));
}
}

See Also :
Reading and writing text files
Package by feature, not layer

Parse text
There are various ways of parsing text. The usual tools are:
String.split methods
StringTokenizer and StreamTokenizer classes
Scanner class
Pattern and Matcher classes, which implement regular expressions
for the most complex parsing tasks, you can use tools such as JavaCC
Example 1
This example uses Scanner. Here, the contents of a file containing name-value pairs is read, and each
line is parsed into its constituent data.
import
import
import
import
import
import

java.io.IOException;
java.nio.charset.Charset;
java.nio.charset.StandardCharsets;
java.nio.file.Path;
java.nio.file.Paths;
java.util.Scanner;

/** Assumes UTF-8 encoding. JDK 7+. */


public class ReadWithScanner {
public static void main(String... aArgs) throws IOException {
ReadWithScanner parser = new ReadWithScanner("C:\\Temp\\test.txt");
parser.processLineByLine();
log("Done.");
}
/**
Constructor.
@param aFileName full name of an existing, readable file.
*/
public ReadWithScanner(String aFileName){
fFilePath = Paths.get(aFileName);
}
252

Collected Java Practices

/** Template method that calls {@link #processLine(String)}. */


public final void processLineByLine() throws IOException {
try (Scanner scanner = new Scanner(fFilePath, ENCODING.name())){
while (scanner.hasNextLine()){
processLine(scanner.nextLine());
}
}
}
/**
Overridable method for processing lines in different ways.
<P>This simple default implementation expects simple name-value pairs, separated by
an
'=' sign. Examples of valid input:
<tt>height = 167cm</tt>
<tt>mass = 65kg</tt>
<tt>disposition = "grumpy"</tt>
<tt>this is the name = this is the value</tt>
*/
protected void processLine(String aLine){
//use a second Scanner to parse the content of each line
Scanner scanner = new Scanner(aLine);
scanner.useDelimiter("=");
if (scanner.hasNext()){
//assumes the line has a certain structure
String name = scanner.next();
String value = scanner.next();
log("Name is : " + quote(name.trim()) + ", and Value is : " +
quote(value.trim()));
}
else {
log("Empty or invalid line. Unable to process.");
}
}
// PRIVATE
private final Path fFilePath;
private final static Charset ENCODING = StandardCharsets.UTF_8;
private static void log(Object aObject){
System.out.println(String.valueOf(aObject));
}
private String quote(String aText){
String QUOTE = "'";
return QUOTE + aText + QUOTE;
}
}

For a file containing:


height = 167cm
mass = 65kg
disposition = "grumpy"
this is the name = this is the value

the output of the above class is:


Name is
Name is
Name is
Name is
Done.

:
:
:
:

'height', and Value is : '167cm'


'mass', and Value is : '65kg'
'disposition', and Value is : '"grumpy"'
'this is the name', and Value is : 'this is the value'

Example 2
This example uses StringTokenizer . This class is used to parse the text entered into a search box on a
web page. It returns a Set of tokens to be used for pattern matching. Here, any text appearing in quotes is
treated as a single search token. All other text is split into tokens based simply on whitespace.
253

Collected Java Practices

An example run:
>java -cp . SearchBoxParser
[mars, sun, milky way, venus]
import java.util.*;
/**
* The user enters text into a search box. This class is used
* to parse that text into specific search terms (or tokens).
* It eliminates common words, and allows for the quoting of text, using
* double quotes.
* JDK 7+.
*/
public final class SearchBoxParser {
public static void main(String... aArguments) {
SearchBoxParser parser = new SearchBoxParser("mars venus \"milky way\" sun");
Set<String> tokens = parser.parseSearchText();
//display the tokens
System.out.println(tokens);
}
/**
* @param aSearchText is non-null, but may have no content,
* and represents what the user has input in a search box.
*/
public SearchBoxParser(String aSearchText) {
if (aSearchText == null) {
throw new IllegalArgumentException("Search Text cannot be null.");
}
fSearchText = aSearchText;
}
/**
* Parse the user's search box input into a Set of String tokens.
*
* @return Set of Strings, one for each word in fSearchText; here "word"
* is defined as either a lone word surrounded by whitespace, or as a series
* of words surrounded by double quotes, "like this"; also, very common
* words (and, the, etc.) do not qualify as possible search targets.
*/
public Set<String> parseSearchText() {
Set<String> result = new LinkedHashSet<>();
boolean returnTokens = true;
String currentDelims = fWHITESPACE_AND_QUOTES;
StringTokenizer parser = new StringTokenizer(
fSearchText, currentDelims, returnTokens
);
String token = null;
while (parser.hasMoreTokens()) {
token = parser.nextToken(currentDelims);
if (!isDoubleQuote(token)){
addNonTrivialWordToResult(token, result);
}
else {
currentDelims = flipDelimiters(currentDelims);
}
}
return result;
}
// PRIVATE
private String fSearchText;
private static final Set<String> fCOMMON_WORDS = new LinkedHashSet<>();
private static final String fDOUBLE_QUOTE = "\"";
//the parser flips between these two sets of delimiters
private static final String fWHITESPACE_AND_QUOTES = " \t\r\n\"";
private static final String fQUOTES_ONLY ="\"";
/**Very common words to be excluded from searches.*/
static {
fCOMMON_WORDS.add("a");
254

Collected Java Practices

fCOMMON_WORDS.add("and");
fCOMMON_WORDS.add("be");
fCOMMON_WORDS.add("for");
fCOMMON_WORDS.add("from");
fCOMMON_WORDS.add("has");
fCOMMON_WORDS.add("i");
fCOMMON_WORDS.add("in");
fCOMMON_WORDS.add("is");
fCOMMON_WORDS.add("it");
fCOMMON_WORDS.add("of");
fCOMMON_WORDS.add("on");
fCOMMON_WORDS.add("to");
fCOMMON_WORDS.add("the");
}
/**
* Use to determine if a particular word entered in the
* search box should be discarded from the search.
*/
private boolean isCommonWord(String aSearchTokenCandidate){
return fCOMMON_WORDS.contains(aSearchTokenCandidate);
}
private boolean textHasContent(String aText){
return (aText != null) && (!aText.trim().equals(""));
}
private void addNonTrivialWordToResult(String aToken, Set<String> aResult){
if (textHasContent(aToken) && !isCommonWord(aToken.trim())) {
aResult.add(aToken.trim());
}
}
private boolean isDoubleQuote(String aToken){
return aToken.equals(fDOUBLE_QUOTE);
}
private String flipDelimiters(String aCurrentDelims){
String result = null;
if (aCurrentDelims.equals(fWHITESPACE_AND_QUOTES)){
result = fQUOTES_ONLY;
}
else {
result = fWHITESPACE_AND_QUOTES;
}
return result;
}
}

Example 3
This example demonstrates use of regular expressions, by parsing a fully-qualified type name into two
parts - the package and the "simple" type name.
import java.util.regex.*;
public final class RegularExpressions {
/**
* The pattern is matched to the first argument.
*/
public static void main (String... aArguments) {
matchParts(aArguments[0]);
matchAll(aArguments[0]);
}
/**
* The Matcher.find method attempts to match *parts* of the input
* to the given pattern.
*/
private static void matchParts(String aText){
log(fNEW_LINE + "Match PARTS:");
//note the necessity of the comments flag, since our regular
//expression contains comments:
255

Collected Java Practices

Pattern pattern = Pattern.compile(fREGEXP, Pattern.COMMENTS);


Matcher matcher = pattern.matcher(aText);
while (matcher.find()) {
log("Num groups: " + matcher.groupCount());
log("Package: " + matcher.group(1));
log("Class: " + matcher.group(2));
}
}
/**
* The Matcher.matches method attempts to match the *entire*
* input to the given pattern all at once.
*/
private static void matchAll(String aText){
log(fNEW_LINE + "Match ALL:");
Pattern pattern = Pattern.compile(fREGEXP, Pattern.COMMENTS);
Matcher matcher = pattern.matcher(aText);
if(matcher.matches()) {
log("Num groups: " + matcher.groupCount());
log("Package: " + matcher.group(1));
log("Class: " + matcher.group(2));
}
else {
log("Input does not match pattern.");
}
}
//PRIVATE
private static final String fNEW_LINE = System.getProperty("line.separator");
private static void log(String aMessage){
log(aMessage);
}
/**
* A commented regular expression for fully-qualified type names which
* follow the common naming conventions, for example, "com.myappBlah.Thing".
*
* Thus, the "dot + capital letter" is sufficient to define where the
* package names end.
*
* This regular expression uses two groups, one for the package, and one
* for the class. Groups are defined by parentheses. Note that ?: will
* define a group as "non-contributing"; that is, it will not contribute
* to the return values of the <tt>group</tt> method.
*
* As you can see, regular expressions are often cryptic.
*/
private static final String fREGEXP =
"#Group1 - Package prefix without last dot: " + fNEW_LINE +
"( (?:\\w|\\.)+ ) \\." + fNEW_LINE +
"#Group2 - Class name starts with uppercase: " + fNEW_LINE +
"( [A-Z](?:\\w)+ )"
;
}

Some example runs:


>java -cp . RegularExpressions "java.java.Thing java.lang.Random"
Match PARTS:
Num groups: 2
Package: java.java
Class: Thing
Num groups: 2
Package: java.lang
Class: Random
Match ALL:
Input does not match pattern.

256

Collected Java Practices

>java -cp . RegularExpressions "java.java.Thing"


Match PARTS:
Num groups: 2
Package: java.java
Class: Thing
Match ALL:
Num groups: 2
Package: java.java
Class: Thing
>java -cp . RegularExpressions "java.java.Thing "
Match PARTS:
Num groups: 2
Package: java.java
Class: Thing
Match ALL:
Input does not match pattern.

See Also :
Reading and writing text files
Pattern-match lines of a file
Compile regular expressions once

Pattern-match lines of a file


Here's an example of grep -like treatment of the contents of a text file.
In this example, the format of each line is verified against a regular expression, using a Pattern and
Matcher .
Note that a String literal representing a regular expression needs to escape all backslashes. In effect, all
of the backslashes are simply doubled.
Example
import
import
import
import
import
import
import
import
import
import

java.io.BufferedReader;
java.io.IOException;
java.io.LineNumberReader;
java.nio.charset.Charset;
java.nio.charset.StandardCharsets;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;
java.util.regex.Matcher;
java.util.regex.Pattern;

/** JDK 7+. */


public final class LineMatcher {
/**
* Verifies that each line of a text file starts with:
*
\N + tab + integer + tab + text
* where "text" contains word characters (\w).
*
257

Collected Java Practices

* The corresponding regular expression is:


*
"^\\N\t(\d)+\t(\w)+"
*
* The String passed to Pattern needs to double every backslash:
*
"^\\\\N\\t(\\d)+\\t(\\w)+"
*
* If a line is not of the expected pattern, then an
* IllegalStateException is thrown.
*/
public void findBadLines(String aFileName) {
//Pattern and Matcher are used here, not String.matches(regexp),
//since String.matches(regexp) would repeatedly compile the same
//regular expression
Pattern regexp = Pattern.compile("^\\\\N\\t(\\d)+\\t(\\w)+");
Matcher matcher = regexp.matcher("");
Path path = Paths.get(aFileName);
try (
BufferedReader reader = Files.newBufferedReader(path, ENCODING);
LineNumberReader lineReader = new LineNumberReader(reader);
){
String line = null;
while ((line = lineReader.readLine()) != null) {
matcher.reset(line); //reset the input
if (!matcher.find()) {
String msg = "Line " + lineReader.getLineNumber() + " is bad: " + line;
throw new IllegalStateException(msg);
}
}
}
catch (IOException ex){
ex.printStackTrace();
}
}
final static Charset ENCODING = StandardCharsets.UTF_8;
/** Test harness. */
public static void main(String... arguments) {
LineMatcher lineMatcher = new LineMatcher();
lineMatcher.findBadLines("C:\\Temp\\RegexpTest.txt");
System.out.println("Done.");
}
}

See Also :
Reading and writing text files

Read jar version information


A jar file is simply a zipped (compressed) collection of class files, and any associated resources.
It's often important to know which version of a jar file is being used by an application. For example,
upon startup, an application might find it useful to log the version numbers of all of its known jars. This
can be very useful for debugging problems later on.
The MANIFEST file is a text file included with each jar, which specifies high-level information about its
contents. The MANIFEST file usually includes version information. That version information is, in turn,
accessible at run time using methods of the Package class.
Example
258

Collected Java Practices

The following example extracts the name and version of the package containing the class
InternetAddress . (If the javax.mail.internet package is not on your class path, you can easily modify
this code to examine some other package.)
import javax.mail.internet.InternetAddress;
/**
Display package name and version information for
javax.mail.internet.
As long as you can build on object of some known class,
you may get its related package info in this way.
*/
public final class ReadVersion {
public static void main(String... aArgs){
ReadVersion readVersion = new ReadVersion();
readVersion.readVersionInfoInManifest();
}
public void readVersionInfoInManifest(){
//build an object whose class is in the target jar
InternetAddress object = new InternetAddress();
//navigate from its class object to a package object
Package objPackage = object.getClass().getPackage();
//examine the package object
String name = objPackage.getSpecificationTitle();
String version = objPackage.getSpecificationVersion();
//some jars may use 'Implementation Version' entries in the manifest instead
System.out.println("Package name: " + name);
System.out.println("Package version: " + version);
}
}

Example output of this class:


Package name: JavaMail(TM) API Design Specification
Package version: 1.3

Recovering resources
Expensive resources should be reclaimed as soon as possible, by an explict call to a clean-up method
defined for this purpose. If this is not done, then system performance can degrade. In the worst cases, the
system can even fail entirely.
Resources include:
input-output streams
database result sets, statements, and connections
threads
graphic resources
sockets
Resources which are created locally within a method must be cleaned up within the same method, by
calling a method appropriate to the resource itself, such as close or dispose. (The exact name of the
method is arbitrary, but it usually has those conventional names.) This is usually done automatically,
using the try-with-resources feature, added in JDK 7.
259

Collected Java Practices

If try-with-resources isn't available, then you need to clean up resources explicitly, by calling a clean-up
method in a finally clause.
For the case of a resource which is a field, however, there's more work to do:
implement a clean-up method which the user must call when finished with the object, with a name
such as close or dispose
the caller should be able to query an object to see if its clean-up method has been executed
non- private methods (other than the clean-up method itself) should throw an
IllegalStateException if the clean-up method has already been invoked
as a safety net, implement finalize to call the clean-up method as well; if the user of the class
neglects to call the clean-up method, then this may allow recovery of the resource by the system
never rely solely on finalize
This example shows a class which retains a database connection during its lifetime. (This example is
artificial. Actually writing such a class would not seem necessary in practice, since connection pools
already perform such clean-up in the background. It's used merely to demonstrate the ideas mentioned
above.)
import java.sql.*;
import java.text.*;
import java.util.*;
/**
* This class has an enforced life cycle: after destroy is
* called, no useful method can be called on this object
* without throwing an IllegalStateException.
*/
public final class DbConnection {
public DbConnection () {
//build a connection and assign it to a field
//elided.. fConnection = ConnectionPool.getInstance().getConnection();
}
/**
* Ensure the resources of this object are cleaned up in an orderly manner.
*
* The user of this class must call destroy when finished with
* the object. Calling destroy a second time is permitted, but is
* a no-operation.
*/
public void destroy() throws SQLException {
if (fIsDestroyed) {
return;
}
else{
if (fConnection != null) fConnection.close();
fConnection = null;
//flag that destory has been called, and that
//no further calls on this object are valid
fIsDestroyed = true;
}
}
/**
* Fetches something from the db.
*
* This is an example of a non-private method which must ensure that
* <code>destroy</code> has not yet been called
* before proceeding with execution.
*/
synchronized public Object fetchBlah(String aId) throws SQLException {
validatePlaceInLifeCycle();
//..elided
return null;
}
/**
* If the user fails to call <code>destroy</code>, then implementing
260

Collected Java Practices

* finalize will act as a safety net, but this is not foolproof.


*/
protected void finalize() throws Throwable{
try{
destroy();
}
finally{
super.finalize();
}
}
/**
* Allow the user to determine if <code>destroy</code> has been called.
*/
public boolean isDestoyed() {
return fIsDestroyed;
}
// PRIVATE
/**
* Connection which is constructed and managed by this object.
* The user of this class must call destroy in order to release this
* Connection resource.
*/
private Connection fConnection;
/**
* This object has a specific "life cycle", such that methods must be called
* in the order: others + destroy. fIsDestroyed keeps track of the lifecycle,
* and non-private methods must check this value at the start of execution.
* If destroy is called more than once, a no-operation occurs.
*/
private boolean fIsDestroyed;
/**
* Once <code>destroy</code> has been called, the services of this class
* are no longer available.
*
* @throws IllegalStateException if <code>destroy</code> has
* already been called.
*/
private void validatePlaceInLifeCycle(){
if (fIsDestroyed) {
String message = "Method cannot be called after destroy has been called.";
throw new IllegalStateException(message);
}
}
}

See Also :
Always close streams
Never rely on finalize
Finally and catch
Get database connection
Always shut down an ExecutorService

Recursive file listing


Using JDK 7+, a recursive file listing is a relatively simple task. You can usually use
SimpleFileVisitor .
import java.io.IOException;
import java.nio.file.FileVisitResult;
261

Collected Java Practices

import
import
import
import
import
import

java.nio.file.FileVisitor;
java.nio.file.Files;
java.nio.file.Path;
java.nio.file.Paths;
java.nio.file.SimpleFileVisitor;
java.nio.file.attribute.BasicFileAttributes;

/** Recursive listing with SimpleFileVisitor in JDK 7. */


public final class FileListingVisitor {
public static void main(String... aArgs) throws IOException{
String ROOT = "C:\\test";
FileVisitor<Path> fileProcessor = new ProcessFile();
Files.walkFileTree(Paths.get(ROOT), fileProcessor);
}
private static final class ProcessFile extends SimpleFileVisitor<Path> {
@Override public FileVisitResult visitFile(
Path aFile, BasicFileAttributes aAttrs
) throws IOException {
System.out.println("Processing file:" + aFile);
return FileVisitResult.CONTINUE;
}
@Override public FileVisitResult preVisitDirectory(
Path aDir, BasicFileAttributes aAttrs
) throws IOException {
System.out.println("Processing directory:" + aDir);
return FileVisitResult.CONTINUE;
}
}
}

Older style, JDK 6The File class has methods for listing the contents of a single directory, such as list and listFiles ,
but there is no method for listing all files in a directory tree.
Here's a simple utility which returns a sorted List of File objects which reside under a given root
directory. It uses recursion.
import java.util.*;
import java.io.*;
/**
* Recursive file listing under a specified directory.
*
* @author javapractices.com
* @author Alex Wong
* @author anonymous user
*/
public final class FileListing {
/**
* Demonstrate use.
*
* @param aArgs - <tt>aArgs[0]</tt> is the full name of an existing
* directory that can be read.
*/
public static void main(String... aArgs) throws FileNotFoundException {
File startingDirectory= new File(aArgs[0]);
FileListing listing = new FileListing();
List<File> files = listing.getFileListing(startingDirectory);
//print out all file names, in the the order of File.compareTo()
for(File file : files){
System.out.println(file);
}
}
/**
* Recursively walk a directory tree and return a List of all
* Files found; the List is sorted using File.compareTo().
*
262

Collected Java Practices

* @param aStartingDir is a valid directory, which can be read.


*/
public List<File> getFileListing(
File aStartingDir
) throws FileNotFoundException {
validateDirectory(aStartingDir);
List<File> result = getFileListingNoSort(aStartingDir);
Collections.sort(result);
return result;
}
// PRIVATE
private List<File> getFileListingNoSort(
File aStartingDir
) throws FileNotFoundException {
List<File> result = new ArrayList<>();
File[] filesAndDirs = aStartingDir.listFiles();
List<File> filesDirs = Arrays.asList(filesAndDirs);
for(File file : filesDirs) {
result.add(file); //always add, even if directory
if (! file.isFile()) {
//must be a directory
//recursive call!
List<File> deeperList = getFileListingNoSort(file);
result.addAll(deeperList);
}
}
return result;
}
/**
* Directory is valid if it exists, does not represent a file, and can be read.
*/
private void validateDirectory (
File aDirectory
) throws FileNotFoundException {
if (aDirectory == null) {
throw new IllegalArgumentException("Directory should not be null.");
}
if (!aDirectory.exists()) {
throw new FileNotFoundException("Directory does not exist: " + aDirectory);
}
if (!aDirectory.isDirectory()) {
throw new IllegalArgumentException("Is not a directory: " + aDirectory);
}
if (!aDirectory.canRead()) {
throw new IllegalArgumentException("Directory cannot be read: " + aDirectory);
}
}
}

Example run:
>java -cp . FileListing "C:\TEMP\"
C:\TEMP\Hokkaido.txt
C:\TEMP\Honshu.txt
C:\TEMP\mb
C:\TEMP\mb\Kyushu.txt
C:\TEMP\mb\Shikoku.txt

Replace a substring
Static Replacement

263

Collected Java Practices

Replacing one static String with another can be done in various ways:
public final class ReplaceSubstring {
/**
* Simplest in Java 1.5, using the replace method, which
* takes CharSequence objects.
*/
public static String replace15(
String aInput, String aOldPattern, String aNewPattern
){
return aInput.replace(aOldPattern, aNewPattern);
}
/**
* Not quite as simple in Java 1.4. The replaceAll method works,
* but requires more care, since it uses regular expressions, which
* may contain special characters.
*/
public static String replace14(
String aInput, String aOldPattern, String aNewPattern
){
/*
* The replaceAll method is a bit dangerous to use.
* The aOldPattern is converted into a regular expression.
* Thus, if aOldPattern may contain characters which have
* special meaning to regular expressions, then they must
* be 'escaped' before being passed to replaceAll. It is
* easy to forget to do this.
*
* In addition, aNewPattern treats '$' as special characters
* as well: they refer to 'back references'.
*/
return aInput.replaceAll(aOldPattern, aNewPattern);
/*
Here is an alternative implementation using Pattern and Matcher,
which is preferred when the same pattern is used repeatedly
final Pattern pattern = Pattern.compile( aOldPattern );
final Matcher matcher = pattern.matcher( aInput );
return matcher.replaceAll( aNewPattern );
*/
}
/**
* If Java 1.4 is unavailable, the following technique may be used.
*
* @param aInput is the original String which may contain substring aOldPattern
* @param aOldPattern is the non-empty substring which is to be replaced
* @param aNewPattern is the replacement for aOldPattern
*/
public static String replaceOld(
final String aInput,
final String aOldPattern,
final String aNewPattern
){
if ( aOldPattern.equals("") ) {
throw new IllegalArgumentException("Old pattern must have content.");
}
final StringBuffer result = new StringBuffer();
//startIdx and idxOld delimit various chunks of aInput; these
//chunks always end where aOldPattern begins
int startIdx = 0;
int idxOld = 0;
while ((idxOld = aInput.indexOf(aOldPattern, startIdx)) >= 0) {
//grab a part of aInput which does not include aOldPattern
result.append( aInput.substring(startIdx, idxOld) );
//add aNewPattern to take place of aOldPattern
result.append( aNewPattern );
//reset the startIdx to just after the current match, to see
//if there are any further matches
startIdx = idxOld + aOldPattern.length();
}
//the final chunk will go to the end of aInput
result.append( aInput.substring(startIdx) );
return result.toString();
264

Collected Java Practices

}
/** Example: update an ip address appearing in a link. */
public static void main (String[] aArguments) {
String OLD_IP = "45.23.102.12";
//escape the '.', a special character in regular expressions
String OLD_IP_REGEX = "45\\.23\\.102\\.12";
String NEW_IP = "99.104.106.95";
String LINK = "http://45.23.102.12:8080/index.html";
log("Old link : " + LINK);
String newLink = replace15(LINK, OLD_IP, NEW_IP);
log("New link with Java 1.5 replace: " + newLink);
newLink = replace14(LINK, OLD_IP_REGEX, NEW_IP);
log("New link with Java 1.4 replaceAll: " + newLink);
newLink = replaceOld(LINK, OLD_IP, NEW_IP);
log("New link with oldest style: " + newLink);
}
private static void log(String aMessage){
System.out.println(aMessage);
}
}

Example run of this class:


Old
New
New
New

link
link
link
link

: http://45.23.102.12:8080/index.html
with Java 1.5 replace: http://99.104.106.95:8080/index.html
with Java 1.4 replaceAll: http://99.104.106.95:8080/index.html
with oldest style: http://99.104.106.95:8080/index.html

Dynamic Replacement
If the replacement string is not fixed, and needs to be created dynamically, then another approach is
required. In the following example, strings of the form "href=Topic182.cjp" are replaced with a
corresponding string "href=#182 ".
The number 182 is taken only as an example. It is in fact extracted dynamically, and referenced in the
replacement string using the back reference "$1", where 1 is the index of the matching group for these
digits.
import java.util.regex.*;
public final class ReplaceSubstringDynamically {
public static void main (String... aArguments) {
String htmlText = "<a href=\"Topic27.cjp\">xyz</a> blah <a
href=Topic8.cjp>abc</a>";
ReplaceSubstringDynamically replace = new ReplaceSubstringDynamically();
System.out.println("Old HTML text : " + htmlText);
System.out.println("New HTML text : " + replace.replaceLinks(htmlText));
}
/**
* Replace the document links in a snippet of HTML with corresponding
* fragment links, which start with the # sign, and refer to labelled
* locations within a single document.
*/
String replaceLinks(String aHtmlTextWithLinks){
Pattern pattern = Pattern.compile(fLINK);
Matcher matcher = pattern.matcher(aHtmlTextWithLinks);
return matcher.replaceAll(fFRAGMENT);
}
/**
* The single matching group of this regex are the digits ((?:\\d){1,3}),
* which correspond to group 1.
*/
265

Collected Java Practices

private static String fLINK = "href=(?:\"|\')?Topic((?:\\d){1,3})\\.cjp(?:\"|\')?";


/**
* The "$1" refers to matching group 1 of fLINK (the digits).
*/
private static String fFRAGMENT = "href=#$1";
}

Example run of this class:


Old HTML text : <a href="Topic27.cjp">xyz</a> blah <a href=Topic8.cjp>abc</a>
New HTML text : <a href=#27>xyz</a> blah <a href=#8>abc</a>

Here's a second example, where the replacement string is computed without using back references.
import java.util.regex.*;
public final class ReplaceSubstringAppendReplacement {
public static void main (String... aArguments) {
String text = "Apples and oranges are better for all.";
ReplaceSubstringAppendReplacement repl = new ReplaceSubstringAppendReplacement();
System.out.println("Old text : " + text);
System.out.println("New text : " + repl.getEditedText(text));
}
/**
* Replace all words starting with the letter 'a' or 'A' with
* their uppercase forms.
*/
String getEditedText(String aText){
StringBuffer result = new StringBuffer();
Matcher matcher = fINITIAL_A.matcher(aText);
while (matcher.find()) {
matcher.appendReplacement(result, getReplacement(matcher));
}
matcher.appendTail(result);
return result.toString();
}
private static final Pattern fINITIAL_A = Pattern.compile(
"(?:\\s|^)a(?:\\w)*",
Pattern.CASE_INSENSITIVE
);
private String getReplacement(Matcher aMatcher){
return aMatcher.group(0).toUpperCase();
}
}

Example run of this class:


Old text : Apples and oranges are better for all.
New text : APPLES AND oranges ARE better for ALL.

Warning
The methods:
String.replaceAll(String, String)
String.replaceFirst(String, String)
Matcher.appendReplacement(StringBuffer, String)

treat '$' and '\' in the replacement text as special characters. If the replacement text can contain
arbitrary text, then these characters will usually be escaped using Matcher.quoteReplacement(String) .
266

Collected Java Practices

See Also :
Escape special characters

Representing money
Representing money:
use BigDecimal , int , or long ( BigDecimal is the recommended default)
the int and long forms represent pennies (or the equivalent, of course)
BigDecimal is a little more inconvenient to use, but has built-in rounding modes
double or float are not recommended, since they always carry small rounding differences
the Currency class encapsulates standard identifiers for the world's currencies
Number of digits:
<=9 : use BigDecimal , int , or long
<=18 : use BigDecimal , or long
>18 : use BigDecimal

Reminders for BigDecimal :


the recommended constructor is BigDecimal(String) , not BigDecimal(double) - see javadoc
BigDecimal objects are immutable - operations always return new objects, and never modify the
state of existing objects
the ROUND_HALF_EVEN style of rounding introduces the least bias. It is also called bankers'
rounding, or round-to-even.
Example 1
Example of using BigDecimal to perform monetary calculations:
import java.math.BigDecimal;
import java.util.Currency;
/**
* Example of typical calculations with monetary values, implemented with
* <tt>BigDecimal</tt>.
*
* <P>This example is for a currency which has two decimal places.
*
* See
* http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html
*
* Note in particular how the <em>default</em> scale of the result of an
* operation is calculated from the scales of the two input numbers :
* <ul>
* <li> a + b : max[ scale(a), scale(b) ]
* <li> a - b : max[ scale(a), scale(b) ]
* <li> a * b : scale(a) + scale(b)
* <li> a / b : scale(a) - scale(b)
* </ul>
*/
public final class MoneyCalculation {
/**
* Simple test harness.
*
* Takes two numeric arguments, representing monetary values, in a form
* which can be passed successfully to the <tt>BigDecimal(String)</tt>
267

Collected Java Practices

* constructor (<tt>25.00, 25.0, 25</tt>, etc).


*
* Note that the <tt>String</tt> constructor is preferred for
* <tt>BigDecimal</tt>.
*/
public static void main(String... aArgs){
BigDecimal amountOne = new BigDecimal(aArgs[0]);
BigDecimal amountTwo = new BigDecimal(aArgs[1]);
MoneyCalculation calc = new MoneyCalculation(amountOne, amountTwo);
calc.doCalculations();
}
public MoneyCalculation(BigDecimal aAmountOne, BigDecimal aAmountTwo){
fAmountOne = rounded(aAmountOne);
fAmountTwo = rounded(aAmountTwo);
}
public void doCalculations() {
log("Amount One: " + fAmountOne);
log("Amount Two: " + fAmountTwo);
log("Sum : " + getSum());
log("Difference : " + getDifference());
log("Average : " + getAverage());
log("5.25% of Amount One: " + getPercentage());
log("Percent Change From Amount One to Two: " + getPercentageChange());
}
// PRIVATE
private BigDecimal fAmountOne;
private BigDecimal fAmountTwo;
/**
* Defined centrally, to allow for easy changes to the rounding mode.
*/
private static int ROUNDING_MODE = BigDecimal.ROUND_HALF_EVEN;
/**
* Number of decimals to retain. Also referred to as "scale".
*/
private static int DECIMALS = 2;
//An alternate style for this value :
//private static int DECIMAL_PLACES =
// Currency.getInstance("USD").getDefaultFractionDigits()
//;
private
private
private
private

static
static
static
static

int EXTRA_DECIMALS = 4;
final BigDecimal TWO = new BigDecimal("2");
BigDecimal HUNDRED = new BigDecimal("100");
BigDecimal PERCENTAGE = new BigDecimal("5.25");

private void log(String aText){


System.out.println(aText);
}
private BigDecimal getSum(){
return fAmountOne.add(fAmountTwo);
}
private BigDecimal getDifference(){
return fAmountTwo.subtract(fAmountOne);
}
private BigDecimal getAverage(){
return getSum().divide(TWO, ROUNDING_MODE);
}
private BigDecimal getPercentage(){
BigDecimal result = fAmountOne.multiply(PERCENTAGE);
result = result.divide(HUNDRED, ROUNDING_MODE);
return rounded(result);
}
private BigDecimal getPercentageChange(){
BigDecimal fractionalChange = getDifference().divide(
fAmountOne, EXTRA_DECIMALS, ROUNDING_MODE
);
return rounded(fractionalChange.multiply(HUNDRED));
268

Collected Java Practices

}
private BigDecimal rounded(BigDecimal aNumber){
return aNumber.setScale(DECIMALS, ROUNDING_MODE);
}
}

An example run gives:


>java -cp . MoneyCalculation 513.89 612.25
Amount One: 513.89
Amount Two: 612.25
Sum : 1126.14
Difference : 98.36
Average : 563.07
5.25% of Amount One: 26.98
Percent Change From Amount One to Two: 19.14

Example 2
The lack of an actual Money class in the standard JDK libraries is frustrating. Such a class would have
some nice advantages:
the name Money reads at a higher level of abstraction than BigDecimal
operations on BigDecimal can be wrapped to yield a more suitable form for practical use
Here's an example of such a Money class.
import
import
import
import
import
import
import
import

java.util.*;
java.io.Serializable;
java.io.IOException;
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
java.math.BigDecimal;
static java.math.BigDecimal.ZERO;
java.math.RoundingMode;

/**
* Represent an amount of money in any currency.
*
* <P>This class assumes <em>decimal currency</em>, without funky divisions
* like 1/5 and so on. <tt>Money</tt> objects are immutable. Like {@link BigDecimal},
* many operations return new <tt>Money</tt> objects. In addition, most operations
* involving more than one <tt>Money</tt> object will throw a
* <tt>MismatchedCurrencyException</tt> if the currencies don't match.
*
* <h2>Decimal Places and Scale</h2>
* Monetary amounts can be stored in the database in various ways. Let's take the
* example of dollars. It may appear in the database in the following ways :
* <ul>
* <li>as <tt>123456.78</tt>, with the usual number of decimal places
*
associated with that currency.
* <li>as <tt>123456</tt>, without any decimal places at all.
* <li>as <tt>123</tt>, in units of thousands of dollars.
* <li>in some other unit, such as millions or billions of dollars.
* </ul>
*
* <P>The number of decimal places or style of units is referred to as the
* <em>scale</em> by {@link java.math.BigDecimal}. This class's constructors
* take a <tt>BigDecimal</tt>, so you need to understand it use of the idea of scale.
*
* <P>The scale can be negative. Using the above examples :
* <table border='1' cellspacing='0' cellpadding='3'>
* <tr><th>Number</th><th>Scale</th></tr>
* <tr><td>123456.78</th><th>2</th></tr>
* <tr><td>123456</th><th>0</th></tr>
* <tr><td>123 (thousands)</th><th>-3</th></tr>
* </table>
269

Collected Java Practices

*
* <P>Note that scale and rounding are two separate issues.
* In addition, rounding is only necessary for multiplication and division operations.
* It doesn't apply to addition and subtraction.
*
* <h2>Operations and Scale</h2>
* <P>Operations can be performed on items having <em>different scale</em>.
* For example, these operations are valid (using an <em>ad hoc</em>
* symbolic notation):
* <PRE>
* 10.plus(1.23) => 11.23
* 10.minus(1.23) => 8.77
* 10.gt(1.23) => true
* 10.eq(10.00) => true
* </PRE>
* This corresponds to typical user expectations.
* An important exception to this rule is that {@link #equals(Object)} is sensitive
* to scale (while {@link #eq(Money)} is not) . That is,
* <PRE>
*
10.equals(10.00) => false
* </PRE>
*
* <h2>Multiplication, Division and Extra Decimal Places</h2>
* <P>Operations involving multiplication and division are different, since the result
* can have a scale which exceeds that expected for the given currency. For example
* <PRE>($10.00).times(0.1256) => $1.256</PRE>
* which has more than two decimals. In such cases, <em>this class will always round
* to the expected number of decimal places for that currency.</em>
* This is the simplest policy, and likely conforms to the expectations of most
* end users.
*
* <P>This class takes either an <tt>int</tt> or a {@link BigDecimal} for its
* multiplication and division methods. It doesn't take <tt>float</tt> or
* <tt>double</tt> for those methods, since those types don't interact well with
* <tt>BigDecimal</tt>. Instead, the <tt>BigDecimal</tt> class must be used when the
* factor or divisor is a non-integer.
*
* <P><em>The {@link #init(Currency, RoundingMode)} method must be called at least
* once before using the other members of this class.</em> It establishes your
* desired defaults. Typically, it will be called once (and only once) upon startup.
*
* <P>Various methods in this class have unusually terse names, such as
* {@link #lt} and {@link #gt}. The intent is that such names will improve the
* legibility of mathematical expressions. Example :
* <PRE> if ( amount.lt(hundred) ) {
*
cost = amount.times(price);
* }</PRE>
*/
public final class Money implements Comparable<Money>, Serializable {
/**
* Thrown when a set of <tt>Money</tt> objects do not have matching currencies.
*
* <P>For example, adding together Euros and Dollars does not make any sense.
*/
public static final class MismatchedCurrencyException extends RuntimeException {
MismatchedCurrencyException(String aMessage){
super(aMessage);
}
}
/**
* Set default values for currency and rounding style.
*
* <em>Your application must call this method upon startup</em>.
* This method should usually be called only once (upon startup).
*
* <P>The recommended rounding style is {@link RoundingMode#HALF_EVEN}, also called
* <em>banker's rounding</em>; this rounding style introduces the least bias.
*
* <P>Setting these defaults allow you to use the more terse constructors of this
class,
* which are much more convenient.
*
* <P>(In a servlet environment, each app has its own classloader. Calling this
* method in one app will never affect the operation of a second app running in the
same
* servlet container. They are independent.)
*/
public static void init(Currency aDefaultCurrency, RoundingMode aDefaultRounding){
270

Collected Java Practices

DEFAULT_CURRENCY = aDefaultCurrency;
DEFAULT_ROUNDING = aDefaultRounding;
}
/**
* Full constructor.
*
* @param aAmount is required, can be positive or negative. The number of
* decimals in the amount cannot <em>exceed</em> the maximum number of
* decimals for the given {@link Currency}. It's possible to create a
* <tt>Money</tt> object in terms of 'thousands of dollars', for instance.
* Such an amount would have a scale of -3.
* @param aCurrency is required.
* @param aRoundingStyle is required, must match a rounding style used by
* {@link BigDecimal}.
*/
public Money(BigDecimal aAmount, Currency aCurrency, RoundingMode aRoundingStyle){
fAmount = aAmount;
fCurrency = aCurrency;
fRounding = aRoundingStyle;
validateState();
}
/**
* Constructor taking only the money amount.
*
* <P>The currency and rounding style both take default values.
* @param aAmount is required, can be positive or negative.
*/
public Money(BigDecimal aAmount){
this(aAmount, DEFAULT_CURRENCY, DEFAULT_ROUNDING);
}
/**
* Constructor taking the money amount and currency.
*
* <P>The rounding style takes a default value.
* @param aAmount is required, can be positive or negative.
* @param aCurrency is required.
*/
public Money(BigDecimal aAmount, Currency aCurrency){
this(aAmount, aCurrency, DEFAULT_ROUNDING);
}
/** Return the amount passed to the constructor. */
public BigDecimal getAmount() { return fAmount; }
/** Return the currency passed to the constructor, or the default currency. */
public Currency getCurrency() { return fCurrency; }
/** Return the rounding style passed to the constructor, or the default rounding
style. */
public RoundingMode getRoundingStyle() { return fRounding; }
/**
* Return <tt>true</tt> only if <tt>aThat</tt> <tt>Money</tt> has the same currency
* as this <tt>Money</tt>.
*/
public boolean isSameCurrencyAs(Money aThat){
boolean result = false;
if ( aThat != null ) {
result = this.fCurrency.equals(aThat.fCurrency);
}
return result;
}
/** Return <tt>true</tt> only if the amount is positive. */
public boolean isPlus(){
return fAmount.compareTo(ZERO) > 0;
}
/** Return <tt>true</tt> only if the amount is negative. */
public boolean isMinus(){
return fAmount.compareTo(ZERO) < 0;
}
/** Return <tt>true</tt> only if the amount is zero. */
public boolean isZero(){
return fAmount.compareTo(ZERO) == 0;
271

Collected Java Practices

}
/**
* Add <tt>aThat</tt> <tt>Money</tt> to this <tt>Money</tt>.
* Currencies must match.
*/
public Money plus(Money aThat){
checkCurrenciesMatch(aThat);
return new Money(fAmount.add(aThat.fAmount), fCurrency, fRounding);
}
/**
* Subtract <tt>aThat</tt> <tt>Money</tt> from this <tt>Money</tt>.
* Currencies must match.
*/
public Money minus(Money aThat){
checkCurrenciesMatch(aThat);
return new Money(fAmount.subtract(aThat.fAmount), fCurrency, fRounding);
}
/**
* Sum a collection of <tt>Money</tt> objects.
* Currencies must match. You are encouraged to use database summary functions
* whenever possible, instead of this method.
*
* @param aMoneys collection of <tt>Money</tt> objects, all of the same currency.
* If the collection is empty, then a zero value is returned.
* @param aCurrencyIfEmpty is used only when <tt>aMoneys</tt> is empty; that way,
this
* method can return a zero amount in the desired currency.
*/
public static Money sum(Collection<Money> aMoneys, Currency aCurrencyIfEmpty){
Money sum = new Money(ZERO, aCurrencyIfEmpty);
for(Money money : aMoneys){
sum = sum.plus(money);
}
return sum;
}
/**
* Equals (insensitive to scale).
*
* <P>Return <tt>true</tt> only if the amounts are equal.
* Currencies must match.
* This method is <em>not</em> synonymous with the <tt>equals</tt> method.
*/
public boolean eq(Money aThat) {
checkCurrenciesMatch(aThat);
return compareAmount(aThat) == 0;
}
/**
* Greater than.
*
* <P>Return <tt>true</tt> only if 'this' amount is greater than
* 'that' amount. Currencies must match.
*/
public boolean gt(Money aThat) {
checkCurrenciesMatch(aThat);
return compareAmount(aThat) > 0;
}
/**
* Greater than or equal to.
*
* <P>Return <tt>true</tt> only if 'this' amount is
* greater than or equal to 'that' amount. Currencies must match.
*/
public boolean gteq(Money aThat) {
checkCurrenciesMatch(aThat);
return compareAmount(aThat) >= 0;
}
/**
* Less than.
*
* <P>Return <tt>true</tt> only if 'this' amount is less than
* 'that' amount. Currencies must match.
*/
272

Collected Java Practices

public boolean lt(Money aThat) {


checkCurrenciesMatch(aThat);
return compareAmount(aThat) < 0;
}
/**
* Less than or equal to.
*
* <P>Return <tt>true</tt> only if 'this' amount is less than or equal to
* 'that' amount. Currencies must match.
*/
public boolean lteq(Money aThat) {
checkCurrenciesMatch(aThat);
return compareAmount(aThat) <= 0;
}
/**
* Multiply this <tt>Money</tt> by an integral factor.
*
* The scale of the returned <tt>Money</tt> is equal to the scale of 'this'
* <tt>Money</tt>.
*/
public Money times(int aFactor){
BigDecimal factor = new BigDecimal(aFactor);
BigDecimal newAmount = fAmount.multiply(factor);
return new Money(newAmount, fCurrency, fRounding);
}
/**
* Multiply this <tt>Money</tt> by an non-integral factor (having a decimal point).
*
* <P>The scale of the returned <tt>Money</tt> is equal to the scale of
* 'this' <tt>Money</tt>.
*/
public Money times(double aFactor){
BigDecimal newAmount = fAmount.multiply(asBigDecimal(aFactor));
newAmount = newAmount.setScale(getNumDecimalsForCurrency(), fRounding);
return new Money(newAmount, fCurrency, fRounding);
}
/**
* Divide this <tt>Money</tt> by an integral divisor.
*
* <P>The scale of the returned <tt>Money</tt> is equal to the scale of
* 'this' <tt>Money</tt>.
*/
public Money div(int aDivisor){
BigDecimal divisor = new BigDecimal(aDivisor);
BigDecimal newAmount = fAmount.divide(divisor, fRounding);
return new Money(newAmount, fCurrency, fRounding);
}
/**
* Divide this <tt>Money</tt> by an non-integral divisor.
*
* <P>The scale of the returned <tt>Money</tt> is equal to the scale of
* 'this' <tt>Money</tt>.
*/
public Money div(double aDivisor){
BigDecimal newAmount = fAmount.divide(asBigDecimal(aDivisor), fRounding);
return new Money(newAmount, fCurrency, fRounding);
}
/** Return the absolute value of the amount. */
public Money abs(){
return isPlus() ? this : times(-1);
}
/** Return the amount x (-1). */
public Money negate(){
return times(-1);
}
/**
* Returns
* {@link #getAmount()}.getPlainString() + space + {@link #getCurrency()}.getSymbol().
*
* <P>The return value uses the runtime's <em>default locale</em>, and will not
* always be suitable for display to an end user.
273

Collected Java Practices

*/
public String toString(){
return fAmount.toPlainString() + " " + fCurrency.getSymbol();
}
/**
* Like {@link BigDecimal#equals(java.lang.Object)}, this <tt>equals</tt> method
* is also sensitive to scale.
*
* For example, <tt>10</tt> is <em>not</em> equal to <tt>10.00</tt>
* The {@link #eq(Money)} method, on the other hand, is <em>not</em>
* sensitive to scale.
*/
public boolean equals(Object aThat){
if (this == aThat) return true;
if (! (aThat instanceof Money) ) return false;
Money that = (Money)aThat;
//the object fields are never null :
boolean result = (this.fAmount.equals(that.fAmount) );
result = result && (this.fCurrency.equals(that.fCurrency) );
result = result && (this.fRounding == that.fRounding);
return result;
}
public int hashCode(){
if ( fHashCode == 0 ) {
fHashCode = HASH_SEED;
fHashCode = HASH_FACTOR * fHashCode + fAmount.hashCode();
fHashCode = HASH_FACTOR * fHashCode + fCurrency.hashCode();
fHashCode = HASH_FACTOR * fHashCode + fRounding.hashCode();
}
return fHashCode;
}
public int compareTo(Money aThat) {
final int EQUAL = 0;
if ( this == aThat ) return EQUAL;
//the object fields are never null
int comparison = this.fAmount.compareTo(aThat.fAmount);
if ( comparison != EQUAL ) return comparison;
comparison = this.fCurrency.getCurrencyCode().compareTo(
aThat.fCurrency.getCurrencyCode()
);
if ( comparison != EQUAL ) return comparison;
comparison = this.fRounding.compareTo(aThat.fRounding);
if ( comparison != EQUAL ) return comparison;
return EQUAL;
}
// PRIVATE //
/**
* The money amount.
* Never null.
* @serial
*/
private BigDecimal fAmount;
/**
* The currency of the money, such as US Dollars or Euros.
* Never null.
* @serial
*/
private final Currency fCurrency;
/**
* The rounding style to be used.
* See {@link BigDecimal}.
* @serial
*/
private final RoundingMode fRounding;

274

Collected Java Practices

/**
* The default currency to be used if no currency is passed to the constructor.
*/
private static Currency DEFAULT_CURRENCY;
/**
* The default rounding style to be used if no currency is passed to the constructor.
* See {@link BigDecimal}.
*/
private static RoundingMode DEFAULT_ROUNDING;
/** @serial */
private int fHashCode;
private static final int HASH_SEED = 23;
private static final int HASH_FACTOR = 37;
/**
* Determines if a deserialized file is compatible with this class.
*
* Maintainers must change this value if and only if the new version
* of this class is not compatible with old versions. See Sun docs
* for <a href=http://java.sun.com/products/jdk/1.1/docs/guide
* /serialization/spec/version.doc.html> details. </a>
*
* Not necessary to include in first version of the class, but
* included here as a reminder of its importance.
*/
private static final long serialVersionUID = 7526471155622776147L;
/**
* Always treat de-serialization as a full-blown constructor, by
* validating the final state of the de-serialized object.
*/
private void readObject(
ObjectInputStream aInputStream
) throws ClassNotFoundException, IOException {
//always perform the default de-serialization first
aInputStream.defaultReadObject();
//defensive copy for mutable date field
//BigDecimal is not technically immutable, since its non-final
fAmount = new BigDecimal( fAmount.toPlainString() );
//ensure that object state has not been corrupted or tampered with maliciously
validateState();
}
private void writeObject(ObjectOutputStream aOutputStream) throws IOException {
//perform the default serialization for all non-transient, non-static fields
aOutputStream.defaultWriteObject();
}
private void validateState(){
if( fAmount == null ) {
throw new IllegalArgumentException("Amount cannot be null");
}
if( fCurrency == null ) {
throw new IllegalArgumentException("Currency cannot be null");
}
if ( fAmount.scale() > getNumDecimalsForCurrency() ) {
throw new IllegalArgumentException(
"Number of decimals is " + fAmount.scale() + ", but currency only takes " +
getNumDecimalsForCurrency() + " decimals."
);
}
}
private int getNumDecimalsForCurrency(){
return fCurrency.getDefaultFractionDigits();
}
private void checkCurrenciesMatch(Money aThat){
if (! this.fCurrency.equals(aThat.getCurrency())) {
throw new MismatchedCurrencyException(
aThat.getCurrency() + " doesn't match the expected currency : " + fCurrency
);
}
}
/** Ignores scale: 0 same as 0.00 */
private int compareAmount(Money aThat){
275

Collected Java Practices

return this.fAmount.compareTo(aThat.fAmount);
}
private BigDecimal asBigDecimal(double aDouble){
String asString = Double.toString(aDouble);
return new BigDecimal(asString);
}
}

See Also :
Beware of floating point numbers

Send an email
The JavaMail API is not part of core Java SE, but an optional extension. (It's required in Java Enterprise
Edition.) The JavaMail packages can be accessed in two ways:
by placing both mail.jar and activation.jar in the classpath
or, by placing j2ee.jar in the classpath
The javax.mail API uses a properties file for reading server names and related configuration. These
settings will override any system defaults. Alternatively, the configuration can be set directly in code,
using the JavaMail API.
Example
An email configuration file (to run the example, substitute valid values for the uncommented items):

#
#
#
#

Configuration file for javax.mail


If a value for an item is not provided, then
system defaults will be used. These items can
also be set in code.

# Host whose mail services will be used


# (Default value : localhost)
mail.host=mail.blah.com
# Return address to appear on emails
# (Default value : username@host)
mail.from=webmaster@blah.net
#
#
#
#
#
#
#

Other possible items include:


mail.user=
mail.store.protocol=
mail.transport.protocol=
mail.smtp.host=
mail.smtp.user=
mail.debug=

A class which uses this file to send an email:

276

Collected Java Practices

import
import
import
import
import

java.util.*;
java.io.*;
java.nio.file.*;
javax.mail.*;
javax.mail.internet.*;

/**
* Simple demonstration of using the javax.mail API.
*
* Run from the command line. Please edit the implementation
* to use correct email addresses and host name.
*/
public final class Emailer {
public static void main( String... aArguments ){
Emailer emailer = new Emailer();
//the domains of these email addresses should be valid,
//or the example will fail:
emailer.sendEmail(
"fromblah@blah.com", "toblah@blah.com",
"Testing 1-2-3", "blah blah blah"
);
}
/**
* Send a single email.
*/
public void sendEmail(
String aFromEmailAddr, String aToEmailAddr,
String aSubject, String aBody
){
//Here, no Authenticator argument is used (it is null).
//Authenticators are used to prompt the user for user
//name and password.
Session session = Session.getDefaultInstance(fMailServerConfig, null);
MimeMessage message = new MimeMessage(session);
try {
//the "from" address may be set in code, or set in the
//config file under "mail.from" ; here, the latter style is used
//message.setFrom(new InternetAddress(aFromEmailAddr));
message.addRecipient(
Message.RecipientType.TO, new InternetAddress(aToEmailAddr)
);
message.setSubject(aSubject);
message.setText(aBody);
Transport.send(message);
}
catch (MessagingException ex){
System.err.println("Cannot send email. " + ex);
}
}
/**
* Allows the config to be refreshed at runtime, instead of
* requiring a restart.
*/
public static void refreshConfig() {
fMailServerConfig.clear();
fetchConfig();
}
// PRIVATE
private static Properties fMailServerConfig = new Properties();
static {
fetchConfig();
}
/**
* Open a specific text file containing mail server
* parameters, and populate a corresponding Properties object.
*/
private static void fetchConfig() {
//This file contains the javax.mail config properties mentioned above.
Path path = Paths.get("C:\\Temp\\MyMailServer.txt");
try (InputStream input = Files.newInputStream(path)) {
fMailServerConfig.load(input);
}
277

Collected Java Practices

catch (IOException ex){


System.err.println("Cannot open and load mail server properties file.");
}
}
}

See Also :
Launch other applications

Time execution speed


Here's an example of a utility Stopwatch class which can provide timings for any section of code.
It's important to note that System.currentTimeMillis has low resolution, on the order of 10ms for most
systems. For timing the execution speed of code, it's usually best to use System.nanoTime instead.
According to this excellent article by David Holmes, the typical resolution of nanoTime is on the order of
microseconds.
Since variations will naturally occur between invocations, you should take the median value of a number
of different measurements.
Profiling tools like Visual VM are another option as well. Profilers are often included in IDEs.
Note that precise micro-benchmarking in Java is a tricky business, mostly because of just-in-time
compilation (the HotSpot compiler):
the presence or absence of compiled code will greatly influence timings.
if you need to time only the compiled version, then you will need to ensure a proper warm up of
the code.
for another data point, you can simply turn off the HotSpot compiler entirely, using java -Xint .
See the copy an array topic for an illustration.
Here's an example run of the Stopwatch class appearing below:
The reading for StringBuilder is: 3.611 ms
The reading is high: 193.484 ms

import java.math.BigDecimal;
/**
Time the execution of any block of code.
<P>This implementation times the duration using <tt>System.nanoTime</tt>.
<P>On most systems <tt>System.currentTimeMillis</tt> has a time
resolution of about 10ms, which is quite poor for timing code, so it is
avoided here.
*/
public final class Stopwatch {
/**
An example of the use of this class to
time the execution of simple String manipulation code.
*/
278

Collected Java Practices

public static void main(String... arguments) {


Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
//do stuff
StringBuilder messageOne = new StringBuilder();
int numIterations = 5000;
for(int idx=0; idx < numIterations; ++idx){
messageOne.append("blah");
}
stopwatch.stop();
//Note that there is no need to call a method to get the duration,
//since toString is automatic here
System.out.println("The reading for StringBuilder is: " + stopwatch);
//reuse the same stopwatch to measure an alternative implementation
//Note that there is no need to call a reset method.
stopwatch.start();
//do stuff again
String messageTwo = null;
for(int idx=0; idx < numIterations; ++idx){
messageTwo = messageTwo + "blah";
}
stopwatch.stop();
//perform a numeric comparsion
if ( stopwatch.toValue() > 5 ) {
System.out.println("The reading is high: " + stopwatch);
}
else {
System.out.println("The reading is low: " + stopwatch);
}
}
/**
Start the stopwatch.
@throws IllegalStateException if the stopwatch is already running.
*/
public void start(){
if (fIsRunning) {
throw new IllegalStateException("Must stop before calling start again.");
}
//reset both start and stop
fStart = System.nanoTime();
fStop = 0;
fIsRunning = true;
fHasBeenUsedOnce = true;
}
/**
Stop the stopwatch.
@throws IllegalStateException if the stopwatch is not already running.
*/
public void stop() {
if (!fIsRunning) {
throw new IllegalStateException("Cannot stop if not currently running.");
}
fStop = System.nanoTime();
fIsRunning = false;
}
/**
Express the "reading" on the stopwatch.
<P>Example: <tt>123.456 ms</tt>. The resolution of timings on most systems
is on the order of a few microseconds, so this style of presentation is usually
appropriate for reflecting the real precision of most timers.
<P>Ref: https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks
@throws IllegalStateException if the Stopwatch has never been used,
or if the stopwatch is still running.
*/
@Override public String toString() {
validateIsReadable();
StringBuilder result = new StringBuilder();
279

Collected Java Practices

BigDecimal value = new BigDecimal(toValue());//scale is zero


//millis, with 3 decimals:
value = value.divide(MILLION, 3, BigDecimal.ROUND_HALF_EVEN);
result.append(value);
result.append(" ms");
return result.toString();
}
/**
Express the "reading" on the stopwatch as a numeric type, in nanoseconds.
@throws IllegalStateException if the Stopwatch has never been used,
or if the stopwatch is still running.
*/
public long toValue() {
validateIsReadable();
return fStop - fStart;
}
// PRIVATE
private long fStart;
private long fStop;
private boolean fIsRunning;
private boolean fHasBeenUsedOnce;
/** Converts from nanos to millis. */
private static final BigDecimal MILLION = new BigDecimal("1000000");
/**
Throws IllegalStateException if the watch has never been started,
or if the watch is still running.
*/
private void validateIsReadable() {
if (fIsRunning) {
String message = "Cannot read a stopwatch which is still running.";
throw new IllegalStateException(message);
}
if (!fHasBeenUsedOnce) {
String message = "Cannot read a stopwatch which has never been started.";
throw new IllegalStateException(message);
}
}
}

See Also :
Copy an array
String concatenation does not scale
Measure application performance

Abstract Factory
Using references to interfaces instead of references to concrete classes is an important way of minimizing
ripple effects. The user of an interface reference is always protected from changes to the underlying
implementation.
The Abstract Factory pattern is one example of this technique. Users of an Abstract Factory can create
families of related objects without any knowledge of their concrete classes. (A typical business
application would usually not need to use this technique, at least as applied to Data Access Objects.)
Example
280

Collected Java Practices

An Abstract Factory is a major part of the full Data Access Object (DAO) scheme. Here, the idea is to
allow the business layer to interact with the data layer almost entirely through interface references. The
business layer remains ignorant of the concrete classes which implement the datastore.
There are two distinct families of items here:
the various datastore implementations (in this case, text files, or a relational database)
the various business objects which need persistence (in this case, User and Device)
Let's take the example of storing Device objects. They may be stored in either a text file or a relational
database. Since the calling code needs to remain ignorant of which is being used, it's natural, of course,
to define an interface that reflects this, along with two corresponding concrete implementations:
package myapp;
public interface DeviceDAO {
Device fetch(String aId) throws DataAccessException;
void add(Device aDevice) throws DataAccessException;
void change(Device aDevice) throws DataAccessException;
void delete(Device aDevice) throws DataAccessException;
}

...with an implementation for a file system:


package myapp;
final class DeviceDAOFileSys implements DeviceDAO {
/*
* The constructor will usually be passed any required config data.
*/
@Override public void add(Device aDevice) throws DataAccessException {
//elided...
}
@Override public void change(Device aDevice) throws DataAccessException {
//elided...
}
@Override public void delete(Device aDevice) throws DataAccessException {
//elided...
}
@Override public Device fetch(String aId) throws DataAccessException {
//elided...
return null;
}
}

...and an implementation for a relational database:


package myapp;
final class DeviceDAORelational implements DeviceDAO {
/*
* The constructor will usually be passed any required config data.
*/
@Override public void add(Device aDevice) throws DataAccessException {
//elided...
}

281

Collected Java Practices

@Override public void change(Device aDevice) throws DataAccessException {


//elided...
}
@Override public void delete(Device aDevice) throws DataAccessException {
//elided...
}
@Override public Device fetch(String aId) throws DataAccessException {
//elided...
return null;
}
}

(The details of these classes are left out, so that you can see the structure better.)
Next, the calling application needs a way to interact with the database, without knowing about the
concrete implementations. This is done using a factory class:
package myapp;
/**
Returns all DAO instances.
Reads a configuration item (defined by your program) to decide
which family of DAO objects it should be returning, for the currently
running program, either file-based or relational-based.
The configuration mechanism may be a System property, a properties file, an
XML file, and so on. The config is often read when the system initializes,
perhaps using a static initializer.
*/
final class DAOFactory {
/* some would implement all of the methods here as static methods */
DeviceDAO getDeviceDAO(){
//elided:
return null;
}
UserDAO getUserDAO(){
//elided:
return null;
}
}

package myapp;
public final class Application {
/**
This calling code is completely unaware of the underlying datastore.
It could be a text file, or a relational database.
*/
void addNewDevice(Device aDevice){
DAOFactory factory = new DAOFactory();
try {
factory.getDeviceDAO().add(aDevice);
}
catch (DataAccessException ex) {
ex.printStackTrace();
}
}
}

282

Collected Java Practices

To pile on even more interfaces, one variation on the above is to make an abstraction or the factory itself:
the factory is defined as an interface, with methods similar to the above. Then, two concrete
implementations of the factory interface are defined, one for each kind of datastore.
package myapp;
public interface Factory {
DeviceDAO getDeviceDAO();
UserDAO getUserDAO();
}

package myapp;
public final class FactoryBuilder {
/** returns a specific implementation of Factory */
Factory getFactory(){
//elided
return null;
}
}

See Also :
Factory methods
Data access objects
Data exception wrapping
Construct Object using class name
Minimize ripple effects
Parse parameters into domain objects

Command objects
It's a common practice to define an interface for processing a user's request at a high level of abstraction.
This corresponds roughly to the Command pattern described in Design Patterns.
Example
This example is in the context of a web application. Action is an interface which:
executes a business operation (validates input, interacts with the datastore, and places appropriate
model objects in request scope or session scope)
sets the identity of the resource which will render the model objects (typically a JSP)
controls when a redirect should be sent to the browser

package hirondelle.web4j.action;
283

Collected Java Practices

import hirondelle.web4j.model.AppException;
/**
<span class="highlight">
Process an HTTP request, and return a {@link ResponsePage}.
</span>
<P><b>This interface is likely the most important
abstraction in WEB4J.</b> Almost every feature implemented by the programmer will
need an implementation of this interface.
<P>Typically, one of the <em>ActionXXX</em> abstract base classes are used to
build implementations of this interface.
*/
public interface Action {
/**
Execute desired operation.
<P>Typical operations include :
<ul>
<li>validate user input
<li>interact with the database
<li>place required objects in the appropriate scope
<li>set the appropriate {@link ResponsePage}.
</ul>
<P>Returns an identifier for the resource (for example a JSP) which
will display the end result of this <tt>Action</tt> (using either a
forward or a redirect).
*/
ResponsePage execute() throws AppException;
}

implementations perform similar tasks. These tasks should be placed in an Abstract Base Class
(ABC), which will simplify concrete implementations. The WEB4J tool defines several such ABC's:
Action

ActionImpl - collects various protected final utility methods needed by


ActionImpl in turn forms a base class for the ActionTemplateXXX classes.
ActionTemplateListAndEdit - shows a listing, and a form to edit items in
ActionTemplateShowAndApply - shows a form for editing a single item
ActionTemplateSearch - shows a form for entering search criteria

many Action classes.


the listing

The various ActionTemplateXXX classes implement the template method design pattern. The application
programmer chooses a particular template based in the desired style of user interface.
Here's an example of a concrete Action, which subclasses the ActionTemplateShowAndApply class
mentioned above. It provides implementations for three methods: show , validateUserInput, and apply .
package hirondelle.fish.main.discussion;
import
import
import
import
import
import
import
import
import
import
import

hirondelle.web4j.BuildImpl;
hirondelle.web4j.action.ActionTemplateShowAndApply;
hirondelle.web4j.action.ResponsePage;
hirondelle.web4j.database.DAOException;
hirondelle.web4j.database.Db;
hirondelle.web4j.database.SqlId;
hirondelle.web4j.model.AppException;
hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelFromRequest;
hirondelle.web4j.request.RequestParameter;
hirondelle.web4j.request.RequestParser;

import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
284

Collected Java Practices

/**
List comments, and add new ones.
<P>Comments are listed, along with a paging mechanism.
@sql statements.sql
@view view.jsp
*/
public final class CommentAction extends ActionTemplateShowAndApply {
public static final SqlId FETCH_RECENT_COMMENTS = new
SqlId("FETCH_RECENT_COMMENTS");
public static final SqlId ADD_COMMENT = new SqlId("ADD_COMMENT");
/** Constructor. */
public CommentAction(RequestParser aRequestParser){
super(FORWARD, REDIRECT, aRequestParser);
}
public static final RequestParameter COMMENT_BODY = RequestParameter.withLengthCheck(
"Comment Body"
);
/** Used for the paging mechanism. */
public static final RequestParameter PAGE_SIZE = RequestParameter.withRegexCheck(
"PageSize", Pattern.compile("(\\d){1,4}")
);
/** Used for the paging mechanism. */
public static final RequestParameter PAGE_INDEX = RequestParameter.withRegexCheck(
"PageIndex", Pattern.compile("(\\d){1,4}")
);
/** Show the listing of comments, and a form for adding new messages. */
protected void show() throws AppException {
addToRequest(ITEMS_FOR_LISTING, fetchRecentComments());
}
/** Ensure user input can build a new {@link Comment}. */
protected void validateUserInput() throws AppException {
try {
ModelFromRequest builder = new ModelFromRequest(getRequestParser());
/*
This is an example of using a time which, for testing purposes,
can be made independent of the true system time. The value of
the 'now' variable depends on the implementation of TimeSource.
*/
long now = BuildImpl.forTimeSource().currentTimeMillis();
fComment = builder.build(
Comment.class, getLoggedInUserName(), COMMENT_BODY, new Date(now)
);
}
catch (ModelCtorException ex){
addError(ex);
}
}
/** Add a new {@link Comment} to the database. */
protected void apply() throws AppException {
//no possibility of a duplicate error.
addNew(fComment);
}
// PRIVATE //
private Comment fComment;
private static final ResponsePage FORWARD = new ResponsePage(
"Discussion", "view.jsp", CommentAction.class
);
private static final ResponsePage REDIRECT = new ResponsePage(
"CommentAction.show?PageIndex=1&PageSize=10"
);
/*
Here, the DAO methods are not in a separate class, but simply regular methods of
this Action. This reasonable since the methods are short, and they do not make
this Action class significantly more complex.
*/

285

Collected Java Practices

/**
Return an immutable {@link List} of recent {@link Comment} objects.
<P>The definition of what constitutes "recent" is left deliberately vague, to
allow various versions of "recent" - last 5 messages, messages entered over
the last N days, etc.
*/
private List<Comment> fetchRecentComments() throws DAOException {
return Db.list(Comment.class, FETCH_RECENT_COMMENTS);
}
/**
Add <tt>aNewComment</tt> to the database. No duplicates are possible.
@param aNewComment to be added to the datastore.
*/
private void addNew(Comment aNewComment) throws DAOException {
Object[] params = {
aNewComment.getUserName(), aNewComment.getBody(), aNewComment.getDate()
};
Db.edit(ADD_COMMENT, params);
}
}

See Also :
Refactor large Controllers
Parse parameters into domain objects
Template method
Forward versus redirect
A Web App Framework - WEB4J

Factory methods
Factory methods are static methods that return an instance of the native class. Examples in the JDK:
LogManager.getLogManager
Pattern. compile
Collections.unmodifiableCollection, Collections.synchronizeCollection , and so on
Calendar.getInstance
Factory methods:
have names, unlike constructors, which can clarify code.
do not need to create a new object upon each invocation - objects can be cached and reused, if
necessary.
can return a subtype of their return type - in particular, can return an object whose implementation
class is unknown to the caller. This is a very valuable and widely used feature in many frameworks
which use interfaces as the return type of static factory methods.
Common names for factory methods include getInstance and valueOf. These names are not mandatory
- choose whatever makes sense for each case.
Example
public final class ComplexNumber {
286

Collected Java Practices

/**
* Static factory method returns an object of this class.
*/
public static ComplexNumber valueOf(float aReal, float aImaginary) {
return new ComplexNumber(aReal, aImaginary);
}
/**
* Caller cannot see this private constructor.
*
* The only way to build a ComplexNumber is by calling the static
* factory method.
*/
private ComplexNumber(float aReal, float aImaginary) {
fReal = aReal;
fImaginary = aImaginary;
}
private float fReal;
private float fImaginary;
//..elided
}

See Also :
Immutable objects
Private constructor
Implementing toString
Data access objects
Avoid clone
Abstract Factory

Immutable objects
Immutable objects are simply objects whose state (the object's data) cannot change after construction.
Examples of immutable objects from the JDK include String and Integer.
Immutable objects greatly simplify your program, since they:
are simple to construct, test, and use
are automatically thread-safe and have no synchronization issues
don't need a copy constructor
don't need an implementation of clone
allow hashCode to use lazy initialization, and to cache its return value
don't need to be copied defensively when used as a field
make good Map keys and Set elements (these objects must not change state while in the collection)
have their class invariant established once upon construction, and it never needs to be checked
again
always have "failure atomicity" (a term used by Joshua Bloch): if an immutable object throws an
exception, it's never left in an undesirable or indeterminate state
Immutable objects have a very compelling list of positive qualities. Without question, they are among the
simplest and most robust kinds of classes you can possibly build. When you create immutable classes,
entire categories of problems simply disappear.
287

Collected Java Practices

Make a class immutable by following these guidelines:


ensure the class cannot be overridden - make the class final , or use static factories and keep
constructors private
make fields private and final
force callers to construct an object completely in a single step, instead of using a no-argument
constructor combined with subsequent calls to setXXX methods (that is, avoid the Java Beans
convention)
do not provide any methods which can change the state of the object in any way - not just setXXX
methods, but any method which can change state
if the class has any mutable object fields, then they must be defensively copied when they pass
between the class and its caller
In Effective Java, Joshua Bloch makes this compelling recommendation :
"Classes should be immutable unless there's a very good reason to make them mutable....If a class
cannot be made immutable, limit its mutability as much as possible."
It's interesting to note that BigDecimal is technically not immutable, since it's not final .
Example
import java.util.Date;
/**
* Planet is an immutable class, since there is no way to change
* its state after construction.
*/
public final class Planet {
public Planet (double aMass, String aName, Date aDateOfDiscovery) {
fMass = aMass;
fName = aName;
//make a private copy of aDateOfDiscovery
//this is the only way to keep the fDateOfDiscovery
//field private, and shields this class from any changes that
//the caller may make to the original aDateOfDiscovery object
fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
}
/**
* Returns a primitive value.
*
* The caller can do whatever they want with the return value, without
* affecting the internals of this class. Why? Because this is a primitive
* value. The caller sees its "own" double that simply has the
* same value as fMass.
*/
public double getMass() {
return fMass;
}
/**
* Returns an immutable object.
*
* The caller gets a direct reference to the internal field. But this is not
* dangerous, since String is immutable and cannot be changed.
*/
public String getName() {
return fName;
}
// /**
// * Returns a mutable object - likely bad style.
// *
// * The caller gets a direct reference to the internal field. This is usually
dangerous,
// * since the Date object state can be changed both by this class and its caller.
// * That is, this class is no longer in complete control of fDate.
288

Collected Java Practices

//
//
//
//

*/
public Date getDateOfDiscovery() {
return fDateOfDiscovery;
}
/**
* Returns a mutable object - good style.
*
* Returns a defensive copy of the field.
* The caller of this method can do anything they want with the
* returned Date object, without affecting the internals of this
* class in any way. Why? Because they do not have a reference to
* fDate. Rather, they are playing with a second Date that initially has the
* same data as fDate.
*/
public Date getDateOfDiscovery() {
return new Date(fDateOfDiscovery.getTime());
}
// PRIVATE
/**
* Final primitive data is always immutable.
*/
private final double fMass;
/**
* An immutable object field. (String objects never change state.)
*/
private final String fName;
/**
* A mutable object field. In this case, the state of this mutable field
* is to be changed only by this class. (In other cases, it makes perfect
* sense to allow the state of a field to be changed outside the native
* class; this is the case when a field acts as a "pointer" to an object
* created elsewhere.)
*/
private final Date fDateOfDiscovery;

Note that javadoc 1.4 includes the -tag option, whereby simple custom tags may be defined. One might
define an @is.Immutable tag, for example, to document a class as being immutable.
You might also consider defining your own tag interface for immutable objects.
See Also :
Validate state with class invariants
Copy constructors
Defensive copying
Factory methods
Use final liberally
Implementing hashCode
Lazy initialization
Document thread safety
Avoid JavaBeans style of construction
Model Objects
Tag or marker interfaces

Lazy initialization
289

Collected Java Practices

Lazy initialization is a performance optimization. It's used when data is deemed to be 'expensive' for some
reason. For example:
if the hashCode value for an object might not actually be needed by its caller, always calculating
the hashCode for all instances of the object may be felt to be unnecessary.
since accessing a file system or network is relatively slow, such operations should be put off until
they are absolutely required.
Lazy initialization has two objectives:
delay an expensive operation until it's absolutely necessary
store the result of that expensive operation, such that you won't need to repeat it again
As usual, the size of any performance gain, if any, is highly dependent on the problem, and in many cases
may not be significant. As with any optimization, this technique should be used only if there is a clear
and significant benefit.
To avoid a NullPointerException , a class must self-encapsulate fields that have lazy initialization. That
is, a class cannot refer directly to such fields, but must access them through a method.
The hashCode method of an immutable Model Object is a common candidate for lazy initialization.
Example 1
In this example, there are two fields with lazy initialization - fHashCode and fAwards.
import java.util.*;
public final class Athlete {
public Athlete(int aId){
//a toy implementation:
fId = aId;
fName = "Roger Bannister";
//fAwards is not set here!
}
//..elided
/**
Lazy initialization is used here; this assumes that awards
may not always be of interest to the caller,
and that for some reason it is particularly expensive to
fetch the List of Awards.
*/
public List<String> getAwards(){
if (fAwards == null) {
//the fAwards field has not yet been populated
//Here is a toy implementation
List<String> awards = new ArrayList<>();
awards.add("Gold Medal 2006");
awards.add("Bronze Medal 1998");
fAwards = awards;
}
return fAwards;
}
/**
This style applies only if the object is immutable.
Another alternative is to calculate the hashCode once, when the
object is initially constructed (again, applies only when object is
immutable).
*/
@Override public int hashCode(){
if (fHashCode == 0) {
fHashCode = HashCodeUtil.SEED;
fHashCode = HashCodeUtil.hash(fHashCode, fId);
290

Collected Java Practices

fHashCode = HashCodeUtil.hash(fHashCode, fName);


//self-encapusulated: fAwards is not referenced directly,
//since it may be null:
fHashCode = HashCodeUtil.hash(fHashCode, getAwards());
}
return fHashCode;
}
// PRIVATE
private int fId;
private String fName;
private List<String> fAwards;
private int fHashCode;
}

Example 2
Here, the look up of the printers available to a desktop PC is treated as an expensive operation.
import java.util.Arrays;
import java.util.List;
import
import
import
import
import
import
import

javax.print.DocFlavor;
javax.print.PrintService;
javax.print.PrintServiceLookup;
javax.print.attribute.HashPrintRequestAttributeSet;
javax.print.attribute.PrintRequestAttributeSet;
javax.print.attribute.standard.OrientationRequested;
javax.print.attribute.standard.Sides;

/** Printing services available to a desktop client. */


public final class Printers {
/** Print some plain text (perhaps internally converted to PDF). */
void printSomething(String aText, PrintService aPrinter) {
//...elided
}
/** Return the list of printers that can print PDFs (double-sided, portrait).*/
List<PrintService> listAvailablePrinters(){
if(fAvailablePrinters == null){
//double-sided, portrait, for PDF files.
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(Sides.DUPLEX);
attrs.add(OrientationRequested.PORTRAIT);
//Expensive operation! This can take several seconds in some environments:
fAvailablePrinters = Arrays.asList(
PrintServiceLookup.lookupPrintServices(DocFlavor.INPUT_STREAM.PDF, attrs)
);
}
return fAvailablePrinters;
}
// PRIVATE
/**
Looked up once, the first time it's needed, and then stored using a
static reference. If it was a non-static reference, then
the list of available printers would not be looked up just once,
but perhaps many times (once per 'Printers' object, and not once per
loaded 'Printers' class).
Self-encapsulate :
If this class's implementation needs to reference this item, it must do
so indirectly, by calling listAvailablePrinters().
*/
private static List<PrintService> fAvailablePrinters;
}

Example 3
291

Collected Java Practices

Lazy initialization is particularly useful for GUIs which take a long time to construct.
There are several policies for GUI construction which a design may follow:
always build - construct the window many times, whenever it is demanded, and do not cache the
result.
first-request build - construct the window once, when first requested. Cache the result for any
further requests, should they occur.
background build - construct the window once, in a low priority worker thread, when the system is
initialized. Cache the result for any requests, should they occur.
Here is an example of the first-request style, in which the fEditor field has lazy initialization (see the
actionPerformed method).
package hirondelle.stocks.preferences;
import
import
import
import

java.awt.event.*;
javax.swing.*;
java.util.*;
java.util.logging.*;

import
import
import
import
import

hirondelle.stocks.util.Args;
hirondelle.stocks.util.ui.StandardEditor;
hirondelle.stocks.util.ui.UiUtil;
hirondelle.stocks.preferences.PreferencesEditor;
hirondelle.stocks.util.Util;

/**
* Present dialog to allow update of user preferences.
*
* <P>Related preferences are grouped together and placed in
* a single pane of a <tt>JTabbedPane</tt>, which corresponds to an
* implementation of {@link PreferencesEditor}. Values are pre-populated with
* current values for preferences.
*
*<P>Most preferences have default values. If so, a
* <tt>Restore Defaults</tt> button is provided for that set of related
* preferences.
*
*<P>Preferences are not changed until the <tt>OK</tt> button is pressed.
* Exception: the logging preferences take effect immediately, without the need
* for hitting <tt>OK</tt>.
*/
public final class EditUserPreferencesAction extends AbstractAction {
/**
* Constructor.
*
* @param aFrame parent window to which this dialog is attached.
* @param aPrefEditors contains implementations of {@link PreferencesEditor},
* each of which is placed in a pane of a <tt>JTabbedPane</tt>.
*/
public EditUserPreferencesAction (JFrame aFrame, List<PreferencesEditor>
aPrefEditors) {
super("Preferences...", UiUtil.getEmptyIcon());
Args.checkForNull(aFrame);
Args.checkForNull(aPrefEditors);
fFrame = aFrame;
putValue(SHORT_DESCRIPTION, "Update user preferences");
putValue(LONG_DESCRIPTION, "Allows user input of preferences.");
putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
fPrefEditors = aPrefEditors;
}
/** Display the user preferences dialog. */
@Override public void actionPerformed(ActionEvent event) {
fLogger.info("Showing user preferences dialog.");
//lazy construction: fEditor is created only once, when this action
//is explicitly invoked
if (fEditor == null) {
fEditor = new Editor("Edit Preferences", fFrame);
}
292

Collected Java Practices

fEditor.showDialog();
}
// PRIVATE
private JFrame fFrame;
private java.util.List<PreferencesEditor> fPrefEditors;
private static final Logger fLogger =
Util.getLogger(EditUserPreferencesAction.class);
/**
* Specifying this as a field allows for "lazy" creation and use of the GUI, which is

* of particular importance for a preferences dialog, since they are usually


heavyweight,
* and have a large number of components.
*/
private Editor fEditor;
/**
* Return GUI for editing all preferences, pre-populated with current
* values.
*/
private JComponent getPrefEditors(){
JTabbedPane content = new JTabbedPane();
content.setTabPlacement(JTabbedPane.LEFT);
int idx = 0;
for(PreferencesEditor prefEditor: fPrefEditors) {
JComponent editorGui = prefEditor.getUI();
editorGui.setBorder(UiUtil.getStandardBorder());
content.addTab(prefEditor.getTitle() , editorGui);
content.setMnemonicAt(idx, prefEditor.getMnemonic());
++idx;
}
return content;
}
/** Called only when the user hits the OK button. */
private void saveSettings(){
fLogger.fine("User selected OK. Updating table preferences.");
for(PreferencesEditor prefEditor: fPrefEditors) {
prefEditor.savePreferences();
}
}
/**
* An example of a nested class which is nested because it is attached only
* to the enclosing class, and it cannot act as superclass since multiple
* inheritance of implementation is not possible.
*
* The implementation of this nested class is kept short by calling methods
* of the enclosing class.
*/
private final class Editor extends StandardEditor {
Editor(String aTitle, JFrame aParent){
super(aTitle, aParent, StandardEditor.CloseAction.HIDE);
}
@Override public JComponent getEditorUI () {
JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
content.add(getPrefEditors());
//content.setMinimumSize(new Dimension(300,300));
return content;
}
@Override public void okAction() {
saveSettings();
dispose();
}
}
}

See Also :

293

Collected Java Practices

Constructors in general
Implementing hashCode
Self-encapsulate fields
Standardized dialogs

Model Objects
The term Model Object is an informal term, with no widely accepted definition. Here, Model Objects
(MOs) refer to data-centric classes which encapsulate closely related items.
Model Objects:
are very common, and are used in almost all applications
are often central to an application, since they usually model problem domain objects
often map roughly to the records of a corresponding database table
are often used as return values for Data Access Object methods
are easily tested using JUnit (or a similar tool)
can be used to implement the Model in a Model-View-Controller pattern
Should Model Objects follow the JavaBeans conventions?
Not necessarily. In fact, some argue that the JavaBeans style is to be avoided as a general model for
Model Objects.
Should Model Objects be immutable?
Given the deep simplicity of immutable objects, some prefer to design their Model Objects as immutable.
However, when the underlying data changes, a new object must be created, instead of simply calling a
setXXX method on an existing object. Some argue that this penalty is too high, while others argue that it
is a micro-optimization - especially in cases where the data is "read-mostly", and the state of
corresponding Model Objects changes only rarely.
Implementing Model Objects as immutable seems particularly natural in web applications. There, Model
Objects are most commonly placed in request scope, not session scope. In this case, there is no longlived object for the user to directly alter, so the Model Object can be immutable.
Example
represents a comment posted to a message board. Its implementation follows the Immutable
Object pattern. Comment provides the usual getXXX methods. Note that, in this case, a defensive copy is
used for the Date field. It also implements the toString , equals, and hashCode methods.
Comment

The constructor is responsible for establishing the class invariant, and performs Model Object validation.
package hirondelle.fish.main.discussion;
import
import
import
import
import
import

java.util.*;
hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Check;
hirondelle.web4j.security.SafeText;
static hirondelle.web4j.util.Consts.FAILS;

/**
Comment posted by a possibly-anonymous user.
*/
public final class Comment {
294

Collected Java Practices

/**
Constructor.
@param aUserName identifies the logged in user posting the comment.
@param aBody the comment, must have content.
@param aDate date and time when the message was posted.
*/
public Comment (
SafeText aUserName, SafeText aBody, Date aDate
) throws ModelCtorException {
fUserName = aUserName;
fBody = aBody;
fDate = aDate.getTime();
validateState();
}
/** Return the logged in user name passed to the constructor. */
public SafeText getUserName() {
return fUserName;
}
/** Return the body of the message passed to the constructor.
public SafeText getBody() {
return fBody;
}

*/

/**
Return a <a href="http://www.javapractices.com/Topic15.cjp">defensive copy</a>
of the date passed to the constructor.
<P>The caller may change the state of the returned value, without affecting
the internals of this <tt>Comment</tt>. Such copying is needed since
a {@link Date} is a mutable object.
*/
public Date getDate() {
// the returned object is independent of fDate
return new Date(fDate);
}
/** Intended for debugging only. */
@Override public String toString() {
return ModelUtil.toStringFor(this);
}
@Override public boolean equals( Object aThat ) {
Boolean result = ModelUtil.quickEquals(this, aThat);
if ( result == null ){
Comment that = (Comment) aThat;
result = ModelUtil.equalsFor(
this.getSignificantFields(), that.getSignificantFields()
);
}
return result;
}
@Override public int hashCode() {
if ( fHashCode == 0 ) {
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE //
private final SafeText fUserName;
private final SafeText fBody;
/** Long is used here instead of Date in order to ensure immutability.*/
private final long fDate;
private int fHashCode;
private Object[] getSignificantFields(){
return new Object[] {fUserName, fBody, new Date(fDate)};
}
private void validateState() throws ModelCtorException {
ModelCtorException ex = new ModelCtorException();
if( FAILS == Check.required(fUserName) ) {
ex.add("User name must have content.");
295

Collected Java Practices

}
if ( FAILS == Check.required(fBody) ) {
ex.add("Comment body must have content.");
}
if ( ! ex.isEmpty() ) throw ex;
}
}

See Also :
Validate state with class invariants
Defensive copying
Immutable objects
Use a testing framework (JUnit)
Data access objects
Avoid JavaBeans style of construction
Use Model-View-Controller framework
A Web App Framework - WEB4J
Validation belongs in a Model Object

Plugin Factory
It's often useful to be able to quickly and easily switch one implementation of a given feature with
another. This is especially useful when writing unit tests for your code, but the technique isn't strictly
limited to unit tests.
A plugin factory is one way of quickly swapping implementations. The general idea is to:
define a Java interface for the methods whose implementation you want to be able to swap.
define 2 or more concrete implementations of that interface.
create a corresponding method in a plugin factory class to return one of those implementations, as
defined by some configuration setting.
Using configuration of some kind (often simply a text file), the plugin factory knows which concrete
implementation it's supposed to return to its caller.
It's important for your application to treat the Plugin Factory as the sole source for implementations of
the corresponding interfaces. That is, the rest of your app is not supposed to have direct knowledge of
the concrete implementations. The Plugin Factory is meant to keep that knowledge secret.
A plugin factory can have a number of methods defined, each returning an implementation of a specific
interface.
A recurring theme in object programming is allowing old code to call new code. A Plugin Factory is
simply another variation on that important theme.
Example
As an example, let's take the idea of a fake system clock. In this case, you want the current time to be
defined centrally, in one place. You also want to be able to swap in various ways of defining the current
296

Collected Java Practices

time, either for testing or for other reasons.


Here's the interface:
package myapp;
/**
Return the time in milliseconds from the Java epoch.
When combined with a TimeZone, such a millisecond value can be
used to create a date-time value.
Variation: one might decide to return a date-time object directly,
instead of a millisecond value.
*/
public interface TimeSource {
long currentTimeMillis();
}

Here's an example of a particular concrete implementation of the above interface:


package hirondelle.jp.util;
public final class TimeSourceOneDayAdvance implements TimeSource {
public long currentTimeMillis() {
return System.currentTimeMillis() + MILLISECS_PER_DAY;
}
private static final int MILLISECS_PER_DAY = 1000*60*60*24;
}

Here's a caller that uses a concrete implementation, without knowing its underlying class:
package myapp;
/**
Use a concrete implementation of an interface, without being linked directly to the
the implementing class.
The concrete implementation is known only to the PluginFactory class.
*/
public final class UseFakeSystemClock {
public void doSomethingThatDependsOnTime(){
TimeSource timesource = PluginFactory.getTimeSource();
long currentTime = timesource.currentTimeMillis();
System.out.println("Current millisecond value: " + currentTime);
}
}

Finally, here's a sketch of the Plugin Factory itself. Note that you can add more methods to this class,
each corresponding to a different interface.
package myapp;
import java.util.LinkedHashMap;
import java.util.Map;
/** Return concrete implementations for specific, known interfaces. */
public final class PluginFactory {
/**
Read in configuration data that maps names of interfaces to names of
297

Collected Java Practices

corresponding concrete implementation classes. Called early upon startup,


before any implementations are needed by the rest of the program.
<P>Example of a possible entry in such a config file :
myapp.TimeSource = myapp.TimeSourceOneDayAdvance
*/
public static void init(){
//elided
//perhaps a properties file is read, perhaps some other source is used
}
/*
* Another variation: allow the caller to swap in different implementation classes
* at runtime, after calling init. This allows testing code to swap in various
* implementations.
*/
/**
Return the concrete implementation of the TimeSource interface.
*/
public static TimeSource getTimeSource() {
String implClassName = fImplementations.get("myapp.TimeSource");
TimeSource result = (TimeSource)buildObject(implClassName);
return result;
}
// PRIVATE
/**
Map the name of an interface to the name of a corresponding concrete
implementation class.
*/
private static final Map<String, String> fImplementations = new LinkedHashMap<>();
private static Object buildObject(String aClassName){
Object result = null;
try {
//note that, with this style, the implementation needs to have a
//no-argument constructor!
Class implClass = Class.forName(aClassName);
result = implClass.newInstance();
}
catch (ClassNotFoundException ex) {
//elided
}
catch (InstantiationException ex) {
//elided
}
catch (IllegalAccessException ex) {
//elided
}
return result;
}
}

See Also :
Construct Object using class name
Use a fake system clock

Private constructor
Private constructors prevent a class from being explicitly instantiated by its callers.
There are some common cases where a private constructor can be useful:
298

Collected Java Practices

classes containing only static utility methods


classes containing only constants
type safe enumerations
singletons
These examples fall into two categories.
Object construction is entirely forbidden
No objects can be constructed, either by the caller or by the native class. This is only suitable for classes
that offer only static members to the caller.
In these cases, the lack of an accessbile constructor says to the caller: "There are no use cases for this
class where you need to build an object. You can only use static items. I am preventing you from even
trying to build an object of this class." See class for constants for an illustration.
If the programmer does not provide a constructor for a class, then the system will always provide a
default, public no-argument constructor. To disable this default constructor, simply add a private noargument constructor to the class. This private constructor may be empty. Somewhat paradoxically,
creation of objects by the caller is in effect disabled by the addition of this private no-argument
constructor.
Object construction is private only
Objects can be constructed, but only internally. For some reason, a class needs to prevent the caller from
creating objects.
In the case of a singleton, the policy is that only one object of that class is supposed to exist. Creation of
multiple objects of that class is forbidden.
In the case of JDK 1.4 type-safe enumerations, more than one object can be created, but again, there is a
limitation. Only a specific set of objects is permitted, one for each element of the enumeration. (For JDK
1.5 enums, the creation of such objects is done implicitly by the Java language, not explicitly by the
application programmer.)
See Also :
Type-Safe Enumerations
Class for constants
Factory methods
Singleton

Singleton
Singleton classes represent objects for which only one single instance should exist.
The important question here is this: will any actual harm come to the system if more than 1 object is
created? If the answer is "no" (and it usually is), then there's no need whatsoever to use a singleton. If
the answer is "yes", then you will need to consider it. The main point is that a singleton should only be
used if it's really necessary.
299

Collected Java Practices

Here's an article from Oracle describing them. This article, along with many others, reports many subtle
problems with singletons. You should likely exercise care when using this pattern.
Example 1
This is the preferred style of implementing singletons. It uses a simple enumeration. It has no special
needs for serialization, and is immune to clever attacks.
/** Preferred style for singletons. */
public enum SantaClaus {
INSTANCE;
/**Add some behavior to the object. */
public void distributePresents(){
//elided
}
/** Demonstrate use of SantaClaus. */
public static void main(String... aArgs){
SantaClaus fatGuy = SantaClaus.INSTANCE;
fatGuy.distributePresents();
//doesn't compile :
//SantaClaus fatGuy = new SantaClaus();
}
}

Example 2
Here's an alternate style. If you decide that the class should no longer be a singleton, you may simply
change the implementation of getInstance.
public final class Universe {
public static Universe getInstance() {
return fINSTANCE;
}
// PRIVATE
/**
* Single instance created upon class loading.
*/
private static final Universe fINSTANCE = new Universe();
/**
* Private constructor prevents construction outside this class.
*/
private Universe() {
//..elided
}
}

Example 3
If the above style of singleton is to be Serializable as well, then you must add a readResolve method.
import java.io.*;
public final class EasterBunny implements Serializable {
public static EasterBunny getInstance() {
return fINSTANCE;
}
// PRIVATE
/**
* Single instance created upon class loading.
300

Collected Java Practices

*/
private static final EasterBunny fINSTANCE =

new EasterBunny();

/**
* Private constructor prevents construction outside this class.
*/
private EasterBunny() {
//..elided
}
/**
* If the singleton implements Serializable, then this
* method must be supplied.
*/
private Object readResolve() throws ObjectStreamException {
return fINSTANCE;
}
}

You might also consider defining your own tag interface for singleton classes.
See Also :
Private constructor
Implementing Serializable
Some classes need readResolve
Tag or marker interfaces

Template method
Template methods:
are used in most abstract base classes
are perhaps the most commonly used of all design patterns
define the general steps of a method, while deferring the implementation of at least one of the steps
to a concrete subclass
Example
is an abstract base class which defines a template method for executing multiple database
operations within a transaction. It's useful to define these steps in one place. The alternative is to repeat
the same structure every time a transaction is required. As usual, such code repetition should always be
aggressively eliminated.

TxTemplate

The executeTx method is the template method. It's final , and defines the general outline of how to
execute a database transaction. The specific database actions to be taken are implemented by calling the
abstract method executeMultipleSqls .
import java.sql.*;
import java.util.logging.*;
import hirondelle.web4j.BuildImpl;
import hirondelle.web4j.util.Util;
import hirondelle.web4j.util.Consts;
/**
* Template for executing a local, non-distributed transaction versus a
* single database, using a single connection.
*
301

Collected Java Practices

* <P>This abstract base class implements the template method design pattern.
*/
public abstract class TxTemplate implements Tx {
//..elided
/**
* <b>Template</b> method calls the abstract method {@link #executeMultipleSqls}.
* <P>Returns the same value as <tt>executeMultipleSqls</tt>.
*
* <P>A <tt>rollback</tt> is performed if <tt>executeMultipleSqls</tt> fails.
*/
public final int executeTx() throws DAOException {
int result = 0;
fLogger.fine(
"Editing within a local transaction, with isolation level : " +
fTxIsolationLevel
);
ConnectionSource connSource = BuildImpl.forConnectionSource();
if(Util.textHasContent(fDatabaseName)){
fConnection = connSource.getConnection(fDatabaseName);
}
else {
fConnection = connSource.getConnection();
}
try {
TxIsolationLevel.set(fTxIsolationLevel, fConnection);
startTx();
result = executeMultipleSqls(fConnection);
endTx(result);
}
catch(SQLException rootCause){
fLogger.fine("Transaction throws SQLException.");
rollbackTx();
String message =
"Cannot execute edit. ErrorId code : " + rootCause.getErrorCode() +
Consts.SPACE + rootCause
;
if (rootCause.getErrorCode() ==
DbConfig.getErrorCodeForDuplicateKey().intValue()){
throw new DuplicateException(message, rootCause);
}
throw new DAOException(message, rootCause);
}
catch (DAOException ex){
fLogger.fine("Transaction throws DAOException.");
rollbackTx();
throw ex;
}
finally {
DbUtil.logWarnings(fConnection);
DbUtil.close(fConnection);
}
fLogger.fine("Total number of edited records: " + result);
return result;
}
/**
* Execute multiple SQL operations in a single local transaction.
*
* <P>This method returns the number of records edited.
*/
public abstract int executeMultipleSqls(
Connection aConnection
) throws SQLException, DAOException;
// PRIVATE
private Connection fConnection;
private String fDatabaseName;
private final TxIsolationLevel fTxIsolationLevel;
private static final boolean fOFF = false;
private static final boolean fON = true;
private static final Logger fLogger = Util.getLogger(TxTemplate.class);
private void startTx() throws SQLException {
302

Collected Java Practices

fConnection.setAutoCommit(fOFF);
}
private void endTx(int aNumEdits) throws SQLException, DAOException {
if ( BUSINESS_RULE_FAILURE == aNumEdits ) {
fLogger.severe("Business rule failure occured. Cannot commit transaction.");
rollbackTx();
}
else {
fLogger.fine("Commiting transaction.");
fConnection.commit();
fConnection.setAutoCommit(fON);
}
}
private void rollbackTx() throws DAOException {
fLogger.severe("ROLLING BACK TRANSACTION.");
try {
fConnection.rollback();
}
catch(SQLException ex){
throw new DAOException("Cannot rollback transaction", ex);
}
}
}

Here's an example of using TxTemplate . It alters the set of roles attached to an end user, first by deleting
all existing roles, and then by adding the new roles one at a time.
final class RoleDAO {
//..elided
/**
* Update all roles attached to a user.
*
* <P>This implementation will treat all edits to user roles as
* '<tt>DELETE-ALL</tt>, then <tt>ADD-ALL</tt>' operations.
*/
boolean change(UserRole aUserRole) throws DAOException {
Tx update = new UpdateTransaction(aUserRole);
return Util.isSuccess(update.executeTx());
}
// PRIVATE //
/** Cannot be a {@link hirondelle.web4j.database.TxSimple}, since there is looping.
*/
private static final class UpdateTransaction extends TxTemplate {
UpdateTransaction(UserRole aUserRole){
super(ConnectionSrc.ACCESS_CONTROL);
fUserRole = aUserRole;
}
public int executeMultipleSqls(
Connection aConnection
) throws SQLException, DAOException {
int result = 0;
//perform edits using a shared connection
result = result + DbTx.edit(aConnection, ROLES_DELETE, fUserRole.getUserName());
for(Id roleId : fUserRole.getRoles()){
result = result +
DbTx.edit(aConnection,ROLES_ADD,fUserRole.getUserName(),roleId);
}
return result;
}
private UserRole fUserRole;
}
}

303

Collected Java Practices

See Also :
Consider composition instead of subclassing
Command objects
Wrapper (Decorator)

Type-Safe Enumerations
Enumerations are sets of closely related items, for example:
cardinal directions - north, south, east, west
types of novels - mystery, classic, fantasy, romance, science-fiction
flavours of ice cream - chocolate, vanilla, raspberry, maple
Type-safe enumerations (also called "enum types", or simply "enums") were added to the Java language
in JDK 1.5, and represent a special kind of class. If JDK 1.5 is not available, type-safe enumerations can
still be implemented as a regular Java class.
Type-safe enumerations should be used liberally. In particular, they are a robust alternative to the simple
String or int constants used in many older APIs to represent sets of related items.
Reminders:
enums are implicitly final subclasses of java.lang.Enum
if an enum is a member of a class, it's implicitly static
new can never be used with an enum, even within the enum type itself
name and valueOf simply use the text of the enum constants, while toString may be overridden to
provide any content, if desired
for enum constants, equals and == amount to the same thing, and can be used interchangeably
enum constants are implicitly public static final
the order of appearance of enum constants is called their "natural order", and defines the order used
by other items as well : compareTo , iteration order of values , EnumSet, EnumSet.range .
enums have a built-in serialization mechanism, which can't be overridden. The mechanism uses the
name and valueOf methods.
Warning:
As with any class, it's easy to provide methods in an enum type which change the state of an enum
constant. Thus, the term "enum constant" is rather misleading. What is constant is the identity of the
enum element, not its state. Perhaps a better term would have been "enum element" instead of "enum
constant".
Constructors for an enum type should be declared as private. The compiler allows non private declares
for constructors, but this seems misleading to the reader, since new can never be used with enum types.
Here are some simple examples of defining and using enums:
import java.util.EnumSet;
public final class EnumExamples {
public static final void main(String... aArgs){
log("Exercising enumerations...");
304

Collected Java Practices

exerEnumsMiscellaneous();
exerMutableEnum();
exerEnumRange();
exerBitFlags();
exerEnumToStringAndValueOf();
log("Done.");
}
// PRIVATE //
private static void log(Object aText){
System.out.println(String.valueOf(aText));
}
/** Example 1 - simple list of enum constants. */
enum Quark {
/*
* These are called "enum constants".
* An enum type has no instances other than those defined by its
* enum constants. They are implicitly "public static final".
* Each enum constant corresponds to a call to a constructor.
* When no args follow an enum constant, then the no-argument constructor
* is used to create the corresponding object.
*/
UP,
DOWN,
CHARM,
STRANGE,
BOTTOM,
TOP
}
//does not compile, since Quark is "implicitly final":
//private static class Quarky extends Quark {}
/**
* Example 2 - adding a constructor to an enum.
*
* If no constructor is added, then the usual default constructor
* is created by the system, and declarations of the
* enum constants will correspond to calling this default constructor.
*/
public enum Lepton {
//each constant implicity calls a constructor :
ELECTRON(-1, 1.0E-31),
NEUTRINO(0, 0.0);
/*
* This constructor is private.
* Legal to declare a non-private constructor, but not legal
* to use such a constructor outside the enum.
* Can never use "new" with any enum, even inside the enum
* class itself.
*/
private Lepton(int aCharge, double aMass){
//cannot call super ctor here
//calls to "this" ctors allowed
fCharge = aCharge;
fMass = aMass;
}
final int getCharge() {
return fCharge;
}
final double getMass() {
return fMass;
}
private final int fCharge;
private final double fMass;
}
/**
* Example 3 - adding methods to an enum.
*
* Here, "static" may be left out, since enum types which are class
* members are implicitly static.
*/
static enum Direction {
NORTH,
SOUTH,
EAST,
WEST; //note semicolon needed only when extending behavior
305

Collected Java Practices

//overrides and additions go here, below the enum constants


@Override public String toString(){
/*
* Either name() or super.toString() may be called here.
* name() is final, and always returns the exact name as specified in
* declaration; toString() is not final, and is intended for presentation
* to the user. It seems best to call name() here.
*/
return "Direction: " + name();
}
/** An added method. */
public boolean isCold() {
//only NORTH is 'cold'
return this == NORTH;
}
}
/**
* Example 4 - adding a method which changes the state of enum constants.
*/
private enum Flavor {
CHOCOLATE(100),
VANILLA(120),
STRAWBERRY(80);
void setCalories(int aCalories){
//changes the state of the enum 'constant'
fCalories = aCalories;
}
int getCalories(){
return fCalories;
}
private Flavor(int aCalories){
fCalories = aCalories;
}
private int fCalories;
}
/*
* What follows are various methods which exercise the above enums.
*/
private static void exerEnumsMiscellaneous(){
//toString method by default uses the identifier
log("toString: " + Quark.BOTTOM);
//equals and == amount to the same thing
if ( Quark.UP == Quark.UP ) {
log("UP == UP");
}
if ( Quark.UP.equals(Quark.UP) ) {
log("UP.equals(UP)");
}
//compareTo order is defined by order of appearance in the definition of
//the enum
if ( Quark.UP.compareTo(Quark.DOWN) < 0 ) {
//this branch is chosen
log("UP before DOWN");
}
else if ( Quark.UP.compareTo(Quark.DOWN) > 0 ) {
log("DOWN before UP");
}
else {
log("UP same as DOWN");
}
//values() returns Quark[], not List<Quark>
log("Quark values : " + Quark.values());
//the order of values matches the order of appearance :
for ( Quark quark : Quark.values() ){
log("Item in Quark.values() : " + quark);
}
log("toString : " + Direction.NORTH);
if ( Direction.EAST.isCold() ){
306

Collected Java Practices

log("East is cold");
}
else {
log("East is not cold.");
}
log("Electron charge : " + Lepton.ELECTRON.getCharge());
//parsing text into an enum constant :
Lepton lepton = Enum.valueOf(Lepton.class, "ELECTRON");
log("Lepton mass : " + lepton.getMass());
//throws IllegalArgumentException if text is not known to enum type :
try {
Lepton anotherLepton = Enum.valueOf(Lepton.class, "Proton");
}
catch (IllegalArgumentException ex){
log("Proton is not a Lepton.");
}
//More compact style for parsing text:
Lepton thirdLepton = Lepton.valueOf("NEUTRINO");
log("Neutrino charge : " + thirdLepton.getCharge() );
}
private static void exerMutableEnum(){
Flavor.VANILLA.setCalories(75); //change the state of the enum "constant"
log("Calories in Vanilla: " + Flavor.VANILLA.getCalories());
}
private static void exerEnumRange(){
for (Direction direction : EnumSet.range(Direction.NORTH, Direction.SOUTH)){
log("NORTH-SOUTH: " + direction);
}
}
private static void exerBitFlags(){
EnumSet<Direction> directions = EnumSet.of(Direction.EAST, Direction.NORTH);
for(Direction direction : directions) {
log(direction);
}
}
/**
* The valueOf method uses name(), not toString(). There is no need
* to synchronize valueOf with toString.
*/
private static void exerEnumToStringAndValueOf(){
Direction dir = Direction.valueOf("EAST"); //successful
log("Direction toString : " + dir);
dir = Direction.valueOf("Direction: EAST"); //fails
}
}

Here's a simple example of using EnumSet:


import java.util.*;
public class CommonLanguage {
enum Lang {ENGLISH, FRENCH, URDU, JAPANESE}
/** Find the languages in common between two people. */
public static void main(String... aArgs){
EnumSet<Lang> ariane = EnumSet.of(Lang.FRENCH, Lang.ENGLISH);
EnumSet<Lang> noriaki = EnumSet.of(Lang.JAPANESE, Lang.ENGLISH);
log( "Languages in common: " + commonLangsFor(ariane, noriaki) );
}
private static Set<Lang> commonLangsFor(Set<Lang> aThisSet, Set<Lang> aThatSet){
Set<Lang> result = new LinkedHashSet<>();
for(Lang lang: aThisSet){
if( aThatSet.contains(lang) ) {
result.add(lang);
}
}
307

Collected Java Practices

return result;
}
private static void log(Object aMessage){
System.out.println(String.valueOf(aMessage));
}
}

Creating type-safe enumerations in older versions of Java


(This discussion closely follows the techniques described in the first edition of Effective Java.)
Prior to JDK 1.5, type-safe enumerations can be implemented as a regular Java class. They come in
various styles, corresponding to the features they include:
a List of VALUES, for iterating over all values of the enumeration
implementing avalueOf method for parsing text into an enumeration element
implementing Comparable
implementing Serializable
Exporting a List of VALUES is highly recommended. It gives users an Iterator for cycling through all
possible values. The alternative (hard-coding particular values of the enumeration) will break when those
values change or are extended to include new values.
Comparison of two objects belonging to an enumeration is the same in all styles :
the Object.equals method is never overridden
either equals or == can be used to perform comparisons, since they amount to the same thing
Example 1
VerySimpleSuit is a minimal type-safe enumeration. It is almost
Strings or int s, apart from the private, empty constructor.

as simple as defining a set of related

public final class VerySimpleSuit {


/**
* Enumeration
*/
public static
public static
public static
public static

elements are constructed once upon class loading.


final
final
final
final

VerySimpleSuit
VerySimpleSuit
VerySimpleSuit
VerySimpleSuit

CLUBS = new VerySimpleSuit();


DIAMONDS = new VerySimpleSuit();
HEARTS = new VerySimpleSuit();
SPADES = new VerySimpleSuit();

/**
* Private constructor prevents construction outside of this class.
*/
private VerySimpleSuit() {
//empty
}
}

Example 2
is another simple style of implementing a type-safe enumeration, which includes an
implementation of toString :

SimpleSuit

public final class SimpleSuit {

308

Collected Java Practices

/**
* Enumeration
*/
public static
public static
public static
public static

elements are constructed once upon class loading.


final
final
final
final

SimpleSuit
SimpleSuit
SimpleSuit
SimpleSuit

CLUBS = new SimpleSuit ("Clubs");


DIAMONDS = new SimpleSuit ("Diamonds");
HEARTS = new SimpleSuit ("Hearts");
SPADES = new SimpleSuit ("Spades");

public String toString() {


return fName;
}
// PRIVATE
private final String fName;
/**
* Private constructor prevents construction outside of this class.
*/
private SimpleSuit(String aName) {
fName = aName;
}
}

Example 3
Suit

adds these features:


a valueOf method for parsing text into an enumeration element
exports a List of VALUES
implements Comparable

import java.util.*;
public final class Suit implements Comparable {
/**
* Enumeration elements are constructed once upon class loading.
* Order of appearance here determines the order of compareTo.
*/
public static final Suit CLUBS = new Suit ("Clubs");
public static final Suit DIAMONDS = new Suit ("Diamonds");
public static final Suit HEARTS = new Suit ("Hearts");
public static final Suit SPADES = new Suit ("Spades");
public String toString() {
return fName;
}
/**
* Parse text into an element of this enumeration.
*
* @param aText takes one of the values 'Clubs',
* 'Diamonds', 'Hearts', 'Spades'.
*/
public static Suit valueOf(String aText){
Iterator iter = VALUES.iterator();
while (iter.hasNext()) {
Suit suit = (Suit)iter.next();
if ( aText.equals(suit.toString()) ){
return suit;
}
}
//this method is unusual in that IllegalArgumentException is
//possibly thrown not at its beginning, but at its end.
throw new IllegalArgumentException(
"Cannot parse into an element of Suit : '" + aText + "'"
);
}
309

Collected Java Practices

public int compareTo(Object that) {


return fOrdinal - ( (Suit)that ).fOrdinal;
}
private final String fName;
private static int fNextOrdinal = 0;
private final int fOrdinal = fNextOrdinal++;
/**
* Private constructor prevents construction outside of this class.
*/
private Suit(String aName) {
fName = aName;
}
/**
* These two lines are all that's necessary to export a List of VALUES.
*/
private static final Suit[] fValues = {CLUBS, DIAMONDS, HEARTS, SPADES};
//VALUES needs to be located here, otherwise illegal forward reference
public static final List VALUES =
Collections.unmodifiableList(Arrays.asList(fValues));
}

Example 4
The AccountType enumeration:
exports avalueOf method for parsing text into an enumeration element
exports a List of VALUES
implements Comparable
implements Serializable

/**
* The only element which is serialized is an ordinal identifier. Thus,
* any enumeration values added in the future must be constructed AFTER the
* already existing objects, otherwise serialization will be broken.
*/
import java.io.*;
import java.util.*;
public class AccountType implements Serializable, Comparable {
public static final AccountType CASH = new AccountType("Cash");
public static final AccountType MARGIN = new AccountType("Margin");
public static final AccountType RSP = new AccountType("RSP");
//FUTURE VALUES MUST BE CONSTRUCTED HERE, AFTER ALL THE OTHERS
public String toString() {
return fName;
}
/**
* Parse text into an element of this enumeration.
*
* @param takes one of the values 'Cash', 'Margin', 'RSP'.
*/
public static AccountType valueOf(String aText){
Iterator iter = VALUES.iterator();
while (iter.hasNext()){
AccountType account = (AccountType)iter.next();
if ( aText.equals(account.toString()) ){
return account;
}
}
throw new IllegalArgumentException(
"Cannot be parsed into an enum element : '" + aText + "'"
);
310

Collected Java Practices

}
public int compareTo(Object aObject) {
return fOrdinal - ((AccountType)aObject).fOrdinal;
}
// PRIVATE
private transient final String fName;
private static int fNextOrdinal = 1;
private final int fOrdinal = fNextOrdinal++;
private AccountType (String aName) {
fName = aName;
}
//export VALUES with these two items
private static final AccountType[] fValues = {CASH, MARGIN, RSP};
public static final List VALUES =
Collections.unmodifiableList(Arrays.asList(fValues));
//Implement Serializable with these two items
private Object readResolve() throws ObjectStreamException {
return fValues[fOrdinal];
}
private static final long serialVersionUID = 64616131365L;
}

See Also :
Use enums to restrict arguments
Modernize old code

Wrapper (Decorator)
Wrapper (or Decorator) is one of the most important design patterns.
Wrappers are commonly used in:
input-output stream APIs
servlet filters
GUI toolkits (when adding scrollbars or other decorations to components, for example)
The basic idea of a wrapper is to call-forward to an underlying object, while simultaneously allowing for
new code to be executed just before and/or just after the call. Wrappers can be chained together, one
after another. In this way, you can mix-and-match behaviors in various ways.
Note that this changes the behavior of an object, without needing to alter the implementation of a class,
and also without needing to extend a class.
Here's an example implementation of a wrapper design pattern (other variations are also possible):
public interface TransformText {
String render(String aInputText);
}

/** Wrapper (Decorator) design pattern. */


311

Collected Java Practices

public final class WrapperDemo {


public static void main(String... aArgs){
TransformText transformer = new BaseWrapper(new Echo());
show(transformer.render("blah.")); // 'blah.'
transformer = new Capitalize(new Echo());
show(transformer.render("blah.")); // 'BLAH.'
transformer = new RemovePeriods(new Capitalize(new Echo()));
show(transformer.render("blah.")); // 'BLAH'
transformer = new RemovePeriods(new Echo());
show(transformer.render("blah.")); // 'blah'
}
private static void show(String aText){
System.out.println(aText);
}
private static final class Echo implements TransformText{
public String render(String aText) {
return aText;
}
}
/**
This class both implements the interface AND is constructed
with an implementation of the same interface.
*/
private static class BaseWrapper implements TransformText {
BaseWrapper(TransformText aTransformText){
fShowText = aTransformText;
}
/** Template method, calls 'before' and 'after' methods. */
public final String render(String aText) {
String text = before(aText);
text = fShowText.render(text); //call-forward
return after(text);
}
/** This default implementation does nothing.*/
String before(String aText){
return aText;
}
/** This default implementation does nothing.*/
String after(String aText){
return aText;
}
private TransformText fShowText;
}
private static final class Capitalize extends BaseWrapper {
Capitalize(TransformText aTransformText){
super(aTransformText);
}
@Override String before(String aText) {
String result = aText;
if (aText != null){
result = result.toUpperCase();
}
return result;
}
}
private static final class RemovePeriods extends BaseWrapper {
RemovePeriods(TransformText aTransformText){
super(aTransformText);
}
@Override String after(String aText) {
String result = aText;
if (aText != null){
result = result.replace(".", "");
}
return result;
}
}
}

312

Collected Java Practices

The output of this class is:


blah.
BLAH.
BLAH
blah

See Also :
Template method

Avoid basic style errors


Many beginners to Java repeat the same basic style errors. Such style errors don't make your program
incorrect, but they make your program less maintainable. In essence, many beginners write code that
suffers from the same basic underlying defect of not being written with compassion for the reader.
There are in fact two target platforms for all code: the runtime hardware, and the human cerebral cortex.
All code 'runs' in the human brain, in the sense that all code needs to be understood by a human being.
Coding style is always concerned with this second target platform, not with the first.
Some common basic style errors:
classes too long
methods too long
little or no javadoc
no convention for distinguishing between local variables, arguments, and fields
many empty catch blocks that suppress exceptions
inappropriate use of multiple return statements
using exceptions to define regular program flow
excessive use of the instanceof operator
using floating point data to represent money
preferring arrays over collections (especially in JDK 5+)
not ordering class members by scope
Some basic style errors are much more damaging to your code than others. The single most harmful bad
habit is that of excessive length.Classes that are too long are hard to understand. As a guideline, if a class
is more than about 300 lines long, you should likely consider splitting it up into smaller pieces. Similarly,
methods that are more than a single screen are very likely too long, and would almost always benefit
from being split into several methods. It's not uncommon for a useful method to consist only of a single
line of code.
See Also :
Avoid empty catch blocks
Exceptions and control flow
Beware of instanceof operator
Prefer Collections over older classes
Naming conventions
313

Collected Java Practices

Use javadoc liberally


Clarifying method
Multiple return statements
Separate public and private members
Beware of floating point numbers

Avoid raw types


Raw types refer to using a generic type without specifying a type parameter. For example, List is a raw
type, while List<String> is a parameterized type.
When generics were introduced in JDK 1.5, raw types were retained only to maintain backwards
compatibility with older versions of Java. Although using raw types is still possible, they should be
avoided:
they usually require casts
they aren't type safe, and some important kinds of errors will only appear at runtime
they are less expressive, and don't self-document in the same way as parameterized types
Example
import java.util.*;
public final class AvoidRawTypes {
void withRawType(){
//Raw List doesn't self-document,
//doesn't state explicitly what it can contain
List stars = Arrays.asList("Arcturus", "Vega", "Altair");
Iterator iter = stars.iterator();
while(iter.hasNext()) {
String star = (String) iter.next(); //cast needed
log(star);
}
}
void withParameterizedType(){
List<String> stars = Arrays.asList("Spica", "Regulus", "Antares");
for(String star : stars){
log(star);
}
}
private void log(Object aMessage) {
System.out.println(String.valueOf(aMessage));
}
}

See Also :
Modernize old code

Beware of instanceof operator


314

Collected Java Practices

Many hold that the instanceof operator should be used only as a last resort, and that an overridden
method is usually (but not always) a better alternative.
The instanceof operator can be used to call a method based explicitly on the class of some object,
instead of implicitly using an overridden method and polymorphism. Thus, inexperienced programmers
may mistakenly use instanceof where an overidden method is more appropriate.
A common exception to this guideline, however, is the use of instanceof within an equals method.
From Effective C++, by Scott Meyers :
"Anytime you find yourself writing code of the form "if the object is of type T1, then do something, but if
it's of type T2, then do something else," slap yourself.
Here is an example of the type of abuse Scott Meyers is speaking of:
/**
* Naive, incorrect use of instanceof.
*/
public final class BadInstanceOf {
public static void doSomething(Animal aAnimal){
if (aAnimal instanceof Fish){
Fish fish = (Fish)aAnimal;
fish.swim();
}
else if (aAnimal instanceof Spider){
Spider spider = (Spider)aAnimal;
spider.crawl();
}
}
// PRIVATE
private static class Animal {}
private static final class Fish extends Animal {
void swim(){}
}
private static final class Spider extends Animal {
void crawl(){}
}
}

The mistake is corrected by using an overridable method:


/**
* Using polymorphism to call different methods.
* Does not use instanceof.
*/
public final class BadInstanceOfFixed {
public static void main(String... aArgs){
log("Starting...");
Animal animal = new Animal();
doSomething(animal);
//repoint the same 'animal' reference to other objects:
animal = new Fish();
doSomething(animal);
animal = new Spider();
doSomething(animal);
log("Done.");
}
/**
* This method implementation doesn't care at all
* about Fish/Spider. It just knows that it has
315

Collected Java Practices

* been passed an Animal. Different versions of


* 'move' are called, specific to each Animal.
*/
public static void doSomething(Animal aAnimal){
aAnimal.move();
}
// PRIVATE
private static class Animal {
void move(){
log("Move like an animal...");
}
}
private static final class Fish extends Animal {
@Override void move(){
log("Move like a fish...");
}
}
private static final class Spider extends Animal {
@Override void move(){
log("Move like a spider...");
}
}
private static void log(String aMessage){
System.out.println(aMessage);
}
}

An example run of this class:


Starting...
Move like an animal...
Move like a fish...
Move like a spider...
Done.

See Also :
Implementing equals
Avoid basic style errors

Class for constants


Creating a class whose sole job is to define widely-used constants is simple.
Example
package hirondelle.web4j.util;
/**
Collected constants of general utility.
<P>All members of this class are immutable.
<P>(This is an example of
<a href='http://www.javapractices.com/Topic2.cjp'>class for constants</a>.)
*/
public final class Consts {
/** Opposite of {@link #FAILS}. */
public static final boolean PASSES = true;
/** Opposite of {@link #PASSES}. */
public static final boolean FAILS = false;
316

Collected Java Practices

/** Opposite of {@link #FAILURE}. */


public static final boolean SUCCESS = true;
/** Opposite of {@link #SUCCESS}. */
public static final boolean FAILURE = false;
/**
Useful for {@link String} operations, which return an index of <tt>-1</tt> when
an item is not found.
*/
public static final int NOT_FOUND = -1;
/** System property
public static final
/** System property
public static final
/** System property
public static final

- <tt>line.separator</tt>*/
String NEW_LINE = System.getProperty("line.separator");
- <tt>file.separator</tt>*/
String FILE_SEPARATOR = System.getProperty("file.separator");
- <tt>path.separator</tt>*/
String PATH_SEPARATOR = System.getProperty("path.separator");

public
public
public
public
public
public

String
String
String
String
String
String

static
static
static
static
static
static

final
final
final
final
final
final

EMPTY_STRING = "";
SPACE = " ";
TAB = "\t";
SINGLE_QUOTE = "'";
PERIOD = ".";
DOUBLE_QUOTE = "\"";

// PRIVATE //
/**
The caller references the constants using <tt>Consts.EMPTY_STRING</tt>,
and so on. Thus, the caller should be prevented from constructing objects of
this class, by declaring this private constructor.
*/
private Consts(){
//this prevents even the native class from
//calling this ctor as well :
throw new AssertionError();
}
}

The caller refers to these constants using static references, such as Consts.EMPTY_STRING ,
Consts.SUCCESS , and so on. Since the caller will never need to create a Consts object, a private
constructor is used to enforce that policy. The members of such a constants class are excellent candidates
for a static import.
It's also possible to use an interface to bring constants into scope. Many argue strongly that such a
practice is a strange use of interfaces, which are intended to state the services provided by a class.
See Also :
Interface for constants
Private constructor
Use static imports rarely

Construct classes from the outside in


When experimenting with the design of a new class, it's very helpful to approach the task from the point
of view of the caller:
the quality of the final design will be higher, since there is more feedback from use cases
the class is often constructed significantly faster, since discovery of poor design decisions usually
happens much earlier
317

Collected Java Practices

Much of design is summed up simply as:


make your best guess
find and fix the errors
repeat
The critical step seems to be the second. The best techniques are those which discover the errors the
fastest. Coding "from the outside in" seems to perform very well in this regard.
Constructing a non-private method from the outside in might involve these steps:
write some code from the point of view of the caller
make the code compile by providing a simple stub implementation of the method you are designing.
For a void method, such a stub is simply an empty method. For a method which has a return value,
such a stub might simply return null , 0, false , or an empty String.
experimenting with different designs is simple at this point, since the amount of calling code is
minimal, and the implementations are toys
correctness of method name and return type are determined by the needs of the caller
correctness of arguments seems to be determined more by the needs of the implementation, not the
caller
is the return type correct? This is an important item, since changes in return type will often have
large ripple effects in the caller.
is the method intelligible at the point of call?
when the design is more or less stable, write the javadoc of the method as the formal specification
of the contract between the method and its callers. Often, this will also help clarify details and
border cases.
finally, replace the stub implementation with a real implementation, and test it
In effect, such techniques use the programming language itself as a design tool.
See Also :
Use a testing framework (JUnit)
Use javadoc liberally
Separate public and private members
Design by Contract

Do not break portability


Portability across different operating systems is one of the principal advantages of using Java.
Here are some ways to ensure your application remains portable:
don't use hard-coded file names and paths - use the Path class, and allow file locations to be
configured during deployment, or at runtime.
remember that some systems have case-sensitive file names (Unix), while others do not (Windows).
be very wary of Runtime.exec and Method.invoke.
don't rely on thread scheduling and thread priorities to define program logic.
don't use native methods.
don't hard-code sizes and positions of GUI elements - use a LayoutManager instead.
don't rely on a specific screen resolution - use java.awt.Toolkit.getScreenResolution instead.
318

Collected Java Practices

For web apps, CSS media queries can help you adapt to a wide range of screen sizes.
don't hard-code colors using numeric values - use the symbolic constants in java.awt.Color and
java.awt.SystemColor instead.
don't hard-code text sizes.
use the System.getProperty(String) method to refer to items which depend on the system, such
as line terminators and path separators.
Common System.getProperty(String) items can be placed in a general purpose constants class:
/**
* Collected constants of very general utility.
*
* All constants must be immutable.
* No instances of this class can be constructed.
*/
public final class Consts {
/**
* Prevent object construction outside of this class.
*/
private Consts(){
//empty
}
/**
* Only refer to primitives and immutable objects.
*
* Arrays present a problem since arrays are always mutable.
* DO NOT USE public static final array fields.
* One style is to instead use an unmodifiable List, built in a
* static initializer block.
*
* Another style is to use a private array and wrap it up like so:
* <pre>
* private static final Vehicle[] PRIVATE_VEHICLES = {...};
* public static final List VEHICLES =
*
Collections.unmodifiableList(Arrays.asList(PRIVATE_VEHICLES));
* </pre>
*/
//characters
public static final String NEW_LINE = System.getProperty("line.separator");
public static final String FILE_SEPARATOR = System.getProperty("file.separator");
public static final String PATH_SEPARATOR = System.getProperty("path.separator");
public
public
public
public

static
static
static
static

final
final
final
final

//algebraic signs
public static final
public static final
public static final
public static final

String
String
String
String

EMPTY_STRING = "";
SPACE = " ";
PERIOD = ".";
TAB = "\t";

int POSITIVE = 1;
int NEGATIVE = -1;
String PLUS_SIGN = "+";
String NEGATIVE_SIGN = "-";

See Also :
Class for constants
Thread priorities are not portable

Don't declare local variables before use


319

Collected Java Practices

Most local variables are declared and initialized on the same line, at the point in the method where both
its initial value is available and the variable itself is immediately useful.
Declaring local variables without using them immediately may unnecessarily increase their scope. This
decreases legibility, and increases the likelihood of error.
There are two common cases where a local variable is assigned some default initial value (typically
null, 0, false, or an empty String):
variables which need to be visible outside of a try block, and are thus declared and initialized just
before the try block (in modern code using try-with-resources, this is now relatively rare)
some loop variables, which are initialized to some default value just before the loop
Example
Here, input and output are examples of local variables being initialized to null , since they need to be
visible in both the try and finally blocks.
As well, line is an example of a loop variable declared and initialized outside the loop.
Note that this example uses JDK 6, simply to illustrate the point. In JDK 7, try-with-resources would be
used to automatically close streams, and the issue of stream references being initialized to null would not
occur.
import java.io.*;
/** JDK 6 or before. */
public class ReadWriteTextFile {
/**
* Fetch the entire contents of a text file, and return it in a String.
* This style of implementation does not throw Exceptions to the caller.
*
* @param aFile is a file which already exists and can be read.
*/
static public String getContents(File aFile) {
//...checks on aFile are elided
StringBuilder contents = new StringBuilder();
try {
//use buffering, reading one line at a time
//FileReader always assumes default encoding is OK!
BufferedReader input = new BufferedReader(new FileReader(aFile));
try {
String line = null; //not declared within while loop
/*
* readLine is a bit quirky :
* it returns the content of a line MINUS the newline.
* it returns null only for the END of the stream.
* it returns an empty String if two newlines appear in a row.
*/
while (( line = input.readLine()) != null){
contents.append(line);
contents.append(System.getProperty("line.separator"));
}
}
finally {
input.close();
}
}
catch (IOException ex){
ex.printStackTrace();
}
return contents.toString();
}

320

Collected Java Practices

/**
* Change the contents of text file in its entirety, overwriting any
* existing text.
*
* This style of implementation throws all exceptions to the caller.
*
* @param aFile is an existing file which can be written to.
* @throws IllegalArgumentException if param does not comply.
* @throws FileNotFoundException if the file does not exist.
* @throws IOException if problem encountered during write.
*/
static public void setContents(File aFile, String aContents)
throws FileNotFoundException, IOException {
if (aFile == null) {
throw new IllegalArgumentException("File should not be null.");
}
if (!aFile.exists()) {
throw new FileNotFoundException ("File does not exist: " + aFile);
}
if (!aFile.isFile()) {
throw new IllegalArgumentException("Should not be a directory: " + aFile);
}
if (!aFile.canWrite()) {
throw new IllegalArgumentException("File cannot be written: " + aFile);
}
//use buffering
Writer output = new BufferedWriter(new FileWriter(aFile));
try {
//FileWriter always assumes default encoding is OK!
output.write( aContents );
}
finally {
output.close();
}
}
/** Simple test harness.
*/
public static void main (String... aArguments) throws IOException {
File testFile = new File("C:\\Temp\\blah.txt");
System.out.println("Original file contents: " + getContents(testFile));
setContents(testFile, "The content of this file has been overwritten...");
System.out.println("New file contents: " + getContents(testFile));
}
}

See Also :
Initializing fields to 0-false-null is redundant

Fields should usually be private


Fields should be declared private unless there is a good reason for not doing so.
One of the guiding principles of lasting value in programming is "Minimize ripple effects by keeping
secrets." When a field is private, the caller cannot usually get inappropriate direct access to the field.
To clarify, there are three possibilities for a private field:
primitive data such as int , boolean, and so on.
immutable objects such as String and BigDecimal , whose data never changes.
mutable objects, whose data can change (that is, they can change state). These need special care,
and often need a defensive copy. It's easy to forget this. You should always remember that mutable
321

Collected Java Practices

object fields are 'hot spots' in your code, and need special care.
It's only the last case - the mutable object private field - where inappropriate direct access to a private
field is possible. Here's an illustration:
Example 1
import java.util.Date;
/**
* Planet is an immutable class, since there is no way to change
* its state after construction.
*/
public final class Planet {
public Planet (double aMass, String aName, Date aDateOfDiscovery) {
fMass = aMass;
fName = aName;
//make a private copy of aDateOfDiscovery
//this is the only way to keep the fDateOfDiscovery
//field private, and shields this class from any changes that
//the caller may make to the original aDateOfDiscovery object
fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
}
/**
* Returns a primitive value.
*
* The caller can do whatever they want with the return value, without
* affecting the internals of this class. Why? Because this is a primitive
* value. The caller sees its "own" double that simply has the
* same value as fMass.
*/
public double getMass() {
return fMass;
}
/**
* Returns an immutable object.
*
* The caller gets a direct reference to the internal field. But this is not
* dangerous, since String is immutable and cannot be changed.
*/
public String getName() {
return fName;
}
// /**
// * Returns a mutable object - likely bad style.
// *
// * The caller gets a direct reference to the internal field. This is usually
dangerous,
// * since the Date object state can be changed both by this class and its caller.
// * That is, this class is no longer in complete control of fDate.
// */
// public Date getDateOfDiscovery() {
//
return fDateOfDiscovery;
// }
/**
* Returns a mutable object - good style.
*
* Returns a defensive copy of the field.
* The caller of this method can do anything they want with the
* returned Date object, without affecting the internals of this
* class in any way. Why? Because they do not have a reference to
* fDate. Rather, they are playing with a second Date that initially has the
* same data as fDate.
*/
public Date getDateOfDiscovery() {
return new Date(fDateOfDiscovery.getTime());
}
// PRIVATE
/**
* Final primitive data is always immutable.
322

Collected Java Practices

*/
private final double fMass;
/**
* An immutable object field. (String objects never change state.)
*/
private final String fName;
/**
* A mutable object field. In this case, the state of this mutable field
* is to be changed only by this class. (In other cases, it makes perfect
* sense to allow the state of a field to be changed outside the native
* class; this is the case when a field acts as a "pointer" to an object
* created elsewhere.)
*/
private final Date fDateOfDiscovery;
}

The above class uses a defensive copy as part of its design, but there are cases in which defensive copies
aren't desired. The point is that for mutable fields, you need to know the difference, and make the
appropriate choice.
Example 2
This is a counter-example.
A common exception to this rule is that primitive constants and immutable objects can be declared as
public static final fields. For example, see class for constants.
See Also :
Defensive copying
Immutable objects
Minimize ripple effects

Interface for constants


It's possible to place widely used constants in an interface. If a class implements such an interface, then
the class can refer to those constants without a qualifying class name. This is only a minor advantage.
The static import feature should always be considered as a replacement for this practice.
Placing constants in an interface was a popular technique in the early days of Java, but now many
consider it a distasteful use of interfaces, since interfaces should deal with the services provided by an
object, not its data. As well, the constants used by a class are typically an implementation detail, but
placing them in an interface promotes them to the public API of the class.
Example
interface OlympicMedal {
static final String GOLD = "Gold";
static final String SILVER = "Silver";
static final String BRONZE = "Bronze";
}

Here's an example of using this interface to reference its constants:


323

Collected Java Practices

public final class OlympicAthlete implements OlympicMedal {


public OlympicAthlete(int aId){
//..elided
}
//..elided
public void winEvent(){
//the athlete gets a gold medal
//note the reference is NOT qualified, as
//in OlympicMedal.GOLD
fMedal = GOLD;
}
// PRIVATE
private String fMedal; //possibly null
}

See Also :
Type-Safe Enumerations
Class for constants
Use static imports rarely

Know the core libraries


Java differs from most other languages in that the number of classes and interfaces in its standard
libraries is very large. Many common tasks have already been implemented by these libraries.
Advice to beginners might include:
don't be too intimidated by the large number of classes. Some are used often, but most are used
relatively rarely.
the most widely used packages are java.lang and java.util .
for working with data, see java.sql , javax.sql , java.io, and java.nio.file .
for graphical applications, see the Swing classes (javax.swing, and so on).
for server applications, see the servlet and Java Server Page APIs.
for other packages, you should skim through their documentation just to get an idea of what is
available. Later, when a specific need arises, you will often know which packages might be helpful.
As well, the standard JDK libraries:
are generally of high quality
often improve their performance over time
are widely used, and usually form defacto standards
Implementing something which already exists in the libraries is probably wasted effort.
Significant changes and additions to the standard libraries occur in each major release, and it pays to
keep current.
The Java 7 release includes:

324

Collected Java Practices

type inference
try-with-resources
java.nio.file - new ways to interact with the file system
multi-catch - catching multiple exceptions at once
JavaFX
binary literals
The Java 6 release includes:
scripting
the JConsole monitoring tool
various Swing improvements
The Java 5 release includes:
generics
an enhanced for loop
enums
autoboxing
and annotations
The Java 1.4 release includes:
regular expressions
assertions
logging services

See Also :
Modernize old code

Minimize ripple effects


Much of object programming is centered on minimizing the ripple effects caused by changes to a
program. This is done simply by keeping details secret (information hiding or encapsulation).
The principal ways of doing this are:
indirection - named constants replacing "magic numbers", for example
minimizing visibility - private fields, package-private classes, for example
generic references (polymorphism) - using high level references (interfaces or abstract classes)
instead of low level references (concrete classes)
All of these techniques accomplish the same thing - they confine knowledge of implementation details to
the smallest possible part of a program. That is, they keep a secret of some sort.
Constant and liberal use of the above techniques is recommended.
An interesting quote from chapter one of Design Patterns, regarding the use of generic references:

325

Collected Java Practices

"This so greatly reduces implementation dependencies between subsystems that it leads to the following
principle of reusable object-oriented design :
Program to an interface, not an implementation.
Don't declare variables to be instances of particular concrete classes. Instead, commit only to an
interface defined by an abstract class. You will find this to be a common theme of the design patterns in
this book."
(They state a second general principle as well: "Favor object composition over class inheritance.")
See Also :
Fields should usually be private
Use interface references to Collections
Reading and writing text files
Data access objects
Consider composition instead of subclassing
Abstract Factory
Parse parameters into domain objects

Naming conventions
It's common to use a variable naming convention to distinguish between fields, arguments, and local
variables.
Within the body of a method, all of these types of variables can appear. Many find naming conventions to
be a helpful aid in getting a rapid understanding of a method's implementation. These conventions have
only one purpose: to pour understanding into the brain of the reader as quickly as possible.
To understand a method, you need to understand its data. An important part of understanding data is
understanding where it's defined - as a field, argument, or local variable. Naming conventions which
distinguish these cases usually allow the reader to understand an implementation more quickly, since they
no longer need to scan the class to determine where items are defined.
Some different styles are:
field: fBlah, _blah, blah_, this.blah, m_blah, myBlah
argument: aBlahBlah, pBlah
local variable: blahBlah
constant: BLAH_BLAH
As well, some use a naming convention for type names, to distinguish between interfaces and regular
classes:
interface: IBlahBlah
class: BlahBlah, CBlahBlah
Note that this 'I' and 'C' convention is fundamentally different from the other conventions mentioned
above. This convention is visible to the caller, while the other naming conventions, being confined to the
implementation, are not.

326

Collected Java Practices

The example code on this site usually follows these conventions:


field: fBlahBlah
argument: aBlahBlah
local variable: blahBlah
constant: BLAH_BLAH
class: BlahBlah
interface: BlahBlah (same as class)
The return value of a method is sometimes given a conventional name as well.
See also Oracle's remarks on naming conventions in general.
See Also :
Conventional name for return value
Coding conventions
Avoid basic style errors

Nest classes with care


In Java, you can nest classes within other classes. This feature is very useful when you need it, but it can
be easily abused.
When you have a choice, you should declare your nested classes as static. The reason is that non-static
nested classes (inner classes) are tightly coupled to the enclosing parent, while static nested classes aren't.
This extra coupling can sometimes cause problems. In general, static nested classes are safer than nonstatic ones, and less prone to error.
Secondly, you should very rarely use deep levels of nesting.
The terminology for nested classes is a bit confusing. Here's a reminder of the hierarchy:
nested class
static
inner class (non-static)
regular, not within a method or constructor
inside a method or constructor
anonymous
local

Some more reminders:


static - not tied to a parent instance (but can see the private fields of a parent, if it has an object
reference)
non-static (inner class) - always tied to a parent instance
anonymous - no access modifiers; no extends or implements ; no constructor
local - never static; no access modifiers; can access only final items that are in scope; this is the
rarest form of nested class.
Here's a class which sketches all the different types of nested class:
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
java.util.Timer;
327

Collected Java Practices

import
import java.util.TimerTask;
public final class MakeMovie {
/**
Static, since has no need of being attached to a parent instance.
*/
static final class Actor {
Actor(String aName){
fName = aName;
}
String getName(){
return fName;
}
private String fName;
}
/**
Non-static (inner) class, since casting will need to know about the
movie's data - its name, director, and budget.
*/
final class Casting {
void chooseFrom(List<Actor> aActors){
//can reference the parent's data directly:
if(fDirector.equals("Stanley Kubrick")){
//elided...
}
}
List<Actor> getSelectedCast(){
return fSelectedCast;
}
//static void doThis(){} //does not compile, since static
private List<Actor> fSelectedCast;
}
void wrapParty(){
Timer timer = new Timer();
//anonymous class - the implementation of TimerTask
//this is usually how anonymous classes are used - to define a single method
timer.schedule(
new TimerTask(){
@Override public void run() {
//elided...
};
},
new Date()
);
}
void shootScene(final int aSceneNumber){
//local class - this style seems to be rather rare
class Camera {
void shoot(){
//won't compile unless aSceneNumber is final:
System.out.println(aSceneNumber);
}
}
Camera camera = new Camera();
camera.shoot();
}
//elided...
private String fName;
private String fDirector;
private BigDecimal fBudget;
}

Output parameters
328

Collected Java Practices

A method may occasionally need to use a parameter for its return value - what might be loosely called an
"output parameter" or a "result parameter". The caller creates an output parameter object, and then passes
it to a method which changes the state of the object (its data). When the method returns, the caller then
examines this new state.
Output parameters:
must be visible in both a method and its caller
cannot be null , since the object needs to be created by the caller
cannot be immutable objects - a String, for example, cannot be used as an output parameter, since
it cannot change state
should probably be used only occasionally, and after careful consideration
Example
Here, there's both an output parameter ( aCookieValue) and a boolean return value: if the output
parameter is indeed populated, then the return value is set to true .
import javax.servlet.http.*;
public final class CookieValue {
/**
* Determine if a user's request contains a particular cookie, and if it does
* then place its value in an "out" parameter.
*
* Example
* <pre>
* if ( CookieValue.getCookieValue("ID", aRequest, aIdValue) ) {
*
//use aIdValue
* }
* </pre>
*
* @param aCookieName the non-null name attached to the Cookie.
* @param aRequest is the non-null request forwarded from the user's browser which
* is to be examined for the presence of the given cookie.
* @param aCookieValue is a non-null "out" parameter in which the
* cookie's value is placed - if found - for further examination by the
* caller; it must have a zero length.
* @throws IllegalArgumentException if aCookieValue.length() > 0.
*/
static boolean getCookieValue(
String aCookieName, HttpServletRequest aRequest, final StringBuilder aCookieValue
){
if (aCookieValue.length()>0) throw new IllegalArgumentException();
boolean userHasCookie = false;
Cookie[] cookies = aRequest.getCookies();
if (cookies != null){
for(Cookie cookie : cookies){
if (aCookieName.equals(cookie.getName())){
userHasCookie = true;
//change the state of the output param (aCookieValue)
aCookieValue.append(cookie.getValue());
}
}
}
return userHasCookie;
}
}

An alternative design is to return a null value if the cookie is not actually present. This would replace
the current boolean return value with a String return value which should be checked for null .
However, that alternative design seems weaker, since it seems prudent to avoid using null return values.

329

Collected Java Practices

See Also :
Immutable objects
Avoid null if possible

Separate public and private members


It's a common practice to separate the members of a class according to their scope (private, packageprivate, protected, public).
It's a matter of some debate, however, which should come first.
Private-first style:
is advocated by Oracle's coding conventions.
is significantly more popular among Java programmers, and is thus expected by many readers.
emphasizes implementation details over exported API.
Public-first style (or, more precisely, largest-scope-first):
it agrees with the idea that the exported API is an order of magnitude more important than the
implementation - if the exported API should almost always be read first, then clearly it should
appear first.
it agrees with Steve McConnell's Fundamental Theorem of Formatting: "good visual layout shows
the logical structure of a program". Here, placing public members first puts a clear visual emphass
on the exported API, and relegates implementation details to the bottom.
Joshua Bloch once stated that the Oracle coding conventions are not used or maintained by Oracle,
and shouldn't be taken too seriously.
public-first is popular among C++ programmers; for instance, the popular books Design Patterns
and Effective C++ always follow the public-first style. Over time, the C++ community has
apparently come to favor this style as more effective. Is the Java community ignoring a lesson
already learned elsewhere?

See Also :
Construct classes from the outside in
Avoid basic style errors

String concatenation does not scale


To build Strings dynamically, one may use either the String concatenation operator + or the
StringBuilder class. ( StringBuilder is generally preferred over StringBuffer , since it avoids internal
synchronization costs.) In the great majority of cases, only a few items are concatenated, and either style
may be used freely, according to taste, without concern for performance.
On relatively rare occasions, however, when performing extensive String manipulation, replacing + with
StringBuilder.append is likely recommended. Compilers often implement concatenation operations by
creating intermediate objects in the background. Such intermediate objects are not needed by
StringBuilder .
330

Collected Java Practices

Cases in which + is very likely acceptable:


if concatenating only a small number of items together, then the difference in relative performance
is very small, and possibly not even measurable (the great majority of use cases of the
concatenation operator fall into this category).
in branches of code which represent a failure in the system - for example, a lost database
connection, or an invalid method parameter. Since these branches are very rarely exercised, the
speed at which the system fails is usually not important.
Here's an illustration of the difference in execution speed between + and StringBuilder.append , when
performing many concatenations. Clearly, even the + operator is quite fast, and the difference is not
going to be noticeable in the great majority of cases encountered in practice.
Example runs (class appears below):
Here, -Xint was used to turn off the HotSpot compiler. This ensures the program is always interpreted,
and will execute in a uniform environment from start to finish, without any compilation into native code.)
Num iterations: 10
Task using + operator:
175,351 nanoseconds
Task using StringBuilder.append: 35,057 nanoseconds
Num iterations: 1000
Task using + operator:
18,295,297 nanoseconds
Task using StringBuilder.append: 1,200,376 nanoseconds
Num iterations: 10000
Task using + operator:
1,134,679,975 nanoseconds
Task using StringBuilder.append: 8,757,343 nanoseconds

/**
* Illustrates the speed difference between + operator and
* StringBuilder.append, when performing many concatenations.
*/
public final class AvoidConcatenation {
/**
* Takes a single argument : the number of iterations to perform.
*/
public static void main (String... arguments) {
fNumIterations = Integer.parseInt(arguments[0]);
long start = System.nanoTime();
doWithConcatenationOperator();
long finish = System.nanoTime();
System.out.println("Num iterations: " + fNumIterations);
StringBuilder message = new StringBuilder();
message.append("Task using + operator: ");
message.append( finish - start );
message.append(" nanoseconds");
System.out.println(message);
start = System.nanoTime();
doWithStringBuilder();
finish = System.nanoTime();
message = new StringBuilder("Task using StringBuilder.append: ");
message.append( finish - start );
message.append(" nanoseconds");
System.out.println(message);
}
// PRIVATE
private static int fNumIterations;
private static String doWithConcatenationOperator() {
String result = "start";
331

Collected Java Practices

for (int idx = 0; idx < fNumIterations; idx++) {


result = result + "blah";
}
return result;
}
private static String doWithStringBuilder() {
StringBuilder result = new StringBuilder("start");
for (int idx = 0; idx < fNumIterations; idx++) {
result.append("blah");
}
return result.toString();
}
}

See Also :
Implementing toString
Time execution speed

Tag or marker interfaces


A tag interface (also called a marker interface) is simply an interface with no methods. Some examples
of tag interfaces from the JDK:
Serializable
EventListener

Why define an interface with no methods? Since there are no methods, a tag interface can never define
behavior, at least not in the typical sense. However, even though it has no methods, a tag interface always
carries type information. In some cases, type information itself can be used to solve a problem.
For example, Java's serialization mechanism requires an object to implement Serializable before it will
serialize it. As stated in its javadoc:
The serialization interface has no methods or fields and serves only to identify the semantics of being
serializable.
Tools will often use instanceof or reflection to inspect objects, to see if they implement a given tag
interface. But this isn't the only way a tag interface can be useful. There are some common cases for
which you might consider defining your own tag interface, even without any corresponding use of
instanceof or reflection:
immutable objects (or immutable classes, if you prefer)
singleton classes
The advantages of doing so are:
it emphasizes important, high-level aspects of a class, not otherwise expressed in code.
for both human readers and tools, it allows quick identification of classes having specific
properties.
the javadoc of the tag interface is a natural home for documenting all the characteristics of items
that implement the given interface.
332

Collected Java Practices

See Also :
Immutable objects
Singleton

Uncommon classes need explicit imports


Widely used classes, such as those in java.io and java.util , are well known to the typical Java
programmer. When importing such classes, it's reasonable to use an abbreviated style:
import java.io.*;
import java.util.*;

When importing classes that are not among the most widely used, one should probably use another style,
in which each class has an explicit import statement. If a class is unfamiliar to a reader, an explicit
import makes it easier to find related documentation.
Example
For example, if a class imports items from the Apache Commons FileUpload tool, then instead of :
import org.apache.commons.fileupload.*;

one should likely use the more explicit style :


import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.FileItem;
...etc...

See Also :
Use static imports rarely

Use @Override liberally


The @Override annotation was added in JDK 1.5. It functions more or less as a new method modifier. Its
presence indicates to the compiler that the annotated method must override an existing supertype method,
either from an interface, or an abstract base class.
(When it was first introduced in JDK 1.5, @Override was only applicable to methods defined in a
superclass, and not in a supertype. That is, it originally applied to overriding methods in a base class, but
not to implementing methods defined in an interface. This was changed in JDK 7, and now @Override
can be applied to both.)
The most common use case for @Override is with Object methods:
@Override public String toString(){...}
@Override public boolean equals(Object){...}
333

Collected Java Practices

@Override public int hashCode(){...}

The main reason @Override was created was to deal with simple (but nasty) typographical errors. For
example, a method mistakenly declared as
public int hashcode(){...}

is in fact not an override - the method name has all lower case letters, so it doesn't exactly match the
name of the hashCode() method. It will however compile perfectly well. Such an error is easy to make,
and difficult to catch, which is a dangerous combination. Using the @Override annotation prevents you
from making such errors.
You should be in the habit of using @Override whenever you override a superclass method, or implement
an interface method.
See Also :
Overridable methods need special care
Modernize old code

Use final liberally


Use the final keyword liberally to communicate your intent.
The final keyword has more than one meaning:
a final class cannot be extended
a final method cannot be overridden
final fields, parameters, and local variables cannot change their value once set
In the last case, "value" for primitives is understood in the usual sense, while "value" for objects means
the object's identity, not its state. Once the identity of a final object reference is set, it can still change
its state, but not its identity (that is, you can't re-point the object reference to some other object).
Declaring primitive fields as final automatically ensures thread-safety for that field.
Some habitually declare parameters as final , since this is almost always the desired behaviour. Others
find this verbose, and of little real benefit.
Consistently using final with local variables (when appropriate) can be useful as well. It brings attention
to the non- final local variables, which usually have more logic associated with them (for example,
result variables, accumulators, loop variables). Many find this verbose. A reasonable approach is to
occasionally use final for local variables, but only if there is some unusual condition, whereby making
final explicit can call attention to at least one non- final local variable in the method; this serves to
quickly distinguish the non- final local variables from the others.
Using final :
clearly communicates your intent
allows the compiler and virtual machine to perform minor optimizations
clearly flags items which are simpler in behaviour - final says, "If you are looking for complexity,
you won't find it here."

334

Collected Java Practices

Example
import java.util.*;
import java.lang.reflect.Field;
/** This class cannot be extended, since it's final. */
public final class Boat {
public Boat(final String aName, final int aLength, final Date aDateManufactured){
fName = aName;
fLength = aLength;
//make a defensive copy of the date
fDateManufactured = new Date(aDateManufactured.getTime());
//does not compile, since the items are final:
//aDateManufactured = null;
//aLength = 0;
}
/** Cannot be overridden, since the class itself is final. */
public void setDate(final Date aNewDate){
//even though the field is final, its state can change:
fDateManufactured.setTime(aNewDate.getTime());
//does not compile, since field is final:
//fDateManufactured = aNewDate;
}
/** Return the highest race score. */
public Integer bestRaceScore(){
//the result reference can't be final, since it can be
//re-pointed to different objects
Integer result = Integer.valueOf(0);
//final Integer result = Integer.valueOf(0); //doesn't compile
//this example is artificial, since fRaceScores could be
//referenced directly here...
final List<Integer> scores = fRaceScores;
for(Integer score : scores){
if (score > result){
result = score; //re-point to the max value
}
}
return result;
}
//..elided
// PRIVATE
private final String fName;
private final int fLength;
private List<Integer> fRaceScores = new ArrayList<>();
private final Date fDateManufactured;
}

See Also :
Immutable objects
Designing for subclassing
Overridable methods need special care
Remember styles of inheritance

Use javadoc liberally


Javadoc is a great tool, and should be used with feelings of unbridled joy ;).
335

Collected Java Practices

A method header combined with its associated javadoc form the specification, or contract, of a method. If
the caller fulfills the stated requirements, then the method undertakes to fulfill its stated promises.
Using Javadoc acknowledges that there are two distinct questions a reader can ask about code:
what is this supposed to do? (answered only by the javadoc and method header)
how does it try to do it? (answered only by the implementation)
If javadoc is written correctly, then:
one can understand exactly what services are offered by a method ("what is this supposed this
do?"), without having to look at its implementation ("how does it try to do it?"). Reading an
implementation usually takes a lot more effort than reading javdoc.
the implementation can be checked for correctness versus the specification. That is, some bugs can
be found just by reading the code, as opposed to executing it.
Oracle has published this style guide for writing javadoc comments.
Note that:
if you wish to provide high level descriptions, then you may use the file names overview.html (a
conventional name) and package-info.java to do so (see below).
javadoc is "inherited" - javadoc written for an interface method or an abstract method is
automatically associated with corresponding concrete implementations
it's a common error to put the class level javadoc comment at the very start of a source file, before
the import statements. This is an error, since it must appear just above the class header.
Javadoc 1.4 has added many features, including:
the -tag option allows user-defined custom tags. This feature can be used to implement common
items such as @to.do, @is.Mutable, or @no.Nulls . (Oracle recommends placing a period
somewhere in the custom tag, to avoid potential future conflicts with tags defined by Oracle.)
the -linksource option generates an HTML version of your source code, and links the javadoc to
the source. (The source is presented without any syntax highlighting, but this remains a very nice
feature.)
there is finer-grained control over how javadoc is inherited. The {@inheritDoc} tag is useful here
(warning: this is broken in JDK 1.4.0)
Javadoc 1.5 has these notable additions :
support for new language features, such as generics and annotations
the @literal and @code tags for ensuring text is not treated as markup
Example:
/*
* This comment is NOT a class level javadoc comment.
* Such comments appear just above the class declaration, not at the
* start of the file.
*/
import java.math.BigDecimal;
/**
* Guitar Model Object.
*
* <P>Various attributes of guitars, and related behaviour.
*
* <P>Note that {@link BigDecimal} is used to model the price - not double or float.
336

Collected Java Practices

* See {@link #Guitar(String, BigDecimal, Integer)} for more information.


*
* @author Les Paul
* @version 2.0
*/
final class Guitar {
/**
* Constructor.
*
* @param aName (required) brand name of the guitar. Must have content. Length
* must be in range <tt>1..50</tt>.
* @param aPrice (optional) purchase price of the guitar.
* @param aNumStrings (required) number of strings on the guitar. Can take
* values 6 or 12.
*/
Guitar(String aName, BigDecimal aPrice, Integer aNumStrings){
//...elided
}
//There is a one-line form of javadoc comment, useful for shorter text :
/** Return name passed to the constructor.
String getName(){
return null;
}
/** Return price passed to the constructor.
BigDecimal getPrice(){
return null;
}

*/

*/

/** Value - {@value}, key for storing the current guitar of interest in the
session.*/
public static final String KEY = "guitar";
/**
* Play the guitar.
*
* This method makes no guarantees as to how <em>well</em> the song is played.
* @param aSongTitle must have content, and must have trimmed length greater than 2.
*/
void play(String aSongTitle){
//..elided
}
/**
* Apply standard tuning to the guitar.
*
* @return <tt>true</tt> only if the guitar has been properly tuned.
*/
boolean tune(){
return true;
}
/**
* Destroy the guitar while on stage.
*
* @deprecated Not good for the environment.
*/
void lightOnFireAndSmashLikeAWildman(){
//..elided
}
//...other methods elided
}

Prefer package-info.java to package.html


Originally, an HTML file named package.html was used to specify package level comments. That style
is still available, but it's likely better to use package-info.java instead:
it's shorter, since no boilerplate HTML is needed
the leading '*' on each line can be discarded, making it easy to paste in existing HTML
337

Collected Java Practices

package-info.java

is the only place to annotate a package, if needed

The package-info.java file is unusual since it doesn't contain a class. Indeed, the name package-info
is not a valid class name. Here is a simple example of its contents:
/** Edit the roles attached to a user. */
package hirondelle.fish.access.role;

Here is an example including an annotation for the package:


/** Edit the settings attached to a user. */
@Unpublished
package hirondelle.fish.access.user;

Here is a final example without the leading '*' on every line:


/**
Maintain a list of Members in the Fish and Chips Club.
An Active Member will be part of the RSVP list. When a Member
is inactive, they will not appear on the RSVP listing.
@author B. P. Hennessey
@version 1.0
*/
package hirondelle.fish.main.member;

See Also :
Document thread safety
Construct classes from the outside in
Design by Contract
Avoid basic style errors

Use static imports rarely


Static imports allow the static items of one class to be referenced in another without qualification. Used
indiscriminately, this will likely make code more difficult to understand, not easier to understand.
Example
import java.util.*;
import static java.util.Collections.*;
public final class StaticImporter {
public static void main(String... aArgs){
List<String> things = new ArrayList<>();
things.add("blah");
//This looks like a simple call of a method belonging to this class:
List<String> syncThings = synchronizedList(things);
//However, it actually resolves to :
//List<String> syncThings = Collections.synchronizedList(things);
}
}

An example in which a static import is likely acceptable is a constants class. For example, a scientific or
engineering application might make wide use ofMath.PI. A static import ofjava.lang.Math.* would
Math.PI

PI
338

Collected Java Practices

allow a class to replace

with

More generally, a business application might define a constants class, and import it statically. This would
allowConsts.NEW_LINE to be referenced asNEW_LINE , for example.
See Also :
Class for constants
Uncommon classes need explicit imports
Interface for constants

Use System.exit with care


System.exit

should be used with care. The normal method of terminating a program is to terminate all

user threads.
Cases in which System.exit is appropriate:
utility scripts
GUI applications, in which the event dispatch user thread is created in the background. Here, the
program may be terminated by calling System.exit, or, for example, by setting:
JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)

From Oracles's Portability Cookbook:


"The System.exit method forces termination of all threads in the Java virtual machine. This is
drastic....System.exit should be reserved for a catastrophic error exit, or for cases when a program is
intended for use as a utility in a command script that may depend on the program's exit code."
See Also :
Launch thread is just another user thread

Use Version Control tools


Using Version Control is strongly recommended for all non-trivial projects. For projects with multiple
developers, Version Control is almost always a necessity. The advantages of Version Control are
compelling:
it allows groups of people to work simultaneously on a project with minimal friction.
it allows time travel, in the sense of letting you create a snapshot of the code as it appeared at some
specific time in the past.
it lets you create parallel universes, by letting you define branches for your project. The most
common case of this is having a bug-fix branch along side a main trunk.
it acts as the definitive version of the source.
it allows for automated daily backups.
There are many Version Control systems available. Presented below are reminders for typical use for two
339

Collected Java Practices

such tools, CVS (Concurrent Version System) and Subversion.


More complete information is available from:
Pragmatic Version Control Using Subversion by Mike Mason
Pragmatic Version Control Using CVS by Thomas and Hunt

Subversion
Getting Started With Subversion
download
edit the file named %APPDATA%\Subversion\config (no file extension) You will likely want to edit
global-ignores , editor-cmd , and set enable-auto-props=yes . There are also settings for
pointing to your diff tool.
consider defining an environment variable SVNROOT to point to your subversion repository

Common Commands
svn checkout %SVNROOT%/blah/trunk
Checks out the trunk of a project named blah.
svn commit -m "Some message" blah.txt
Commits a change made to a file in the current directory.
svn help <command>
Get help on a command.
svn status -uv
Shows status. Recursive by default. Talks to server for any updates that you haven't seen yet.
svn log -v
View change sets.

Occasional Commands
svnadmin create C:\my-repos
Create a local repository.
svn import -m "Importing" . %SVNROOT%/blah/trunk
Import project named blah into a repository. Imports the local directory and all of its subdirectories.
svn info
Shows high level information; shows which branch you are on.
svn ls %SVNROOT%
Lists the projects in the repository. Allows you to navigate the repository's tree structure, by the usual
drill-down. '-R' is recursive, and shows each file, not just directories.

Branching
svn mkdir -m "Making first release branch" %SVNROOT%/blah/branch
Create a directory for the blah project to hold all future branches.
340

Collected Java Practices

svn copy -m "Making Release branch" %SVNROOT%/blah/trunk %SVNROOT%/blah/branch/RB-1.0


Create release branch 1.0. Looks like a directory copy operation.
svn co %SVNROOT%/blah/branch/RB-1.0 blah1.0
Check out release branch 1.0 to directory named blah1.0. The working directory is usually beside blah,
which contains the main trunk of development.
svn merge -r11:12 %SVNROOT%/blah/branch/RB-1.0
Merging a fix on a release branch into the trunk. Picks up edits between revision 11 and revision 12.
Needs a commit afterwards.

CVS
CVS Terminology
repository - where CVS stores the source code for projects.
CVSROOT - an environment variable which points to the repository.
workspace or working directory - where the developer does their work. A private copy of the code,
on the developer's host.
checkout - the initial fetch of a project's source tree from the repository.
update - a re-fetch of a file or set of files after the initial checkout.
commit - after verifying edits are correct, the developer will use a commit to post their edits from
their workspace back to the repository.
revision - version of a file, denoted by a string of numbers, as in 1.2 (on the trunk) or 1.1.2.3 (on a
branch).
trunk or mainline - the main branch of development. Releases are often branched off the trunk.
dead files - files that are removed from the project aren't really removed. Instead, they are moved
into the 'dead' state.
regular tag - label for a particular set of revisions in the project. Allows you to define specific
snapshots of the project, which can be used later to recreate that snapshot. Tags always start with a
letter, so you can always distinguish them from revisions (which are numeric, as in 1.2 or 1.1.2.3).
branch tag - label for a project branch. Always starts with a letter. Branches allow parallel
development.
HEAD - a reserved tag name, refers to the most recent revision in the repository.
BASE - a reserved tag name, refers to the revision checked out into the workspace. If a developer
has modified a file, then the BASE is no longer the same as the workspace copy.
sticky - many CVS commands are sticky. This means that the command is implicitly applied using
the most recent tag that was specified (either regular tag or branch tag). Thus, you can specify a tag
once, and then subsequent commands will assume that tag also applies as the default. The idea is to
let you work without repeatedly specifying the tag over and over again.

CVS Edit Codes


? - an item in the local workspace has not yet been added to the repository
U - updated (either changed or added new)
P - updated via a 'patch' (more or less the same as U)
M - item in the local workspace has been modified after retrieval from the repository
C - conflicting edits found between two revisions. Such conflicts need manual intervention, since
they cannot be automatically merged together.
A - added to CVS, but not yet committed.
R - removed from CVS, but not yet committed.

341

Collected Java Practices

Getting Started With CVS


to use CVS from the command line, add CVS to your PATH environment variable.
by default, most commands apply recursively to all subdirectories.
CVS usually outputs dates and times using Universal Time, but you can often alter the output time
zone, using the '-z' option.
CVS lets you define a simple local repository on your host's file system (see below). When getting
started with CVS, it's highly recommended that you create such a 'sandbox' repository, as a safe
place to experiment with CVS.
cvs --help-commands
Lists all CVS commands.
cvs -H update
Displays help for the update command.
cvs -d C:\sandbox init
Creates a local 'sandbox' repository, on the local file system. Such a sandbox is very helpful for
experimenting with CVS.This is a nice feature, since you don't have to set up a server in order to create
the repository. For more formal development, however, your 'real' source code will be stored in a server
on another host, not on the local file system.
tmp>set CVSROOT=C:\sandbox
tmp>cvs import -m "Play around with CVS" myproject myproject initial
Create a new project named 'myproject', built from the current contents of your tmp directory (and
subdirectories), and place the new project into your sandbox repository.
work>cvs -d C:\sandbox checkout myproject
Retrieves from the sandbox repository a copy of all files in myproject, and places them into
work/myproject. Retrieves the latest version of each file (the HEAD).

Common Commands
cvs status Blah.java
Lists general information about the file, whether it has been edited, it's revision, and what branch it's on.
cvs status -q Blah.java
More concise status listing.
cvs -q update -P -d
Updates your workspace with the latest repository files. Performs a merge. Most merges will succeed, but
some will fail, and will result in a conflict. Conflicts must be handled manually.
This is a 'sticky' operation (see above). If your workspace is on the trunk, then you are updated with new
trunk files. If your workspace is on a branch, then you are updated with files from that branch.
cvs -nq update -P -d
Compares your workspace with the repository without actually doing the update. Does not write to your
workspace. The '-n' option means "don't really do this, just show me the current differences between my
workspace and the HEAD of the repository."
cvs -q update -P -d -A
Updates your workspace, and ensures you're updated with the trunk, not a branch.

342

Collected Java Practices

cvs -q udpate -P -d -r REL_1_0


Updates your workspace with a specific release branch.
cvs commit -m "Some appropriate description"
Commits all edits to all files in and under the current directory.
cvs history -c -u myuserid -D yesterday -z AST
Displays what a specific user id committed yesterday, and displays the result in a specific time zone
(AST) instead of Universal Time.

Occasional Commands
work>cvs checkout myproject
Fetches an entire project from the repository, and places it in the local directory named work/myproject.
cvs add Blah.java
cvs commit -m "My new file." Blah.java
Adds a new file to the repository. Note that you need to commit the file before the addition is complete.
cvs add -kb jstl.jar
cvs commit -m "Required library." jstl.jar
Adds a new binary file to the repository. Again, you need to commit the file before the addition is
complete.
work>del Blah.java
work>cvs remove Blah.java
work>cvs commit "No longer needed." Blah.java
Deletes a file. This has 3 steps.
cvs history -c -D "2010/01/01" -z AST Blah.java
Displays a history of commits on a given file, after a given date, with date-time output in a specific time
zone (AST).
cvs log -d today Blah.java
Shows messages for all commits done today for a given file.
cvs -q log -wmyuserid -d"2010/04/13 13:00:00" -S
Shows messages for all commits done by a certain user, after a given date-time
cvs log -rREL_1_0 -wmyuserid Blah.java
Shows the commit messages for a given user id, performed on a specific branch.
work>rename SolarSys.java SolarSystem.java
work>cvs remove SolarSys.java
work>cvs add SolarSystem.java
work>cvs commit -m "Better name." SolarSystem.java
Renames a file. This has 4 steps.
cvs rtag -b RB_1_0 myproject
Creates a release branch named 'RB_1_0' for a project named 'myproject'. This command is applied to the
repository, not to the workspace. A release branch can be created before the release, if desired. Then,
each release candidate is built using the head of the release branch. When the release is final, then you
apply a regular tag to the release branch, to mark the exact point of release.
cvs tag REL_1_0
343

Collected Java Practices

Tags the repository files in your BASE -- the unmodified revisions checked out to your local workspace.
cvs diff --side-by-side Blah.java
Shows the differences between your workspace and the BASE.

CVS Quirks
after you have initially checked out a project to a given local workspace, CVS will remember both
the location of the repository, and your login credentials (as long as you don't log out). Thus,
there's no need to repeatedly state the location of the repository when issuing CVS commands.
if you try to commit a file which actually has no differences with the repository file, CVS will not
create a new revision.

Merging From Branch To Trunk


There are two ways to merge from a branch into the trunk.
cvs -q update -j 1.1.2.2 -j 1.1.2.3 Blah.java
This is usually the preferred method. Note the presence of 2 '-j' options. This means that only the edits
that took the file from 1.1.2.2 to 1.1.2.3 are to be merged into the trunk. This is very helpful, because it
can avoid the repetition of possible conflicts that may have already occurred during previous merges.
cvs -q update -j 1.1.2.3 Blah.java
This is usually not the preferred style (see above). This merge has a single '-j' option. The implementation
of this merge will need to go back to the common ancestor of the trunk and the branch. The problem with
this style is how conflicts are treated. If such a merge has a conflict, it can certainly be resolved, just like
any other conflict. However, the conflict will resurface again later should you need to perform another
merge between the branch and the trunk.

Separate Workspaces For Each Branch


Although there are many variations, it's typical to be working on 2 code streams (sometimes more) :
the trunk : main development for the next release
a branch : bug fixes for the most recent release, already in production
When working on more than a single branch, it's helpful to be able to switch rapidly between them. It's
certainly possible to use a single local workspace to do so, using a 'cvs update' command which
specifies the appropriate branch.
However, many find it simpler and more convenient to just use a separate workspace for each branch.
Since the update command is "sticky", and remembers the branch from which a workspace originally
came from, there's usually no asymmetry between working on the branch and working on the trunk.

Validate method arguments


The first lines of a method are usually devoted to checking the validity of method arguments. The idea is
to fail as quickly as possible in the event of an error. This is particularly important for constructors.
It's a reasonable policy for a class to skip validating arguments of private methods. The reason is that
private methods can only be called from the class itself. Thus, a class author should be able to confirm
344

Collected Java Practices

that all calls of a private method are valid. If desired, the assert keyword can be used to verify
private method arguments, to check the internal consistency of the class. (Using assert for checking
the arguments of a non- private method is not recommended, since disabling such assertions would mean
the contract of the non- private method is no longer being enforced.)
When validating an argument, an Exception is thrown if the test fails. It's often one of these unchecked
exceptions that are thrown:
IllegalArgumentException
NullPointerException
IllegalStateException

These are all subclasses of RuntimeException.


Checked exceptions can also be thrown, as shown in the Model Object validation topic. Many
programmers document these exceptions in the @throws clause of the method's javadoc, since they
clearly state the method's requirements to the caller (the pre-conditions). Others, however, feel that
manually documenting exceptions should be avoided.
If every object parameter of every method in a class needs to be non-null in order to avoid throwing
NullPointerException , then it's acceptable to state this once in the general class javadoc, instead of
repeating it for each method. Another alternative is to state in overview.html (javadoc's summary
description of an entire application) that all parameters are to be considered non-null unless explicitly
stated otherwise.
Example 1
The constructor of this class validates its arguments before doing anything else. If a validation fails, then
an IllegalArgumentException is thrown.
public final class ElementaryParticle {
/**
@param aName has content.
@param aSpeed is in the range 0 (inclusive) to 1 (inclusive), and
is expressed as a fraction of the speed of light. (The photon is
an example of an elementary particle which travels at this speed.)
@exception IllegalArgumentException if a param does not comply.
*/
public ElementaryParticle (String aName, double aSpeed) {
if (!textHasContent(aName)) {
throw new IllegalArgumentException("Name has no content.");
}
if (aSpeed < 0.0 || aSpeed > 1.0) {
throw new IllegalArgumentException("Speed not in range [0..1]: " + aSpeed);
}
fName = aName;
fSpeed = aSpeed;
}
//..other methods elided
// PRIVATE
private String fName;
private double fSpeed;
/**
Returns true if aText is non-null and has visible content.
This is a test which is often performed, and should probably
be placed in a general utility class.
*/
private boolean textHasContent(String aText){
String EMPTY_STRING = "";
return (aText != null) && (!aText.trim().equals(EMPTY_STRING));
}
345

Collected Java Practices

Example 2
Some validations are very common:
check that an object is not null
check that text has visible content
check that a number is in some range
Providing a class for such validations can often be useful. In this example, the Args class throws
IllegalArgumentException if a corresponding boolean check fails. Using Args to validate arguments
will increase legibility noticeably, especially in the case of multiple validations.
package hirondelle.web4j.util;
import java.util.regex.*;
/**
Utility methods for common argument validations.
<P>Replaces <tt>if</tt> statements at the start of a method with
more compact method calls.
<P>Example use case.
<P>Instead of :
<PRE>
public void doThis(String aText){
if (!Util.textHasContent(aText)){
throw new IllegalArgumentException();
}
//..main body elided
}
</PRE>
<P>One may instead write :
<PRE>
public void doThis(String aText){
Args.checkForContent(aText);
//..main body elided
}
</PRE>
*/
public final class Args {
/**
If <code>aText</code> does not satisfy {@link Util#textHasContent}, then
throw an <code>IllegalArgumentException</code>.
<P>Most text used in an application is meaningful only if it has visible content.
*/
public static void checkForContent(String aText){
if( ! Util.textHasContent(aText) ){
throw new IllegalArgumentException("Text has no visible content");
}
}
/**
If {@link Util#isInRange} returns <code>false</code>, then
throw an <code>IllegalArgumentException</code>.
@param aLow is less than or equal to <code>aHigh</code>.
*/
public static void checkForRange(int aNumber, int aLow, int aHigh) {
if ( ! Util.isInRange(aNumber, aLow, aHigh) ) {
throw new IllegalArgumentException(aNumber + " not in range " + aLow + ".." +
aHigh);
}
}
/**
If <tt>aNumber</tt> is less than <tt>1</tt>, then throw an
<tt>IllegalArgumentException</tt>.
346

Collected Java Practices

*/
public static void checkForPositive(int aNumber) {
if (aNumber < 1) {
throw new IllegalArgumentException(aNumber + " is less than 1");
}
}
/**
If {@link Util#matches} returns <tt>false</tt>, then
throw an <code>IllegalArgumentException</code>.
*/
public static void checkForMatch(Pattern aPattern, String aText){
if (! Util.matches(aPattern, aText)){
throw new IllegalArgumentException(
"Text " + Util.quote(aText) + " does not match '" +aPattern.pattern()+ "'"
);
}
}
/**
If <code>aObject</code> is null, then throw a <code>NullPointerException</code>.
<P>Use cases :
<pre>
doSomething( Football aBall ){
//1. call some method on the argument :
//if aBall is null, then exception is automatically thrown, so
//there is no need for an explicit check for null.
aBall.inflate();
//2. assign to a corresponding field (common in constructors):
//if aBall is null, no exception is immediately thrown, so
//an explicit check for null may be useful here
Args.checkForNull( aBall );
fBall = aBall;
//3. pass on to some other method as parameter :
//it may or may not be appropriate to have an explicit check
//for null here, according the needs of the problem
Args.checkForNull( aBall ); //??
fReferee.verify( aBall );
}
</pre>
*/
public static void checkForNull(Object aObject) {
if (aObject == null) {
throw new NullPointerException();
}
}
// PRIVATE
private Args(){
//empty - prevent construction
}
}

See Also :
Validate state with class invariants
Javadoc all exceptions
Assert is for private arguments only
Avoid @throws in javadoc
Model Objects

Wisdom, not rules


347

Collected Java Practices

It's important to realize that the coding techniques you learn should not be applied blindly, without
thinking. That is, while a practice can indeed be applicable most of the time, there are very few rules
which should absolutely never be broken. Rules are blind, in the sense that they often don't deal with the
full context of a problem, or the complete picture.
You shouldn't be afraid to consider breaking the rules, if you have thought about the problem, and when
you feel it's an appropriate thing to do.
When you begin your career as a programmer, following a standard set of rules is likely appropriate. But
as you progress, your skills become stronger, and you start to develop something which is lacking in the
beginner - a sense of taste. This sense of taste, this wisdom which you've acquired through real
experience, is what really guides a mature programmer. In essence, wisdom transcends rules.
Barry Schwartz has an interesting TED talk on this general idea, which is worth listening to.
See Also :
Coding conventions

Consider composition instead of subclassing


Consider composition as an alternative to subclassing.
Composition:
is implemented simply by forwarding all calls to an object field
has no dependence on implementation details of the object field
is more flexible, since it's defined dynamically at run-time, not statically at compile-time
Subclassing has the following issues:
it violates encapsulation, since the implementations of the superclass and subclass become tightly
coupled
new methods added to the superclass can break the subclass
superclass and subclass need to evolve in parallel
designing a class so that it may be safely extended takes extra work - extending a class not
explicitly designed for such use is risky
different packages are often under the control of different programmers - extending a class in a
different package is risky
In addition:
and Cloneable are more difficult to implement when inheritance is present
given a concrete class Aircraft which implements Comparable and overrides equals, it's not
possible to extend such a class ( Boeing747 ) and add a new significant field ( fUpperDeck ), and
simultaneously provide completely correct implementations of compareTo and equals. In this
situation, composition is required. (See Effective Java for further discussion.)
Serializable

A common exception is the template method design pattern. There, the safest style is to make all items in
the abstract base class final , except for the single abstract method which needs to be implemented by
the subclass.
348

Collected Java Practices

An interesting quote from chapter one of Design Patterns:


"That leads us to our second principle of object-oriented design:
Favor object composition over class inheritance.
...Nevertheless, our experience is that designers overuse inheritance as a reuse technique, and designs
are often made more reusable (and simpler) by depending more on object composition. You'll see object
composition applied again and again in the design patterns."
(Their first principle, stated earlier in the chapter, is "Program to an interface, not an implementation".)
See Also :
Implementing compareTo
Implementing equals
Minimize ripple effects
Template method

Designing for subclassing


To create an extendable class:
all constructors, readObject , and clone must not invoke an overridable method (one that is not
static, private , or final )
if a method depends on an implementation of a overridable method, this dependence must be
explicitly stated in its javadoc
don't implement Serializable unless you absolutely need to
don't implement Cloneable unless you absolutely need to
Consider using an "interface + default implementation" pair. This will allow users to choose the desired
style of inheritance:
subclass the default implementation directly
use the default implementation as a field, and forward calls to it
ignore the default implementation, and implement the interface entirely from scratch

See Also :
Use final liberally
Serialization and subclassing
Avoid clone
Overridable methods need special care
Remember styles of inheritance
Constructors shouldn't call overridables

Overridable methods need special care


349

Collected Java Practices

Any method which is not private, static, or final can be overridden. Allowing a method to be
overridden should always be done intentionally, not by accident. Overridable methods, and any methods
which call them, represent unusual places in your code, to which special attention must be paid. This is
because subclassing violates encapsulation, in the sense that it's possible for a subclass to break its
superclass's contract. See Effective Java for more information.
If you don't intend a method to be overridden, then you should declare it as private, static, or final .
When you do override a method, you should use the @Override annotation. This allows the compiler to
verify that the method is indeed a valid override of an existing method. For example, your
implementations of toString , equals, and hashCode should always use @Override in the method
header.
See Also :
Use final liberally
Designing for subclassing
Standardized dialogs
Constructors shouldn't call overridables
Use @Override liberally

Remember styles of inheritance


A class has supertypes: the class it extends, plus any interfaces it implements. A supertype can have 3
different policies for how the implementation of a method is treated:
mandatory: create a mandatory implementation, that can't be overridden - a final method in a
superclass.
default: create a default implementation, that can be overridden - a non- final method in a
superclass.
none provided: no implementation is provided at all, and a concrete class is forced to provide their
own implementation - an interface method, or an abstract method.
A class can take these general policies with respect to overriding, in order of increasing liberality:
disallow it completely - declare the class as final .
allow it, but disallow all overrides - declare all methods as final . A subclass can add new
methods, but it can't change the existing ones.
allow it, and permit some overrides - declare some methods as final , and some as non- final
require it - declare some methods as abstract

See Also :
Use final liberally
Designing for subclassing
Overridable methods need special care

350

Collected Java Practices

Avoid null if possible


Some argue that allowing null into the API of a class, in the form of possibly- null parameters or return
values, should be avoided if possible:
null references can only be used in a boolean test - any other use will throw a
NullPointerException
null references always represent special cases, and if these special cases can be

removed, the code


will be easier to understand
null return values are error prone, since there is no way to ensure that the caller always does a
check-for- null .
methods which accept an explicit null reference as a valid argument are less intelligible at the
point of call. The reader will usually need to consult documentation to determine what exactly the
null argument means.
If null is allowed for a method, then this should be clearly stated in its javadoc. Some follow the
convention that all items are assumed to be non- null unless otherwise specified. Such a convention
could be explicitly stated once in the javadoc overview.html .
There is a very common exception to this rule. For Model Objects, which map roughly to database
records, it's often appropriate to use null to represent optional fields stored as NULL in the database.
See Also :
Prefer empty items to null ones
Model Objects
Consider wrapper classes for optional data

Beware of Byte Order Marks


When dealing with text files having a Unicode encoding, some tools will prepend a special character
called a byte order mark (BOM) to the file. The BOM is from 2 to 4 bytes long, according to the
encoding. (In the UTF-8 encoding, for example, the byte order mark is 3 bytes long.) The BOM is often
not rendered visually in an editor, so they can be difficult to detect.
Probably the most widely used Unicode encoding is the UTF-8 encoding. However, in UTF-8, the BOM
is not required or even recommended by the UTF-8 standard. When a text file having UTF-8 encoding is
under your control, you should usually ensure that it does not get saved with a byte order mark, because
they can lead to problems. (It should always be safe to remove it.)
This is particularly important in the context of a web application. HTML, Java Server Pages, tag files,
and so on, should usually be served using UTF-8 encoding, without using a BOM. If not, the end user
may see strange-looking characters in their browser, or perhaps extra, unwanted empty lines. They can
also cause cascading style sheets to malfunction.
Unfortunately, on the Windows operating system, the Notepad tool will always add a byte order mark
when saving text files as UTF-8, so you need to exercise care when using Notepad.
Some text editors will give you information about the presence or absence of a BOM, and some will not.
To help you control your source code, here's a class which will detect and optionally remove UTF-8
BOMs from a source tree:
351

Collected Java Practices

import
import
import
import
import
import
import
import
import
import
import

java.io.BufferedInputStream;
java.io.BufferedOutputStream;
java.io.File;
java.io.FileInputStream;
java.io.FileOutputStream;
java.io.IOException;
java.io.InputStream;
java.io.OutputStream;
java.util.ArrayList;
java.util.Arrays;
java.util.List;

/**
Detect and remove Byte Order Marks (BOMs) from text files saved with a
Unicode encoding.
<P>Dev tool only. If you use this tool to remove BOMs, please ensure
you have made a backup.
<P>This class assumes the UTF-8 encoding for the BOM, but
is easily changed to handle any encoding.
See http://en.wikipedia.org/wiki/Byte_order_mark for more info.
JDK 5+.
*/
public final class BomDetector {
/** Run the tool against a root directory.*/
public static void main(String... aArgs) throws IOException{
BomDetector bom = new BomDetector(
"C:\\Temp3\\test\\",
".txt", ".jsp", ".jspf", ".tag", ".html",
".css", ".xml", ".js", ".sql", ".tld"
);
int count = 0;
for(String file : bom.findBOMs()){
log(file);
++count;
}
log("Number of files with BOM:" + count);
/*
for(String file : bom.removeBOMs()){
log("Removed BOM from: " + file);
}
*/
}
public BomDetector(String aRootDirectory, String... aFileExtensions){
fRootDir = new File(aRootDirectory);
fExtensions = Arrays.asList(aFileExtensions);
if(!fRootDir.exists() || fRootDir.isFile() ){
throw new RuntimeException("Root directory not valid.");
}
}
/** Find files with BOMs under the given root directory. Return their names. */
public List<String> findBOMs() throws IOException {
List<String> result = new ArrayList<String>();
for(File textFile : findTextFilesBeneath(fRootDir)){
if(startsWithBOM(textFile)){
result.add(textFile.getCanonicalPath());
}
}
return result;
}
/**
Find and remove BOMs from files under the given root directory.
Overwrites files.
Return the names of the affected files.
*/
public List<String> removeBOMs() throws IOException{
List<String> result = new ArrayList<String>();
for(String bomFile : findBOMs()){
stripBomFrom(bomFile);
result.add(bomFile);
352

Collected Java Practices

}
return result;
}
// PRIVATE
private File fRootDir;
private List<String> fExtensions;
/** Different encodings will have different BOMs. This is for UTF-8. */
private final int[] BYTE_ORDER_MARK = {239, 187, 191};
private static void log(Object aThing){
System.out.println(String.valueOf(aThing));
}
private List<File> findTextFilesBeneath(File aStartingDir) throws IOException {
List<File> result = new ArrayList<File>();
File[] filesAndDirs = aStartingDir.listFiles();
List<File> filesDirs = Arrays.asList(filesAndDirs);
for(File file : filesDirs){
if (isTextFile(file)){
result.add(file);
}
if( file.isDirectory() ) {
//recursive call!!
List<File> deeperList = findTextFilesBeneath(file);
result.addAll(deeperList);
}
}
return result;
}
private boolean isTextFile(File aFile) throws IOException{
boolean result = false;
String fileName = aFile.getCanonicalPath();
int finalDot = fileName.lastIndexOf(".");
if (finalDot > -1){
String extension = fileName.substring(finalDot);
result = fExtensions.contains(extension);
}
return result;
}
private boolean startsWithBOM(File aTextFile) throws IOException {
boolean result = false;
if(aTextFile.length() < BYTE_ORDER_MARK.length) return false;
//open as bytes here, not characters
int[] firstFewBytes = new int[BYTE_ORDER_MARK.length];
InputStream input = null;
try {
input = new FileInputStream(aTextFile);
for(int index = 0; index < BYTE_ORDER_MARK.length; ++index){
firstFewBytes[index] = input.read(); //read a single byte
}
result = Arrays.equals(firstFewBytes, BYTE_ORDER_MARK);
}
finally {
input.close();
}
return result;
}
private void stripBomFrom(String aTextFile) throws IOException{
File bomFile = new File(aTextFile);
long initialSize = bomFile.length();
long truncatedSize = initialSize - BYTE_ORDER_MARK.length;
byte[] memory = new byte[(int)(truncatedSize)];
InputStream input = null;
try {
input = new BufferedInputStream(new FileInputStream(bomFile));
input.skip(BYTE_ORDER_MARK.length);
int totalBytesReadIntoMemory = 0;
while(totalBytesReadIntoMemory < truncatedSize){
int bytesRemaining = (int)truncatedSize - totalBytesReadIntoMemory;
int bytesRead = input.read(memory, totalBytesReadIntoMemory, bytesRemaining);
if(bytesRead > 0){
totalBytesReadIntoMemory = totalBytesReadIntoMemory + bytesRead;
353

Collected Java Practices

}
}
overwriteWithoutBOM(memory, bomFile);
}
finally {
input.close();
}
File after = new File(aTextFile);
long finalSize = after.length();
long changeInSize = initialSize - finalSize;
if(changeInSize != BYTE_ORDER_MARK.length){
throw new RuntimeException(
"Change in file size: " + changeInSize +
" Expected change: " + BYTE_ORDER_MARK.length
);
}
}
private void overwriteWithoutBOM(
byte[] aBytesWithoutBOM, File aTextFile
) throws IOException{
OutputStream output = null;
try {
output = new BufferedOutputStream(new FileOutputStream(aTextFile));
output.write(aBytesWithoutBOM);
}
finally {
output.close();
}
}
}

See Also :
Prefer UTF-8 in all layers

Beware of DecimalFormat
Parsing user input into numbers (especially numbers representing money) is a very common task. Such
parsing is often implemented with DecimalFormat . However, parsing with DecimalFormat should be
done with care. You may even want to consider an alternative. Here's why:
surprisingly, a Format does not need to parse all of its input. That is, if it finds a match at the start
of an input String, it will cease parsing, and return a value. When parsing user input, this kind of
behavior is not usually desired. To accomodate this behavior, an extra step is needed - a
ParsePosition object needs to be passed as an output parameter to DecimalFormat .
the class seems to be rather buggy.
the class is harder than most to understand.
Here's an example of parsing with DecimalFormat . An example run appears below. As you can see,
parsing user input will always require the caller to use ParsePosition to detect if the whole input
has been parsed.
there are cases when the parsing seems just plain wrong.

import java.text.*;

354

Collected Java Practices

public final class BewareDecimalFormat {


/**
* parseUserInput DecimalFormat.
*/
public static void main (String... aArguments) {
String patternOne = "#,##0.00";
log("Format : " + patternOne);
parseUserInput("1000", patternOne);
parseUserInput("1,000.00", patternOne);
parseUserInput("1000.33", patternOne);
parseUserInput(".20", patternOne);
parseUserInput(".2", patternOne);
parseUserInput(".222", patternOne);
parseUserInput(".222333444", patternOne);
parseUserInput("100.222333444", patternOne);
parseUserInput(".22A", patternOne);
parseUserInput(".22BlahA", patternOne);
parseUserInput("22Blah", patternOne);
parseUserInput("Blah22", patternOne);
parseUserInput("100000,000.00", patternOne);
parseUserInput("10,0000,000.00", patternOne);
parseUserInput("1,0000,0000.00", patternOne);
String patternTwo= "#,###0.00"; //group every 4 digits, not 3
log(" ");
log("Format : " + patternTwo);
parseUserInput("1000.00", patternTwo);
parseUserInput("1,000.00", patternTwo);
parseUserInput("1,0000.00", patternTwo);
parseUserInput("1,00000.00", patternTwo);
log("Done.");
}
// PRIVATE
private static void parseUserInput(String aValue, String aDecimalFormatPattern) {
DecimalFormat format = new DecimalFormat(aDecimalFormatPattern);
ParsePosition parsePosition = new ParsePosition(0);
Object object = format.parse(aValue, parsePosition);
if(object == null) {
log("Failed to parse: " + aValue);
}
else if(parsePosition.getIndex() < aValue.length()) {
log(
aValue + " parsed OK (not whole input) ParsePos:" +
parsePosition.getIndex() + ", Parse Result: " + object
);
}
else {
log(
aValue + " parsed OK. ParsePos: " + parsePosition.getIndex() +
", Parse Result: " + object
);
}
}
private static void log(Object aMessage){
System.out.println(String.valueOf(aMessage));
}
}

Example run:
>java -cp . BewareDecimalFormat
Format : #,##0.00
1000 parsed OK. ParsePos: 4, Parse Result: 1000
1,000.00 parsed OK. ParsePos: 8, Parse Result: 1000
1000.33 parsed OK. ParsePos: 7, Parse Result: 1000.33
.20 parsed OK. ParsePos: 3, Parse Result: 0.2
.2 parsed OK. ParsePos: 2, Parse Result: 0.2
.222 parsed OK. ParsePos: 4, Parse Result: 0.222
.222333444 parsed OK. ParsePos: 10, Parse Result: 0.222333444
100.222333444 parsed OK. ParsePos: 13, Parse Result: 100.222333444
355

Collected Java Practices

.22A parsed OK (not whole input) ParsePos:3, Parse Result: 0.22


.22BlahA parsed OK (not whole input) ParsePos:3, Parse Result: 0.22
22Blah parsed OK (not whole input) ParsePos:2, Parse Result: 22
Failed to parse: Blah22
100000,000.00 parsed OK. ParsePos: 13, Parse Result: 100000000
10,0000,000.00 parsed OK. ParsePos: 14, Parse Result: 100000000
1,0000,0000.00 parsed OK. ParsePos: 14, Parse Result: 100000000
Format : #,###0.00
1000.00 parsed OK. ParsePos: 7, Parse Result: 1000
1,000.00 parsed OK. ParsePos: 8, Parse Result: 1000
1,0000.00 parsed OK. ParsePos: 9, Parse Result: 10000
1,00000.00 parsed OK. ParsePos: 10, Parse Result: 100000
Done.

Beware of floating point numbers


Outside of a scientific or engineering context, the use of float and double (and the corresponding
wrapper classes Float and Double) should likely be avoided. The fundamental problem is that rounding
errors will always occur when using these data types - they are unavoidable.
In a typical business application, using float and double to represent money values is dangerous,
because of these rounding issues. Instead, BigDecimal should usually be used to represent money.
From an IBM article on this topic:
"...binary floating-point arithmetic should not be used for financial, commercial, and user-centric
applications or web services because the decimal data used in these applications cannot be represented
exactly using binary floating-point."
From an article by Brian Goetz:
"...it is a bad idea to use floating point to try to represent exact quantities like monetary amounts. Using
floating point for dollars-and-cents calculations is a recipe for disaster. Floating point numbers are best
reserved for values such as measurements, whose values are fundamentally inexact to begin with."
See Also :
Representing money
Avoid basic style errors

Clarifying method
The structure of a method can often be made clearer by splitting it up into smaller methods. Martin
Fowler, author of Refactoring, is an enthusiastic advocate of this style.
Refactoring an original method into smaller pieces has several advantages:
it allows the original method to stay at a higher level of abstraction, which always increases
legibility
it makes the original method simpler
356

Collected Java Practices

it reduces the number of local variables in the original method


it allows method names to automatically document the intent of the code
Sometimes a comment is placed at the start of a chunk of code, explaining its intent. If a clarifying
method is used instead, then the chunk of code gets refactored into its own method, while the comment
itself is replaced by a call to a well-named method. "When a comment grows up, it becomes a method
call".
Example
Here, the method parseSearchText , which was originally monolithic, has been refactored to call several
private methods which perform very well-defined, simple tasks.
Boolean conditions can often benefit from being placed in their own method. Here, isDoubleQuote ,
isCommonWord , and textHasContent are all examples of this.
import java.util.*;
/**
* The user enters text into a search box. This class is used
* to parse that text into specific search terms (or tokens).
* It eliminates common words, and allows for the quoting of text, using
* double quotes.
* JDK 7+.
*/
public final class SearchBoxParser {
public static void main(String... aArguments) {
SearchBoxParser parser = new SearchBoxParser("mars venus \"milky way\" sun");
Set<String> tokens = parser.parseSearchText();
//display the tokens
System.out.println(tokens);
}
/**
* @param aSearchText is non-null, but may have no content,
* and represents what the user has input in a search box.
*/
public SearchBoxParser(String aSearchText) {
if (aSearchText == null) {
throw new IllegalArgumentException("Search Text cannot be null.");
}
fSearchText = aSearchText;
}
/**
* Parse the user's search box input into a Set of String tokens.
*
* @return Set of Strings, one for each word in fSearchText; here "word"
* is defined as either a lone word surrounded by whitespace, or as a series
* of words surrounded by double quotes, "like this"; also, very common
* words (and, the, etc.) do not qualify as possible search targets.
*/
public Set<String> parseSearchText() {
Set<String> result = new LinkedHashSet<>();
boolean returnTokens = true;
String currentDelims = fWHITESPACE_AND_QUOTES;
StringTokenizer parser = new StringTokenizer(
fSearchText, currentDelims, returnTokens
);
String token = null;
while (parser.hasMoreTokens()) {
token = parser.nextToken(currentDelims);
if (!isDoubleQuote(token)){
addNonTrivialWordToResult(token, result);
}
else {
currentDelims = flipDelimiters(currentDelims);
}
}
357

Collected Java Practices

return result;
}
// PRIVATE
private String fSearchText;
private static final Set<String> fCOMMON_WORDS = new LinkedHashSet<>();
private static final String fDOUBLE_QUOTE = "\"";
//the parser flips between these two sets of delimiters
private static final String fWHITESPACE_AND_QUOTES = " \t\r\n\"";
private static final String fQUOTES_ONLY ="\"";
/**Very common words to be excluded from searches.*/
static {
fCOMMON_WORDS.add("a");
fCOMMON_WORDS.add("and");
fCOMMON_WORDS.add("be");
fCOMMON_WORDS.add("for");
fCOMMON_WORDS.add("from");
fCOMMON_WORDS.add("has");
fCOMMON_WORDS.add("i");
fCOMMON_WORDS.add("in");
fCOMMON_WORDS.add("is");
fCOMMON_WORDS.add("it");
fCOMMON_WORDS.add("of");
fCOMMON_WORDS.add("on");
fCOMMON_WORDS.add("to");
fCOMMON_WORDS.add("the");
}
/**
* Use to determine if a particular word entered in the
* search box should be discarded from the search.
*/
private boolean isCommonWord(String aSearchTokenCandidate){
return fCOMMON_WORDS.contains(aSearchTokenCandidate);
}
private boolean textHasContent(String aText){
return (aText != null) && (!aText.trim().equals(""));
}
private void addNonTrivialWordToResult(String aToken, Set<String> aResult){
if (textHasContent(aToken) && !isCommonWord(aToken.trim())) {
aResult.add(aToken.trim());
}
}
private boolean isDoubleQuote(String aToken){
return aToken.equals(fDOUBLE_QUOTE);
}
private String flipDelimiters(String aCurrentDelims){
String result = null;
if (aCurrentDelims.equals(fWHITESPACE_AND_QUOTES)){
result = fQUOTES_ONLY;
}
else {
result = fWHITESPACE_AND_QUOTES;
}
return result;
}
}

See Also :
Avoid basic style errors

Coding conventions
358

Collected Java Practices

Many groups use a specific set of coding conventions as a programming style guide.
Oracle's coding conventions are popular, but be warned that Joshua Bloch (author of Effective Java) once
stated that this document should not be taken too seriously, as it's not actually used at Oracle (formerly
Sun Microsystems)!
Doug Lea, author of Concurrent Programming in Java, has also published a set of coding conventions,
and many others are available on the web.
Coding conventions should be used as a guide, not as a sacred, immutable law of nature. Those with less
experience will initially benefit from following conventions, but as you gain experience, you should rely
more on your own sense of taste, as opposed to blindly following the rules. The problem with rules is
that there are many cases in which they should be broken.
In general, the question in your mind should always be one of compassion for the reader: "Will this
help or hinder a maintainer who is unfamiliar with my code?". That is the guiding principle.
See Also :
Naming conventions
Wisdom, not rules

Compile regular expressions once


objects compile the regular expressions which are passed to them. (The compilation happens in
memory.) If a regular expression is used many times, then this compilation should be performed only
once.
Pattern

Be wary of the these convenience methods, which compile a regular expression each time they are called:
Pattern.matches
String.matches
String.replaceAll
String.replaceFirst
String.split

Example
Here's a comparison of the performance of String.matches versus the use of Pattern and Matcher.
An example run (with the HotSpot compiler turned off) gives:
Time for String: 67.693 ms
Time for Pattern: 12.178 ms

import
import
import
import
import
import

java.io.IOException;
java.nio.charset.Charset;
java.nio.charset.StandardCharsets;
java.nio.file.Path;
java.nio.file.Paths;
java.util.Scanner;
359

Collected Java Practices

import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** JDK 7+. */
public final class FindMatch {
/**
Count the number of times the word "assert" occurs in a file.
Compare the execution times of two techniques.
*/
public static void main (String... aArguments) throws IOException{
FindMatch findMatch = new FindMatch("assert");
Path path = Paths.get("C:\\Temp\\InheritanceInterpreter.java");
Stopwatch stopwatch = new Stopwatch();
stopwatch.start();
findMatch.countMatchesUsingString(path);
stopwatch.stop();
log("Time for String: " + stopwatch);
stopwatch.start();
findMatch.countMatchesUsingPattern(path);
stopwatch.stop();
log("Time for Pattern: " + stopwatch);
}
public FindMatch(String aTarget){
fTargetWord = aTarget;
fPattern = Pattern.compile(fTargetWord);
}
int countMatchesUsingString(Path aPath) throws IOException {
int result = 0;
try (Scanner scanner = new Scanner(aPath, ENCODING.name())){
while (scanner.hasNextLine()){
String line = scanner.nextLine();
if( line.matches(fTargetWord) ){
++ result;
}
}
}
return result;
}
int countMatchesUsingPattern(Path aPath) throws IOException {
int result = 0;
Matcher matcher = fPattern.matcher("");
try (Scanner scanner = new Scanner(aPath, ENCODING.name())){
while (scanner.hasNextLine()){
String line = scanner.nextLine();
matcher.reset(line); //reset the input each time
if (! matcher.find()) {
++result;
}
}
}
return result;
}
// PRIVATE
private final String fTargetWord;
private final Pattern fPattern;
private final static Charset ENCODING = StandardCharsets.UTF_8;
private static void log(Object aObject){
System.out.println(aObject);
}
}

See Also :
360

Collected Java Practices

Time execution speed

Conditional compile
The conditional compilation practice is used to optionally remove chunks of code from the compiled
version of a class. It uses the fact that compilers will ignore any unreachable branches of code.
To implement conditional compilation,
define a static final boolean value as a non-private member of some class
place code which is to be conditionally compiled in an if block which evaluates the boolean
set the value of the boolean to false to cause the compiler to ignore the if block; otherwise, keep
its value as true
This practice is used mostly for implementing debugging statements, logging statements, and for
assertions. With the advent of assert and the Logging API in JDK 1.4, the utility of conditional
compilation is probably reduced.
Example
public final class Debug {
//set to false to allow compiler to identify and eliminate
//unreachable code
public static final boolean ON = true;
}

This class usesDebug to implement conditional compilation :


import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
/**
* Uses conditional compilation to optionally print debugging statements.
*/
public final class GenericServlet extends HttpServlet {
public void init(ServletConfig aConfig) throws ServletException {
super.init(aConfig);
//print out a message, but only if Debug.ON evaluates to true
//if Debug.ON evaluates to false, this entire block of code will be
//passed over by the compiler, and will not be present in output .class file
if (Debug.ON) {
System.out.println("**** INIT OF My Controller *****");
}
}
public void doGet(
HttpServletRequest aRequest,
HttpServletResponse aResponse
) throws ServletException, IOException {
if (Debug.ON) {
System.out.println("Processing doGet");
}
doHttpRequest(aRequest, aResponse);
}
public void doPost(
HttpServletRequest aRequest,
HttpServletResponse aResponse
) throws ServletException, IOException {
if (Debug.ON) {
361

Collected Java Practices

System.out.println("Processing doPost");
}
doHttpRequest(aRequest, aResponse);
}
// PRIVATE //
private void doHttpRequest(
HttpServletRequest aRequest,
HttpServletResponse aResponse
) throws ServletException, IOException {
//empty
}
}

See Also :
Assertions in general
Logging messages

Consider code generators


Some types of classes share the same basic structure, and differ only in details. Type-safe enumerations
and data-centric Model Objects are two examples. In such cases, aids for rapid code generation become
very useful. These may take several forms:
a simple template of text intended for copy-and-paste
a passive code generator - generates source files which may need further edits. If further edits are
performed, then they will be lost if the code generator is run a second time.
an active code generator - generates source files which will not need further edits (the idea here is
to use the code generator as part of the actual build, instead of relying on it for a one-time
operation)
a round-trip code generator - generates source files which may need further edits. If further edits
are performed, then they will not be lost if the code generator is run a second time, since it will
detect such edits, and use them to update its internal model.
Code generators often use simple, ad hoc textual descriptions as input. The codegeneration.net web site is
a useful reference for code generation.
The extensive use of code generation is often called Model-Driven Development (MDD), or ModelDriven Architecture (MDA).

Consider immutable forms for dates


Some programmers prefer to store date fields not as java.util.Date objects, but in an immutable form
such as a long , or an immutable object (such as a DateTime from the date4j library).
Reasons for preferring an immutable representation:
the mutable Date class requires more care than a long integer
the Date class itself has many deprecations, replaced by methods in DateFormat , Calendar , and
362

Collected Java Practices

GregorianCalendar
you may use long as

a simple, locale-independent representation of a date, and use the date-related


classes to provide a particular view of that date
Example
Here, a simple long is used to encapsulate a date:
package myapp.business;
import java.util.*;
public final class Movie {
public Movie(String aTitle, long aReleaseDate){
fTitle = aTitle;
fReleaseDate = aReleaseDate;
}
public Movie(String aTitle, Date aReleaseDate){
this(aTitle, aReleaseDate.getTime());
}
/**
* The caller of this method decides on the
* presentation format of the date, if necessary.
*/
public long getReleaseDate(){
return fReleaseDate;
}
public String getTitle() {
return fTitle;
}
//..other methods elided
// PRIVATE
private String fTitle;
private long fReleaseDate;
}

See Also :
Defensive copying
Immutable objects

Conventional name for return value


Some find that consistently using the conventional name 'result' for the return value of a method can
increase legibility.
Example
import java.io.*;
/**
* Simple utilities to return the stack trace of an
* exception as a String.
*/
public final class StackTraceUtil {

363

Collected Java Practices

public static String getStackTrace(Throwable aThrowable) {


Writer result = new StringWriter();
PrintWriter printWriter = new PrintWriter(result);
aThrowable.printStackTrace(printWriter);
return result.toString();
}
/**
* Defines a custom format for the stack trace as String.
*/
public static String getCustomStackTrace(Throwable aThrowable) {
//add the class name and any message passed to constructor
StringBuilder result = new StringBuilder( "BOO-BOO: " );
result.append(aThrowable.toString());
String NEW_LINE = System.getProperty("line.separator");
result.append(NEW_LINE);
//add each element of the stack trace
for (StackTraceElement element : aThrowable.getStackTrace()){
result.append(element);
result.append(NEW_LINE);
}
return result.toString();
}
/** Demonstrate output. */
public static void main (String... aArguments){
Throwable throwable = new IllegalArgumentException("Blah");
System.out.println(getStackTrace(throwable));
System.out.println(getCustomStackTrace(throwable));
}
}

See Also :
Naming conventions
Stack trace as String
Multiple return statements

Defensive copying
A mutable object is simply an object which can change its state after construction. For example,
StringBuilder and Date are mutable objects, while String and Integer are immutable objects.
A class may have a mutable object as a field. There are two possible cases for how the state of a mutable
object field can change:
its state can be changed only by the native class - the native class creates the mutable object field,
and is the only class which is directly aware of its existence
its state can be changed both by the native class and by its callers - the native class simply points
to a mutable object which was created elsewhere
Both cases are valid design choices, but you must be aware of which one is appropriate for each case.
If the mutable object field's state should be changed only by the native class, then a defensive copy of the
mutable object must be made any time it's passed into (constructors and set methods) or out of ( get
methods) the class. If this is not done, then it's simple for the caller to break encapsulation, by changing
the state of an object which is simultaneously visible to both the class and its caller.
364

Collected Java Practices

Example
Planet has a mutable object field fDateOfDiscovery, which is defensively copied in all constructors,
and in getDateOfDiscovery . Planet represents an immutable class, and has no set methods for its fields.
Note that if the defensive copy of DateOfDiscovery is not made, then Planet is no longer immutable!
import java.util.Date;
/**
* Planet is an immutable class, since there is no way to change
* its state after construction.
*/
public final class Planet {
public Planet (double aMass, String aName, Date aDateOfDiscovery) {
fMass = aMass;
fName = aName;
//make a private copy of aDateOfDiscovery
//this is the only way to keep the fDateOfDiscovery
//field private, and shields this class from any changes that
//the caller may make to the original aDateOfDiscovery object
fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
}
/**
* Returns a primitive value.
*
* The caller can do whatever they want with the return value, without
* affecting the internals of this class. Why? Because this is a primitive
* value. The caller sees its "own" double that simply has the
* same value as fMass.
*/
public double getMass() {
return fMass;
}
/**
* Returns an immutable object.
*
* The caller gets a direct reference to the internal field. But this is not
* dangerous, since String is immutable and cannot be changed.
*/
public String getName() {
return fName;
}
// /**
// * Returns a mutable object - likely bad style.
// *
// * The caller gets a direct reference to the internal field. This is usually
dangerous,
// * since the Date object state can be changed both by this class and its caller.
// * That is, this class is no longer in complete control of fDate.
// */
// public Date getDateOfDiscovery() {
//
return fDateOfDiscovery;
// }
/**
* Returns a mutable object - good style.
*
* Returns a defensive copy of the field.
* The caller of this method can do anything they want with the
* returned Date object, without affecting the internals of this
* class in any way. Why? Because they do not have a reference to
* fDate. Rather, they are playing with a second Date that initially has the
* same data as fDate.
*/
public Date getDateOfDiscovery() {
return new Date(fDateOfDiscovery.getTime());
}
// PRIVATE
/**
* Final primitive data is always immutable.
*/
365

Collected Java Practices

private final double fMass;


/**
* An immutable object field. (String objects never change state.)
*/
private final String fName;
/**
* A mutable object field. In this case, the state of this mutable field
* is to be changed only by this class. (In other cases, it makes perfect
* sense to allow the state of a field to be changed outside the native
* class; this is the case when a field acts as a "pointer" to an object
* created elsewhere.)
*/
private final Date fDateOfDiscovery;
}

See Also :
Copy constructors
Fields should usually be private
Immutable objects
Avoid clone
Encapsulate collections

Design by Contract
The specification of a class or interface is the collection of non-private items provided as services to the
caller, along with instructions for their use, as stated in javadoc. It's a challenge to construct a
specification which:
is as simple as possible
is as clear as possible
has no ambiguity
is completely accurate
allows the reader to completely ignore implementation details (unless there is a bug)
pours understanding into the mind of the reader as quickly as possible, with little chance for
misunderstanding
Design By Contract is an effective guideline of lasting value for creating a specification.
The fundamental idea of Design By Contract is to treat the services offered by a class or interface as a
contract between the class (or interface) and its caller. Here, the word "contract" is meant to convey a
kind of formal, unambiguous agreement between two parties. C++ FAQs describes a contract as being
made of two parts:
requirements upon the caller made by the class
promises made by the class to the caller
If the caller fulfills the requirements, then the class promises to deliver some well-defined service.
Some changes to a specification/contract will break the caller, and some won't. For determining if a
change will break a caller, C++ FAQs uses the memorable phrase "require no more, promise no less": if
366

Collected Java Practices

the new specification does not require more from the caller than before, and if it does not promise to
deliver less than before, then the new specification is compatible with the old, and will not break the
caller.
Although Design By Contract is a formal part of some programming languages, such as Eiffel, it's not a
formal part of Java. Nevertheless, Design By Contract is very useful for designing classes and interfaces.
It can both guide the discovery of a more robust design, and allow more effective expression of that
design in javadoc.
Requirements are simply any conditions on use, for example:
conditions on argument values
conditions on order of execution of methods
condtions on execution in a multi-threaded environment
Requirements must be stated in javadoc, and may be enforced by throwing checked or unchecked
exceptions when the stated conditions are violated. Using assertions for enforcing requirements is not
recommended.
Promises are stated in javadoc. They can be enforced by assertions at the end of a method.
See Also :
Use javadoc liberally
Construct classes from the outside in
Assert use cases

Don't use tab characters


Most code editors allow you to use TAB characters in your source code. Such TAB characters will appear
at the start of each line of your code, controlling indentation. The problem is that TAB characters are
inherently ambiguous, and different tools can render TAB characters in different ways. What may look
reasonable to the author, at time of writing in their particular environment, may be rendered in a
significantly different way in some other tool.
For example, say the following code appears in your editor as:
import java.util.*;
public final class TabsOne {
void decideWhatToDo(){
if(! bored){
continueWhatYourAreDoing();
}
else {
if (! hasCarpalTunnelIssues()) {
code();
}
else {
eatSomething();
}
}
}
}

367

Collected Java Practices

If you are using TAB characters, then this could easily appear in another tool as:
import java.util.*;
public final class TabsTwo {
void decideWhatToDo(){
if(!bored){
continueWhatYourAreDoing();
}
else {
if (! hasCarpalTunnelIssues()) {
code();
}
else {
eatSomething();
}
}
}
}

Even worse, some tools can even render text with TABs with negative indentation:
import java.util.*;
public final class TabsThree {
void decideWhatToDo(){
if(!bored){
continueWhatYourAreDoing();
}
else {
if (! hasCarpalTunnelIssues()) {
code();
}
else {
eatSomething();
}
}
}
}

Most people would consider such code as much more difficult to read, and annoying. Thus, using TAB
characters in source code is often considered highly undesirable.
You should use 2-4 spaces to indent your code, not TAB characters. Modern IDE's let you map TAB
keystrokes to a given number of spaces instead.
Here's a related quote from Code Complete, by Steve McConnell:
"The study concluded that two-to-four-space indentation was optimal. Interestingly, many subjects in the
experiment felt that the six-space indentation was easier to use than the smaller indentations, even
though their scores were lower. That's probably because six spaces looks pleasing. But regardless of how
pretty it looks, six-space indentation turns out to be less readable. This is an example of a collision
between aesthetic appeal and readability."

Extra space in argument list


Some programmers feel that using extra spaces within parentheses - as in ( this ) instead of (that) slightly increases the legibility of code. However, this style doesn't seem to be very common.

368

Collected Java Practices

Example
import java.util.*;
/**
* When using parentheses, extra spaces may be used to slightly
* increase legibility. Here, both styles are illustrated for comparison.
*/
public final class ExtraSpace {
public void explode( String aThing ) {
expand( aThing ); //with extra spaces
burst(aThing);
//without extra spaces
}
// PRIVATE
private static final String fBALLOON = "balloon";
private static final String fTIRE = "tire";
private void expand( String aThing ){
if ( aThing.equals( fBALLOON ) ) { //with extra spaces
//..elided
}
else if (aThing.equals(fTIRE)){
//without extra spaces
//..elided
}
else {
//..elided
}
}
private void burst( String aThing ){
//..elided
}

//with extra spaces

Generating unique IDs


When identifiers are used solely within a database, their generation should be left to the database itself.
(See Statement.getGeneratedKeys.)
Unique identifiers which are "published" in some way may need special treatment, since the identifier
may need to be difficult to guess or forge. A typical example is the value of a cookie used as a session
identifier - simply using a series of consecutive integers is generally unacceptable, since one user could
easily impersonate another by altering the value of the cookie to some nearby integer.

Style 1 - UUID
Starting with Java 5, the UUID class provides a simple means for generating unique ids. The identifiers
generated by UUID are actually universally unique identifiers.
Example
import java.util.UUID;
public class GenerateUUID {
public static final void main(String... aArgs){
//generate random UUIDs
UUID idOne = UUID.randomUUID();
UUID idTwo = UUID.randomUUID();
369

Collected Java Practices

log("UUID One: " + idOne);


log("UUID Two: " + idTwo);
}
private static void log(Object aObject){
System.out.println( String.valueOf(aObject) );
}
}

Example run:
>java -cp . GenerateUUID
UUID One: 067e6162-3b6f-4ae2-a171-2470b63dff00
UUID Two: 54947df8-0e9e-4471-a2f9-9af509fb5889

If Java 5 is not available, then there are other more laborious ways to generate unique ids (see below).

Style 2 - SecureRandom and MessageDigest


The following method uses SecureRandom and MessageDigest :
upon startup, initialize SecureRandom (this may be a lengthy operation)
when a new identifier is needed, generate a random number using SecureRandom
create a MessageDigest of the random number
encode the byte[] returned by the MessageDigest into some acceptable textual form
check if the result is already being used; if it's not already taken, it's suitable as a unique identifier
The MessageDigest class is suitable for generating a "one-way hash" of arbitrary data. (Note that hash
values never uniquely identify their source data, since different source data can produce the same hash
value. The value of hashCode , for example, does not uniquely identify its associated object.) A
MessageDigest takes any input, and produces a String which:
is of fixed length
does not allow the original input to be easily recovered (in fact, this is very hard)
does not uniquely identify the input; however, similar input will produce dissimilar message digests
MessageDigest

is often used as a checksum, for verifying that data has not been altered since its creation.

Example
import java.security.SecureRandom;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class GenerateId {
public static void main (String... arguments) {
try {
//Initialize SecureRandom
//This is a lengthy operation, to be done only upon
//initialization of the application
SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");
//generate a random number
String randomNum = new Integer(prng.nextInt()).toString();
//get its digest
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byte[] result = sha.digest(randomNum.getBytes());
System.out.println("Random number: " + randomNum);
System.out.println("Message digest: " + hexEncode(result));
}
catch (NoSuchAlgorithmException ex) {
370

Collected Java Practices

System.err.println(ex);
}
}
/**
* The byte[] returned by MessageDigest does not have a nice
* textual representation, so some form of encoding is usually performed.
*
* This implementation follows the example of David Flanagan's book
* "Java In A Nutshell", and converts a byte array into a String
* of hex characters.
*
* Another popular alternative is to use a "Base64" encoding.
*/
static private String hexEncode(byte[] aInput){
StringBuilder result = new StringBuilder();
char[] digits = {'0', '1', '2', '3',
'4','5','6','7','8','9','a','b','c','d','e','f'};
for (int idx = 0; idx < aInput.length; ++idx) {
byte b = aInput[idx];
result.append(digits[ (b&0xf0) >> 4 ]);
result.append(digits[ b&0x0f]);
}
return result.toString();
}
}

Example run:
>java -cp . GenerateId
Random number: -1103747470
Message digest: c8fff94ba996411079d7114e698b53bac8f7b037

Style 3 - UID
Finally, here is another method, using a java.rmi.server.UID . The Serializable identifiers generated
by this class are unique on the host on which they are generated, provided that
the host takes more than one millisecond to reboot
the host's clock is never set to run backwards
In order to construct a UID that is globally unique, simply pair a UID with an InetAddress.
Example
import java.rmi.server.UID;
public class UniqueId {
/**
* Build and display some UID objects.
*/
public static void main (String... arguments) {
for (int idx=0; idx<10; ++idx){
UID userId = new UID();
System.out.println("User Id: " + userId);
}
}
}

Example run:
User Id: 3179c3:ec6e28a7ef:-8000
User Id: 3179c3:ec6e28a7ef:-7fff
371

Collected Java Practices

User
User
User
User
User
User
User
User

Id:
Id:
Id:
Id:
Id:
Id:
Id:
Id:

3179c3:ec6e28a7ef:-7ffe
3179c3:ec6e28a7ef:-7ffd
3179c3:ec6e28a7ef:-7ffc
3179c3:ec6e28a7ef:-7ffb
3179c3:ec6e28a7ef:-7ffa
3179c3:ec6e28a7ef:-7ff9
3179c3:ec6e28a7ef:-7ff8
3179c3:ec6e28a7ef:-7ff7

Clearly, these are not secure identifiers - knowing one, it's easy to guess another.

Include 'from', exclude 'to'


There's a convention in the JDK involving from..to ranges of values. The convention is that from is
included, while to is excluded.
Some examples from the JDK:
String.substring(from, to)
List.sublist(from, to)
Arrays.copyOfRange(T[], from, to)

Date Ranges
A related issue is specific to date values. It's common to have a date range specified using dates only
(with no time portion), while the underlying data actually includes both a date and a time. This can easily
lead to off-by-one errors.
For example, taking the following expression:
2010-01-01 <= x <= 2010-01-31

then the following is true:


Date x
In Range?
2010-01-01 00:00:00
Y
2010-01-01 01:00:00
Y
2010-01-30 00:00:00
Y
2010-01-30 23:59:59
Y
2010-01-31 00:00:00 Y (usually)
2010-01-31 00:00:01
N
2010-01-31 02:00:00
N
(The caveat for 2010-01-31 00:00:00 is made since it will depend on the details of how the comparison
is made by the class which models the date/time - usually, it will be within the range.)
The above table shows that times after 2010-01-31 00:00:00 are excluded from the range. This is
usually not desirable. In this case, you need to alter the comparison slighty to:
2010-01-01 <= x < (2010-01-31 + 1 day)

Again, this uses the include-from, exclude-to style mentioned above.

372

Collected Java Practices

Multiple return statements


Some programmers find that, occasionally, some methods become clearer if multiple return statements
are used, instead of the usual single point of exit.
This technique can be easily abused, and should be used with care. It's mainly used to replace nested if
structures.
Multiplereturn statements seem to work well for "guard code" at the beginning of a method, in which
the main body of the method is executed only if certain conditions are satisfied.
Example
Here, equals has multiple return statements, since a successful test implies that further comparison is
redundant. The alternative is to use multiple if statements, which some would find less legible.
import java.util.*;
public final class Auto {
public boolean equals(Object aThat) {
if (this == aThat) return true;
if (!(aThat instanceof Auto)) return false;
Auto that = (Auto)aThat;
return
EqualsUtil.areEqual(this.fName, that.fName) &&
EqualsUtil.areEqual(this.fNumDoors, that.fNumDoors) &&
EqualsUtil.areEqual(this.fOptions, that.fOptions) &&
EqualsUtil.areEqual(this.fGasMileage, that.fGasMileage) &&
EqualsUtil.areEqual(this.fColor, that.fColor) &&
Arrays.equals(this.fMaintenanceChecks, that.fMaintenanceChecks)
;
}
//..elided
// PRIVATE
private
private
private
private
private
private

String fName;
int fNumDoors;
List<String> fOptions;
double fGasMileage;
String fColor;
Date[] fMaintenanceChecks;

See Also :
Implementing equals
Conventional name for return value
Avoid basic style errors

Overloading can be tricky


Overloading methods should be done with care. The compiler decides which version of an overloaded
373

Collected Java Practices

method will be called based on declared compile-time type, not run-time type. For the case in which
overloaded methods have the same number of arguments, the rules regarding this decision can sometimes
be a bit tricky.
If there may be confusion, you may simplify the design:
use different method names, and avoid overloading altogether
retain overloading, but ensure each method has a distinct number of arguments
In addition, it's recommended that varargs not be used when a method is overloaded, since this makes it
more difficult to determine which overload is being called.
Reminder:
Overloading requires methods with distinct signatures. The signature of a method includes its name and
the ordered list of its argument types. All other items appearing in a method header, such as exceptions,
return type, final , and synchronized, do not contribute to a method's signature.

Package by feature, not layer


The first question in building an application is "How do I divide it up into packages?". For typical
business applications, there seems to be two ways of answering this question.

Package By Feature
Package-by-feature uses packages to reflect the feature set. It tries to place all items related to a single
feature (and only that feature) into a single directory/package. This results in packages with high cohesion
and high modularity, and with minimal coupling between packages. Items that work closely together are
placed next to each other. They aren't spread out all over the application. It's also interesting to note that,
in some cases, deleting a feature can reduce to a single operation - deleting a directory. (Deletion
operations might be thought of as a good test for maximum modularity: an item has maximum
modularity only if it can be deleted in a single operation.)
In package-by-feature, the package names correspond to important, high-level aspects of the problem
domain. For example, a drug prescription application might have these packages:
com.app.doctor
com.app.drug
com.app.patient
com.app.presription
com.app.report
com.app.security
com.app.webmaster
com.app.util

and so on...
Each package usually contains only the items related to that particular feature, and no other feature. For
example, the com.app.doctor package might contain these items:
DoctorAction.java - an action
Doctor.java - a Model Object

or controller object

374

Collected Java Practices

- Data Access Object


database items (SQL statements)
user interface items (perhaps a JSP, in the case of a web app)
DoctorDAO.java

It's important to note that a package can contain not just Java code, but other files as well. Indeed, in
order for package-by-feature to really work as desired, all items related to a given feature - from user
interface, to Java code, to database items - must be placed in a single directory dedicated to that feature
(and only that feature).
In some cases, a feature/package will not be used by any other feature in the application. If that's the
case, it may be removed simply by deleting the directory. If it is indeed used by some other feature, then
its removal will not be as simple as a single delete operation.
That is, the package-by-feature idea does not imply that one package can never use items belonging to
other packages. Rather, package-by-feature aggressively prefers package-private as the default scope,
and only increases the scope of an item to public only when needed.

Package By Layer
The competing package-by-layer style is different. In package-by-layer, the highest level packages
reflect the various application "layers", instead of features, as in:
com.app.action
com.app.model
com.app.dao
com.app.util

Here, each feature has its implementation spread out over multiple directories, over what might be
loosely called "implementation categories". Each directory contains items that usually aren't closely
related to each other. This results in packages with low cohesion and low modularity, with high
coupling between packages. As a result, editing a feature involves editing files across different
directories. In addition, deleting a feature can almost never be performed in a single operation.

Recommendation: Use Package By Feature


For typical business applications, the package-by-feature style seems to be the superior of the two:
Higher Modularity
As mentioned above, only package-by-feature has packages with high cohesion, high modularity, and
low coupling between packages.
Easier Code Navigation
Maintenance programmers need to do a lot less searching for items, since all items needed for a given
task are usually in the same directory. Some tools that encourage package-by-layer use package naming
conventions to ease the problem of tedious code navigation. However, package-by-feature transcends the
need for such conventions in the first place, by greatly reducing the need to navigate between directories.
Higher Level of Abstraction
Staying at a high level of abstraction is one of programming's guiding principles of lasting value. It
makes it easier to think about a problem, and emphasizes fundamental services over implementation
details. As a direct benefit of being at a high level of abstraction, the application becomes more selfdocumenting: the overall size of the application is communicated by the number of packages, and the
basic features are communicated by the package names. The fundamental flaw with package-by-layer
style, on the other hand, is that it puts implementation details ahead of high level abstractions - which is
375

Collected Java Practices

backwards.
Separates Both Features and Layers
The package-by-feature style still honors the idea of separating layers, but that separation is implemented
using separate classes. The package-by-layer style, on the other hand, implements that separation using
both separate classes and separate packages, which doesn't seem necessary or desirable.
Minimizes Scope
Minimizing scope is another guiding principle of lasting value. Here, package-by-feature allows some
classes to decrease their scope from public to package-private. This is a significant change, and will help
to minimize ripple effects. The package-by-layer style, on the other hand, effectively abandons packageprivate scope, and forces you to implement nearly all items as public. This is a fundamental flaw, since
it doesn't allow you to minimize ripple effects by keeping secrets.
Better Growth Style
In the package-by-feature style, the number of classes within each package remains limited to the items
related to a specific feature. If a package becomes too large, it may be refactored in a natural way into
two or more packages. The package-by-layer style, on the other hand, is monolithic. As an application
grows in size, the number of packages remains roughly the same, while the number of classes in each
package will increase without bound.
If you still need further convincing, consider the following.
Directory Structure Is Fundamental To Your Code
"As any designer will tell you, it is the first steps in a design process which count for most. The first few
strokes, which create the form, carry within them the destiny of the rest." - Christopher Alexander
(Christopher Alexander is an architect. Without having worked as programmer, he has influenced many
people who think a lot about programming. His early book A Pattern Language was the original
inspiration for the Design Patterns movement. He has thought long and hard about how to build beautiful
things, and these reflections seem to largely apply to software construction as well.)
In a CBC radio interview, Alexander recounted the following story (paraphrased here): "I was working
with one of my students. He was having a very difficult time building something. He just didn't know
how to proceed at all. So I sat with him, and I said this: Listen, start out by figuring out what the most
important thing is. Get that straight first. Get that straight in your mind. Take your time. Don't be too
hasty. Think about it for a while. When you feel that you have found it, when there is no doubt in your
mind that it is indeed the most important thing, then go ahead and make that most important thing. When
you have made that most important thing, ask yourself if you can make it more beautiful. Cut the bullshit,
just get it straight in your head, if you can make it better or not. When that's done, and you feel you
cannot make it any better, then find the next most important thing."
What are the first strokes in an application, which create its overall form? It is the directory structure.
The directory structure is the very first thing encountered by a programmer when browsing source code.
Everything flows from it. Everything depends on it. It is clearly one of the most important aspects of
your source code.
Consider the different reactions of a programmer when encountering different directory structures. For
the package-by-feature style, the thoughts of the application programmer might be like this:
"I see. This lists all the top-level features of the app in one go. Nice."
"Let's see. I wonder where this item is located....Oh, here it is. And everything else I am going to
need is right here too, all in the same spot. Excellent."
376

Collected Java Practices

For the package-by-layer style, however, the thoughts of the application programmer might be more like
this:
"These directories tell me nothing. How many features in this app? Beats me. It looks exactly the
same as all the others. No difference at all. Great. Here we go again..."
"Hmm. I wonder where this item is located....I guess its parts are all over the app, spread around in
all these directories. Do I really have all the items I need? I guess we'll find out later."
"I wonder if that naming convention is still being followed. If not, I will have to look it up in that
other directory."
"Wow, would you look at the size of this single directory...sheesh."
Package-By-Layer in Other Domains is Ineffective
By analogy, one can see that the package-by-layer style leads to poor results. For example, imagine a car.
At the highest level, a car's 'implementation' is divided this way (package-by-feature) :
safety
engine
steering
fuel system
and so on...
Now imagine a car whose 'implementation' under the hood is first divided up according to these lower
level categories (package-by-layer) :
electrical
mechanical
hydraulic
In the case of a transmission problem, for example, you might need to tinker around in these three
compartments. This would mean moving from one part of the car to another completely different one.
While in these various compartments, you could 'see' items having absolutely nothing to do with problem
you are trying to solve. They would simply be in the way, always and everywhere distracting you from
the real task at hand. Wouldn't it make more sense if there was a single place having exactly what you
need, and nothing else?
As a second example, consider a large bureacracy divided up into various departments (package-byfeature):
front office
back office
accounting
personnel
mail room
If a package-by-layer style was used, the primary division would be something like :
executives
managers
employees
Now imagine the bureacracy being divided physically according to these three categories. Each manager
is located, for example, with all the other managers, and not with the employees working for them.
Would that be effective? No, it wouldn't.
377

Collected Java Practices

So why should software be any different? It seems that package-by-layer is just a bad habit waiting to be
broken.
The example applications that come with WEB4J uses the package-by-feature style.
See Also :
JSPs should contain only presentation logic
A Web App Framework - WEB4J
Open file in native directory

Passwords never in clear text


Applications should not refer to the clear text versions of passwords - not in the database, not when
logging, nor in any other area. The clear text version of the password should be the end user's secret. It
should never be directly repeated or stored by an application.
Clear Text Versus Hash
A user inputs a password in its original, unaltered form - that is, in "clear text". However, a database
should never store passwords in clear text. Instead, the value stored by the database should be calculated
as
stored-value = hash(clear-text-password + salt-value)

The intent here is to store text which cannot be easily reverse-engineered back into the original
password. The salt-value is a random string added to the password. It's added to prevent simple
dictionary-style reverse engineering of the hashed value of the plain text password.
(Regarding Tomcat5: its implementation of form-based login doesn't allow for the salt value.)
Hash Functions
SHA-1, SHA-256, SHA-384, and SHA-512 are all examples of hash functions. A hash function is also
called a MessageDigest . (The MD5 hash has been shown to be defective, and should usually be
avoided.)
A hash function is not an encryption. Encrypted items are always meant for eventual decryption. A hash
function, on the other hand, is meant only as a one-way operation. The whole idea of a hash function is
that it should be very difficult to calculate the original input value, given the hash value.
If y = hash(x) is a hash function, and y and x both represent text strings, then
given the output y, it's very hard to deduce the original input x
similar inputs will give markedly different outputs
input x can have arbitrary length
output y will have fixed length
there is only a trivial chance of "collisions", where different inputs give the same output
The above properties allow for passwords (or pass phrases) of arbitrary length, while still letting the
underlying database column which stores the hash value to be of fixed width.
Example
This example uses MessageDigest and the SHA-1 hash function:
378

Collected Java Practices

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/** Example hash function values.*/
public final class HashExamples {
public static void main(String... aArgs) {
try {
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byte[] hashOne = sha.digest("color".getBytes());
log("Hash of 'color': " + hexEncode(hashOne));
sha.reset();
byte[] hashTwo = sha.digest("colour".getBytes());
log("Hash of 'colour': " + hexEncode(hashTwo));
}
catch (NoSuchAlgorithmException ex){
log("No such algorithm found in JRE.");
}
}
private static void log(Object aObject) {
System.out.println(String.valueOf(aObject));
}
/**
* The byte[] returned by MessageDigest does not have a nice
* textual representation, so some form of encoding is usually performed.
*
* This implementation follows the example of David Flanagan's book
* "Java In A Nutshell", and converts a byte array into a String
* of hex characters.
*/
static private String hexEncode( byte[] aInput){
StringBuffer result = new StringBuffer();
char[] digits = {'0', '1', '2', '3',
'4','5','6','7','8','9','a','b','c','d','e','f'};
for (int idx = 0; idx < aInput.length; ++idx) {
byte b = aInput[idx];
result.append( digits[ (b&0xf0) >> 4 ] );
result.append( digits[ b&0x0f] );
}
return result.toString();
}
}

Example run:
Hash of 'color': 6dd0fe8001145bec4a12d0e22da711c4970d000b
Hash of 'colour': 79d41a47e8fec55856a6a6c5ba53c2462be4852e

Prefer empty items to null ones


There are many cases in which an "empty" object can be used instead of a null object reference. This is
usually preferable, since it helps eliminate one of the most common problems encountered during
development: the NullPointerException .
The following qualify as "empty" objects:
the empty String
zero-length arrays
collections containing 0 items
maps containing 0 items
379

Collected Java Practices

Example declarations of empty items:


String item = "";
BigDecimal[] items = new BigDecimal[0];
List<Integer> items = new ArrayList<>();
Map<String, Integer> items = new LinkedHashMap<>();

The Collections class contains several type safe methods which return empty items (which are also
immutable and Serializable) :
Collections.emptyList()
Collections.emptySet()
Collections.emptyMap()

The above methods are occasionally useful.


Example
Here's an example of using an empty, zero-length array instead of a null reference. This simplifies the
caller, since they never have to check for null values. (An even better alternative is to use replace the
array with a possibly-empty collection.)
import java.util.*;
public final class StockPortfolio {
//..elided
/**
* A field is implemented internally as a Collection,
* but offered to the user of this class as an Array.
* This array is never null, even if the underlying Collection is
* empty.
*/
public String[] getStocks() {
/*
NO_STOCKS plays a double role here. If there are elements in fStocks, then
toArray will allocate a new array, using NO_STOCKS simply as an indicator
of the proper type to use. If fStocks has no elements at all, then toArray
will simply return the NO_STOCKS array itself.
*/
return (String[]) fStocks.toArray(NO_STOCKS);
}
// PRIVATE
/** The underlying collection. */
private List fStocks;
/** A constant, empty array, to be used instead of a null array. */
private static final String[] NO_STOCKS = new String[0];
}

See Also :
Prefer Collections over older classes
Avoid null if possible

Quote dynamic text when logging


380

Collected Java Practices

Extraneous leading or trailing whitespace around text is easy to add and difficult to detect - a bad
combination. If text has some logical meaning to the program (for example, the name of a configuration
parameter), then extraneous whitespace around it is almost always undesired.
(This problem occurs for text data, but not for numeric data such as BigDecimal or Integer.)
To help detect such unwanted whitespace, simply quote dynamic text values when logging. It's likely a
good idea to define a simple utility method for this purpose.
Example
This example uses String.valueOf(Object):
public final class Util {
/**
* Surround the result of <tt>String.valueOf(aObject)</tt> with single quotes.
*/
public static String quote(Object aObject){
return "'" + String.valueOf(aObject) + "'";
}
}

See Also :
Logging messages

Self-encapsulate fields
Within the implementation of a class, it's common to refer directly to private fields. There is another
style, in which all internal references to fields are indirect, and proceed through the appropriate get and
set methods. This is called self-encapsulation.
Self-encapsulation:
is needed for lazy initialization
is slightly more difficult to read than direct field access
can establish a synchronization policy for the field
allows subclasses to override the get and set methods, and provide their own definition of how
fields behave
Example
public final class Infant {
public Infant (final String aName, final int aWeight, final String aMood){
setName(aName);
setWeight(aWeight);
setMood(aMood);
}
public void becomeHungry(){
//instead of fMood = "Cranky", use:
setMood("Cranky");
}
381

Collected Java Practices

public String toString(){


//use get methods instead of direct access
final StringBuilder result = new StringBuilder();
result.append("Infant - Name: + ");
result.append(getName());
result.append(" Weight: ");
result.append(getWeight());
result.append(" Mood: ");
result.append(getMood());
return result.toString();
}
public String getName(){
return fName;
}
public void setName(String aName){
fName = aName;
}
public int getWeight(){
return fWeight;
}
public void setWeight(final int aWeight){
fWeight = aWeight;
}
public String getMood(){
return fMood;
}
public void setMood(final String aMood){
fMood = aMood;
}
//..other methods elided
// PRIVATE
private String fName;
private int fWeight;
private String fMood;
}

See Also :
Lazy initialization

Structs are occasionally useful


Some programming languages (such as C and Algol 68) formally define the idea of a struct, as a simple
way of grouping together related data. Although the Java Language Specification doesn't formally define
structs, the equivalent of a struct can certainly be created in Java. Here's an example of a struct which
gathers together data related to books:
import java.math.BigDecimal;
/** Simple struct containing book information. */
final class Book {
String TITLE;
String ISBN;
Integer NUM_PAGES;
BigDecimal PRICE;
}

Many dislike using such structures since they're open to abuse. Any public, mutable members are
382

Collected Java Practices

dangerous, since there's no encapsulation. This is indeed a valid objection to their wide-spread use.
However, structs can still be occasionally useful, and one shouldn't be afraid to use them when
appropriate. In particular, if the data is neither public nor mutable, then the usual objections to structs
lose much of their force.
For example, if the above Book class was in fact a nested private class, and was just a dumb little data
carrier used only inside a single class, then there would be little objection to using it:
like all private data, the struct would be invisible outside the class
since the struct's members are all immutable, then there's no way to change the state of its member
objects

See Also :
Immutable objects

Test using main method


The
public static void main(String...){}

method may be used to test the operation of a class. It should probably be used informally, since it lacks
the features of a more complete testing tool such as JUnit.
When finished testing, main can simply be deleted, or it may be kept for future needs. If kept, it's
probably a good idea to change its scope to private, in order to avoiding polluting the class API with a
method intended only for development. Of course, if main needs to be run again later, you will need to
temporarily change its scope back to public.
Example
Here, SplashScreen.main exercises the display of a splash screen. It's placed at the very bottom of the
class, since it's intended only as a development tool. The informal character of this main is reflected in
the call to Thread.sleep, which simply wastes time for a few seconds. Doing this in the real application
would be silly, since a splash screen's purpose is to give the illusion of rapid feedback. In the real
application, SplashScreen is disposed of not after a fixed number of seconds, but rather after the
appearance of the primary window.
package hirondelle.stocks.main;
import java.awt.*;
import java.net.URL;
import java.awt.image.ImageObserver;
/**
* Present a simple graphic to the user upon launch of the application, to
* provide a faster initial response than is possible with the main window.
*
* <P>Adapted from an
* <a href=http://developer.java.sun.com/developer/qow/archive/24/index.html>item</a>
* on Sun's Java Developer Connection.
*
* <P>This splash screen appears within about 2.5 seconds on a development
* machine. The main screen takes about 6.0 seconds to load, so use of a splash
* screen cuts down the initial display delay by about 55 percent.
383

Collected Java Practices

*
* <P>When JDK 6+ is available, its java.awt.SplashScreen class should be used instead
* of this class.
*/
final class SplashScreen extends Frame {
/**
* Construct using an image for the splash screen.
*
* @param aImageId must have content, and is used by
* {@link Class#getResource(java.lang.String)} to retrieve the splash screen image.
*/
SplashScreen(String aImageId) {
/*
* Implementation Note
* Args.checkForContent is not called here, in an attempt to minimize
* class loading.
*/
if (aImageId == null || aImageId.trim().length() == 0){
throw new IllegalArgumentException("Image Id does not have content.");
}
fImageId = aImageId;
}
/**
* Show the splash screen to the end user.
*
* <P>Once this method returns, the splash screen is realized, which means
* that almost all work on the splash screen should proceed through the event
* dispatch thread. In particular, any call to <tt>dispose</tt> for the
* splash screen must be performed in the event dispatch thread.
*/
void splash(){
initImageAndTracker();
setSize(fImage.getWidth(NO_OBSERVER), fImage.getHeight(NO_OBSERVER));
center();
fMediaTracker.addImage(fImage, IMAGE_ID);
try {
fMediaTracker.waitForID(IMAGE_ID);
}
catch(InterruptedException ex){
System.out.println("Cannot track image load.");
}
SplashWindow splashWindow = new SplashWindow(this,fImage);
}
// PRIVATE
private final String fImageId;
private MediaTracker fMediaTracker;
private Image fImage;
private static final ImageObserver NO_OBSERVER = null;
private static final int IMAGE_ID = 0;
private void initImageAndTracker(){
fMediaTracker = new MediaTracker(this);
URL imageURL = SplashScreen.class.getResource(fImageId);
fImage = Toolkit.getDefaultToolkit().getImage(imageURL);
}
/**
* Centers the frame on the screen.
*
*<P>This centering service is more or less in {@link
hirondelle.stocks.util.ui.UiUtil};
* this duplication is justified only because the use of
* {@link hirondelle.stocks.util.ui.UiUtil} would entail more class loading, which is

* not desirable for a splash screen.


*/
private void center(){
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle frame = getBounds();
setLocation((screen.width - frame.width)/2, (screen.height - frame.height)/2);
}
private final class SplashWindow extends Window {
SplashWindow(Frame aParent, Image aImage) {
super(aParent);
fImage = aImage;
setSize(fImage.getWidth(NO_OBSERVER), fImage.getHeight(NO_OBSERVER));
384

Collected Java Practices

Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();


Rectangle window = getBounds();
setLocation((screen.width - window.width) / 2,(screen.height window.height)/2);
setVisible(true);
}
@Override public void paint(Graphics graphics) {
if (fImage != null) {
graphics.drawImage(fImage,0,0,this);
}
}
private Image fImage;
}
/**
* Developer test harness shows the splash screen for a fixed length of
* time, without launching the full application.
*/
private static void main(String... aArgs){
SplashScreen splashScreen = new SplashScreen("StocksMonitor.gif");
splashScreen.splash();
try {
Thread.sleep(2000);
}
catch(InterruptedException ex) {
System.out.println(ex);
}
System.exit(0);
}
}

See Also :
Use a testing framework (JUnit)
Splash screen

Try alternatives to ResourceBundle


There are some rather disturbing problems with ResourceBundle :
if the translations are stored in a database, there is no way for an application to create a new
localization without code changes (that is, without creating a new database-backed
ResourceBundle class for each new Locale).
prior to JDK 6, there is no way of refreshing the underlying data at runtime.
These are serious defects.
In addition, there are many nuisances associated with using ResourceBundle s and their associated
properties files:
the encoding used in a properties file is ISO-8859-1, not UTF-8. If the app is using a language
which does not use ISO-8859-1, then the native2ascii tool must be used to process the files.
(Rather impolite for an internationalization mechanism, is it not?)
natural text keys versus coder keys: from the point of the caller, using a natural text lookup key has
the advantage that it increases legibility. However, such a lookup key appears as blah=blah in a
properties file. Such duplication makes most programmers justifiably nervous. (If a database is
used, a ResultSet having the same blah=blah structure can be easily created, but without any
385

Collected Java Practices

actual data repeated in the underlying tables.)


in a properties file, there is no easy way to find duplicates. In general, there is no easy way to
perform any sort of query on the data (as there is in a database).
mailing raw properties files to translators does not seem desirable, but building a front end tool for
such files is not easy - so it never gets done
if an item has more than one line, then the line continuation character "\" must be appended to each
line
apostrophe characters are a problem: the MessageFormat class treats apostrophes as special
characters. If an apostrophe appears in a pattern passed to a MessageFormat , it must be escaped, or
else a nice little RuntimeException is thrown. If you send properties files to translators, the
probabilty of errors related to apostrophes is very high. If a database is used instead of properties
files, however, then it's fairly easy to build a front end tool which can do the necessary validations.
(Building such a tool is especially attractive when it can be used with multiple projects.)
use of module names: the ResourceBundle mechanism allows the caller to specify a module name,
which is mapped into a corresponding class name or properties file name. It's possible to seriously
question this design. In general, a method should not know the identity of its caller.
In summary, it seems that ResourceBundle was intended originally for desktop applications and
properties files, and that its design is not robust enough for easy use in the context of a web application
backed by a database.
Fortunately, text translation is not a difficult task, and creating an alternative to ResourceBundle is
actually fairly easy. For an example, please see the hirondelle.web4j.ui.translate package of the WEB4J
tool. It uses a technique called "last second localization".
See Also :
Internationalization

Use a fake system clock


When testing an application, it's often useful to define a fake system clock in order to exercise code that
uses dates.
While it's always possible to change the system clock directly, many view that style as undesirable:
it affects all programs running on a machine, and not just the application being tested
it can be time consuming and annoying to repeatedly change the system clock while testing
Instead of changing the system clock, it's possible to define a fake system clock just for your application.
In production, the fake system clock returns normal time. During testing, the fake system clock is
changed to return any time you need for effective test coverage. Of course, your application code will
always need to reference the fake system clock, and never the real one, in order for this to work.
Example
The TimeSource interface allows you to define various implementations of a fake system clock:
public interface TimeSource {
/** Return the system time. */
long currentTimeMillis();
}
386

Collected Java Practices

This implementation mimics a system clock running one day in advance:


public final class TimeSrc implements TimeSource {
/** One day in advance of the actual time.*/
@Override public long currentTimeMillis() {
return System.currentTimeMillis() + ONE_DAY;
}
private static final long ONE_DAY = 24*60*60*1000;
}

Using various TimeSource implementations, you can mimic any desired behavior for a system clock.
Possible behaviors include:
skip ahead to the future
go back to the past
use a fixed date, and a fixed time
use a fixed date, but still let the time vary
increment by one second each time you 'look' at the clock
change the rate at which time passes, by speeding up or slowing down by a certain factor
use the normal system clock without alteration
For this to work, an application must avoid calling these items directly:
System.currentTimeMillis()
the default constructor for the Date

class (which in turn uses System.currentTimeMillis())

Instead, an application must reference only a TimeSource .


According to your needs, you may have to use the fake system clock in some or all of these places:
your application code
your logging output
your framework classes
your database settings
It's simple to configure the JDK logger to use your fake system clock. A simple custom Formatter can
use your TimeSource to alter the time of the LogRecord :
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
public final class SimpleFormatterTimeSource extends SimpleFormatter {
@Override public String format(LogRecord aLogRecord) {
aLogRecord.setMillis(fTimeSource.currentTimeMillis());
return super.format(aLogRecord);
}
private TimeSource fTimeSource = BuildImpl.forTimeSource();
}

See Also :

387

Collected Java Practices

Use a testing framework (JUnit)


Plugin Factory

Use a testing framework (JUnit)


Use a testing framework to ensure your class fulfills its contract.
One widely used framework is JUnit. JUnit has these advantages:
it's simple to use
it can test a single class at a time, or a suite of tests can be created for a group of classes
it greatly increases your confidence in the correctness of your code
it often improves the design of the class you are testing - since you spend more time thinking about
how an object is actually used, instead of its implementation, defects in its interface become more
obvious
failure of a test is glaringly obvious
the positive feedback provided by successful tests produces unmistakably warm, fuzzy feelings
even in the embittered heart of an experienced programmer ;)

Test Doubles
Unit testing is about testing classes in isolation, one at a time. However, classes usually don't operate in
isolation - they usually have collaborators, other classes needed for correct operation. In some cases,
those collaborators aren't immediately available during unit testing. For example, in a servlet application,
the underlying request and response objects are created by the servlet container, and passed to the
application for further processing. During unit testing, the 'real' request and response are not typically
available, since the servlet container isn't running.
Thus, test doubles are often required during unit testing. Test doubles are used to mimic the collaborators
of a class being tested. See xUnitPatterns.com by Gerard Meszaros for more information.

Tests and Packages


Some programmers put their unit tests in a package separate from the classes being tested. This is neither
necessary nor desirable. It's not necessary since the tests can always be placed in the same package as the
items being tested. It's not desirable for two reasons:
it's not compatible with package-by-feature
it often forces you to increase the scope of items being tested to public, merely for the sake of
running unit tests
From Effective Java, by Johsua Bloch:
"It is not acceptable to make a class, interface, or member a part of a package's exported API to facilitate
testing. Luckily, it isn't necessary either, as tests can be made to run as part of the package being tested,
thus gaining access to its package-private elements."
See Also :
Construct classes from the outside in
Use Ant for build scripts
Test using main method
388

Collected Java Practices

Design by Contract
Choose form validation style carefully
Use a fake system clock

Use Ant for build scripts


Ant is an excellent tool for creating build scripts.
Ant has these characteristics:
it's cross-platform, and implemented in Java
it's fairly easy to learn and use
it uses an XML file to define the steps of a build
like JUnit, it has become part of the standard tool kit for many Java programmers
it allows build scripts to be constructed fairly quickly and cleanly (but it does have some quirks)
Every Java programmer should at least be familiar with Ant.
Using Ant, a build file is made up of tasks, which perform operations commonly needed for builds. The
list of tasks is extensive:
compile source files
copy, rename, and delete files
run unit tests
create javadoc
create jar and zip files
send emails
fetch source code from CVS or other source code management tools
and many other tasks as well

Example Build File


Here's an example of an Ant build file:
<project name="test-ant-builds" default='all' basedir="." >
<description>
Demonstrate the use of the Ant build tool with a simple Java project.
</description>
<!-First define properties, datatypes, and default tasks; then define targets.
Any Ant tasks placed outside of any target are always executed first.
-->
<!-- Override default property values with an external properties file, if present. -->
<property file='build.properties'/>
<!-- Default property values, if not overridden elsewhere: -->
<property name='build' location='build' />
<property name='app.version' value='1.0'/>
<property name='app.name' value='Example App'/>
<property name='distro-name' value='example-app-${app.version}'/>
<tstamp><format property='build.time' pattern='yyyy-MM-dd HH:mm:ss'/></tstamp>
<path id='compile.classpath'>
<fileset dir='lib'>
<include name='*.jar'/>
</fileset>
</path>
<!-- Simply extends the compile.classpath with your own compiled classes. -->
<path id='run.classpath'>
<path refid='compile.classpath'/>
<path location='src'/>
</path>
<fileset id='class.files' dir='src'>
<include name='**/*.class'/>
</fileset>

389

Collected Java Practices


<fileset id='files.for.jar' dir='src'>
<exclude name='**/*.java'/>
<exclude name='**/doc-files/'/>
</fileset>
<fileset id='test.classes' dir='src'>
<include name='**/TEST*.java'/>
</fileset>
<!-- Text files using Ant's '@' syntax are here called template files. -->
<fileset id='template.files' dir='.'>
<include name='**/*_template.*'/>
</fileset>
<!-- Inspect the environment, to see if a deployment host is currently running. -->
<condition property='deployment.server.running' value='true' else='false'>
<socket port='8081' server='127.0.0.1' />
</condition>
<!-- A connection to this URL is used when building javadoc. -->
<condition property='jdk.javadoc.visible' value='true' else='false'>
<http url='http://java.sun.com/javase/6/docs/api/' />
</condition>
<echo>
Application: ${app.name} ${app.version}
Build File : ${ant.file}
Run Date
: ${build.time}
Run by
: ${user.name}
Build Dir : ${build}
Base Dir
: ${basedir}
Java Home : ${java.home}
Deployment host running: ${deployment.server.running}
Connected to the web
: ${jdk.javadoc.visible}
</echo>
<echo message='Create build directory, and its subdirectories.'/>
<mkdir dir="${build}/javadoc"/>
<mkdir dir="${build}/dist"/>
<mkdir dir="${build}/templates"/>
<!-- Now define the targets, which use the properties and datatypes defined above. -->
<target name='clean' description="Delete all build artifacts." >
<delete dir='${build}'/>
<delete>
<fileset refid='class.files'/>
</delete>
<mkdir dir="${build}/javadoc"/>
<mkdir dir="${build}/dist"/>
<mkdir dir="${build}/templates"/>
</target>
<target name='compile' description='Compile source files and place beside source.'>
<javac srcdir="src">
<classpath refid='compile.classpath'/>
</javac>
<!-- Here's a simple way of debugging a path, fileset, or patternset, using its refid: -->
<echo>Classpath: ${toString:compile.classpath}</echo>
</target>
<target name='test' description='Run all JUnit tests.' depends='compile'>
<junit haltonfailure='false'>
<classpath>
<pathelement location="src"/>
</classpath>
<batchtest>
<fileset refid='test.classes'/>
</batchtest>
<formatter type='brief' usefile='no'/>
</junit>
</target>
<target name='launch' description='Build and run the program.' depends='compile, test'>
<java classname='hirondelle.ante.Launcher' classpathref='run.classpath' failonerror='true'>
<arg value="Solar System"/>
</java>
</target>
<target name='jar' description='Create a jar file for distribution.' depends='compile'>
<jar destfile='${build}/dist/${distro-name}.jar' manifest='MANIFEST.MF' duplicate='preserve'>
<fileset refid='files.for.jar'/>
<!-- The static manifest.mf file is merged with additional dynamic items, specified here : -->
<manifest>
<attribute name='Specification-Version' value='${app.version}'/>
<attribute name='Specification-Title' value='${app.name}' />
<attribute name='Implementation-Version' value='${app.version}'/>
<attribute name='Implementation-Title' value='${app.name}' />
</manifest>
</jar>
</target>
<target name='javadoc' description='Generate javadoc.' >
<javadoc
use='true' author='true' version='true'
overview='overview.html'
access='package'
sourcepath='src'
packagenames='*.*'
destdir='${build}/javadoc'
windowtitle='${app.name} ${app.version}'
noqualifier='java.*:javax.*:com.sun.*'
linksource='true'
>
<classpath refid='compile.classpath'/>
<link href='http://java.sun.com/javase/6/docs/api/'/>
<header><![CDATA[<h1>${app.name} ${app.version}</h1>]]></header>
</javadoc>

390

Collected Java Practices


</target>
<target name='text-templates' description='Process template files, and assign values to @ variables.'>
<copy overwrite='true' todir='${build}/templates'>
<fileset refid='template.files'/>
<!-- New files have 'template' removed from their name : -->
<globmapper from='*_template.txt' to='*.txt'/>
<filterset>
<filter token='app.name' value='${app.name}'/>
<filter token='app.version' value='${app.version}'/>
<filter token='build.time' value='${build.time}'/>
</filterset>
</copy>
</target>
<target name='distro-binary' description='Create zip file with executable jar, docs.' depends='jar, javadoc,
text-templates'>
<zip destfile='${build}/dist/${distro-name}-binary.zip' duplicate='preserve'>
<zipfileset dir='${build}/dist/' includes='${distro-name}.jar'/>
<zipfileset dir='${build}/javadoc' prefix='javadoc' />
<zipfileset dir='${build}/templates' includes='README.txt'/>
</zip>
</target>
<target name='distro-source' description='Create zip file with project source code.'>
<zip destfile='${build}/dist/${distro-name}-src.zip' duplicate='preserve' >
<!-- exclude items specific to the author's IDE setup: -->
<zipfileset dir='.' excludes='.classpath, .project'/>
</zip>
</target>
<!-- Add mail.jar and activation.jar to your ANT_HOME/lib! -->
<!-- Please edit, using values appropriate to your environment. -->
<target name='email' >
<mail mailhost='smtp.blah.com' mailport='25' user='blah@blah.com'
password='blah' messageMimeType='text/html'
tolist='blah@whatever.com' from='blah@blah.com' subject='Build completed.'
>
<message>
<![CDATA[ Test email. <P><a href='http://www.google.com'>link</a> ]]>
</message>
</mail>
</target>
<target name='all' description='Create all build artifacts.'
depends='clean, compile, test, jar, javadoc, distro-binary, distro-source'>
<echo>Finished creating all build artifacts.</echo>
</target>
</project>

Example of running the above build script:


C:\johanley\Projects\ant-test>ant -file build.xml all
Buildfile: build.xml
[echo]
[echo]
Application: Example Appl 1.1
[echo]
Build File : C:\johanley\Projects\ant-test\build.xml
[echo]
Run Date
: 2010-07-21 19:14:11
[echo]
Run by
: John
[echo]
Build Dir : C:\@build
[echo]
Base Dir
: C:\johanley\Projects\ant-test
[echo]
Java Home : C:\jdk1.5.0\jre
[echo]
Deployment host running: true
[echo]
Connected to the web
: true
[echo]
[echo] Create build directory, and its subdirectories.
[mkdir] Created dir: C:\@build\javadoc
[mkdir] Created dir: C:\@build\dist
[mkdir] Created dir: C:\@build\templates
clean:
[delete]
[delete]
[mkdir]
[mkdir]
[mkdir]

Deleting directory C:\@build


Deleting 3 files from C:\johanley\Projects\ant-test\src
Created dir: C:\@build\javadoc
Created dir: C:\@build\dist
Created dir: C:\@build\templates

compile:
[javac] Compiling 5 source files
[echo] Classpath: C:\johanley\Projects\ant-test\lib\junit.jar
test:
[junit] Testsuite: hirondelle.ante.TESTLauncher
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0 sec
jar:
[jar] Building jar: C:\@build\dist\example-app-1.1.jar
javadoc:
[javadoc]
[javadoc]
[javadoc]
[javadoc]
[javadoc]
[javadoc]
[javadoc]
[javadoc]
[javadoc]

Generating Javadoc
Javadoc execution
Loading source files for package hirondelle.ante...
Loading source files for package hirondelle.ante.deluvian...
Constructing Javadoc information...
Standard Doclet version 1.5.0_07
Building tree for all the packages and classes...
Generating C:\@build\javadoc\hirondelle/ante/\package-summary.html...
Copying file C:\johanley\Projects\ant-test\src\hirondelle\ante\doc-files\VersionHistory.html
to directory C:\@build\javadoc\hirondelle\ante\doc-files...
[javadoc] Building index for all the packages and classes...
[javadoc] Building index for all classes...

text-templates:
[copy] Copying 1 file to C:\@build\templates

391

Collected Java Practices


distro-binary:
[zip] Building zip: C:\@build\dist\example-app-1.1-binary.zip
distro-source:
[zip] Building zip: C:\@build\dist\example-app-1.1-src.zip
all:
[echo] Finished creating all build artifacts.
BUILD SUCCESSFUL
Total time: 6 seconds

For those already familiar with Ant, here are some reminders regarding its use.

General
tasks placed outside of a target will always be run. You can think of them as being 'global' in
scope.
many tasks are incremental (<javac>, for example), and inspect file timestamp information.
more than a single target can be specified when calling Ant.
file separators can go either way in a build file, \ or /. Both are accepted.
for booleans, on/true/yes means true, and all other values are false.
sometimes a feature of Ant will not be available in an IDE.

Datatypes
usually Paths or Filesets
can be declared within a task, or outside of a task
the id's assigned to a datatype must be unique across all datatypes
Paths
similar to PATH and CLASSPATH
ordered list of directories and files
can include Filesets
many Path items include 'path' in their name, but some don't
Filesets
set of files rooted in a single directory.
Ant doesn't guarantee the order in which files are processed.
membership in a Fileset is done with a Selector, based on file name, size, last-modified date, file
content, and so on.
Filesets are resolved when the declaration is first evaluated. After that, they stay the same, while
the content of the file system can change.
Patternset
just a list of file name patterns
the Patternset doesn't contain files, just file name patterns
usually embedded inside Filesets, but can be used as a datatype itself
'excludes' override 'includes'
a number of file types are excluded by default (CVS files, etc.)
the <defaultexcludes> task controls the list of default excludes
Patternsets use 4 special char's:
* - 0..N characters
392

Collected Java Practices

? - single character
** - used to signify all directories from that point down
/ and \ - path separators
Other datatypes
Filelist - ordered list of files/directories, that may or may not exist.
Dirset - directories only. Used only in javadoc task.
Filterset - alter file content (version number, timestamp), during a copy or move. Uses '@'
character to delimit text in the file that needs to be updated.
FilterReader - again, alters file content. Can be chained together. Flavors: tabs-to-spaces, replace
tokens, escape non-ASCII chars, etc.

Properties
Properties are immutable once set.
Sources of Properties
<property> tasks
properties files referenced by a <property> task
ANT_OPTS environment variable
command line arguments, 'ant -Dx=y', or 'ant -propertfile blah.properties'. This style can never be
overidden by other settings.
Built-in Properties
all JRE system properties (user.name, user.home, etc.)
ant.project.name
ant.file
ant.home
ant.java.version
ant.version
basedir
Properties Files
are typically used to avoid editing the build file itself.
resolve the ${xyz} syntax when used with <property>, but NOT when used with '-propertyfile' on
the Ant command line.
equate to using <property name='x' value='y'> since the 'location' attribute isn't used, this isn't
recommended for files and directories, since this will not resolve relative references. If you do
specify a location in a properties file, then it should be absolute, not relative. In addition, you'll
need to escape backslashes.
Example
# example properties file
app.version = 1.0
#
#
#
#
#

This style also works, and it's not sensitive to order


of appearance in this file
Thus, the contents of this file is manipulated somewhat
after being loaded.
This does NOT work when using 'ant -propertyfile'
393

Collected Java Practices

app.name = My Wonderful App ${app.version}


# you need to escape backslashes
build.dir = C:\\@build

Reminders and Practices


properties are immutable once set.
command line arguments can never be overridden: 'ant -Dx=y', or 'ant -propertfile blah.properties'.
'ant -propertyfile blah.properties' doesn't resolve ${xyz} style references within properties files.
'ant -projecthelp' lists all targets having a description. It also tests the general syntax of your build
file.
if you use a copy task with the '@' placeholder, ensure overwrite='yes'.
when running programs with Ant, using fork='true' can often fix problems.
a common convention for target names is to use dashes, as in 'like-this'
performing a clean before a build is often desirable. For important or official builds, it's essential.
instead of setting properties in an init target, you can simply place them near the start of the build
file, outside of any target. That will cause them to be executed for all targets anyway.
consider adding a description to your project and its targets.
for properties that represent a file or directory, use the 'location' attribute, not 'value'
don't misuse antcall; to specify order, use the order of dependencies instead
to avoid editing the build file itself, use properties files. Place <property file='build.properties'> in
your build file, to let users easily override values. The build file must work without the
build.properties file being present.
for tasks with many attributes, consider putting each attribute on a separate line
PatternSets have a set of default file types to exclude; you can control it using the
<defaultexcludes> task; you should usually only extend the default file types.
if desired, use <property environment='env'/> to force ant properties and system properties into
different namespaces.
you usually want your source code management tool to ignore generated class files. CVS users can
set a $CVSIGNORE environment variable, or place a .cvsignore file in their home directory.
jar-ing files with duplicate='preserve' is usually desirable.

See Also :
Use a testing framework (JUnit)

Use boxing with care


Boxing was introduced in JDK 1.5 to eliminate trivial conversions between primitives ( int , boolean, etc)
and corresponding wrapper objects ( Integer, Boolean, etc). Boxing allows some code to be a bit more
concise and legible. It's also useful when using collections (which only take objects, not primitives).
There are two complimentary aspects of boxing:
auto-boxing, which refers to automatic conversion of an int to an Integer, for example
auto-unboxing, which refers to automatic conversion of a Boolean to a boolean, for example
Boxing partially hides the distinction between primitives and corresponding wrapper objects, but it
doesn't remove it. There are two distinctions which are not changed by boxing:
objects can be null , while primitives cannot
394

Collected Java Practices

objects have both state and identity, while primitives have only state (the value)
Occasionally, these differences can cause problems when using boxing.
Some points to remember:
be careful with null s. Auto-unboxing a null object will cause a NullPointerException .
comparing items with == and equals must be done with care.
The rules for comparison of primitives and wrapper objects are as follows.
If x and y are either both primitives, or both objects, then no boxing occurs:

Two primitives
Two objects
Operation
x == y

compare value

compare identity

x.equals(y)

does not compile

compare value

If one item is a primitive, and the other item is a corresponding wrapper object, then boxing can occur:

Operation
Behavior
x == y
treat as two primitives, and compare value
x.equals(y)
does not compile if x is a primitive; otherwise
treat as two objects, and compare value
Example
import java.util.*;
public final class BoxingExamples {
public static final void main(String... aArgs){
//pass Integer where int expected
explode(new Integer(3));
//pass literal where Boolean expected
tellTruth(true);
//calculate "int-erchangably" with int and Integer
Integer integerYear = new Integer(1989);
Integer otherIntegerYear = integerYear + 10;
int intYear = integerYear + new Integer(15);
log(integerYear.toString());
log(otherIntegerYear.toString());
System.out.println(intYear);
/*
* Comparison of primitives and wrapper objects using == and equals.
*
* When both items are of the same type :
*
2 primitives
2 objects
* ---------------------------------------------------------* ==
: value
identity
* equals() : not applicable
value
*
*
* When one item is a primitive, and the other is an object :
* ==
: treat as two primitives
* x.equals(y) : treat as two objects; do not compile if x is primitive
*/
intYear = 1880;
integerYear = new Integer(1880);
if (intYear == integerYear){
log("intYear == integerYear: TRUE"); // yes
}
395

Collected Java Practices

else {
log("intYear == integerYear : FALSE");
}
if (integerYear.equals(intYear)){
log("integerYear.equals(intYear): TRUE"); //yes
}
else {
log("integerYear.equals(intYear): FALSE");
}
//does not compile "int cannot be dereferenced" :
//intYear.equals(integerYear);
intYear = 1881; //new value
if (intYear == integerYear){
log("intYear == integerYear: TRUE");
}
else {
log("intYear == integerYear : FALSE"); // yes
}
if (integerYear.equals(intYear)){
log("integerYear.equals(intYear): TRUE");
}
else {
log("integerYear.equals(intYear): FALSE"); //yes
}
}
// PRIVATE
private static void explode(int aNumTimes){
log("Exploding " + aNumTimes + " times.");
}
private static void tellTruth(Boolean aChoice){
//instead of if ( aChoice.booleanValue() ) {
if (aChoice) {
log("Telling truth.");
}
else {
log("Lying like a rug.");
}
}
private static void log(String aText){
System.out.println(aText);
}
}

An example run of this class :


>java -classpath . BoxingExamples
Exploding 3 times.
Telling truth.
1989
1999
2004
intYear == integerYear: TRUE
integerYear.equals(intYear): TRUE
intYear == integerYear : FALSE
integerYear.equals(intYear): FALSE

See Also :
Implementing equals

396

Collected Java Practices

Use enums to restrict arguments


Type-safe enumerations are useful for constraining arguments which must take a small set of values,
such as a boolean or a restricted set of Strings or int s.
As an example from the servlet API, recall this method for the creation of an HttpSession:
aRequest.getSession(true);

Any reader who is not familiar with the specification of this method will be forced to look up the
meaning of the boolean literal before they can understand the method call. An alternative style is to
define a well-named variable which explains the intent:
boolean CREATE_IF_ABSENT = true;
aRequest.getSession(CREATE_IF_ABSENT);

However, the caller is not compelled to use such a style. If a type-safe enumeration is used for the
argument, then the caller is indeed forced into a style which has greater clarity.
aRequest.getSession(HttpServletRequest.CreateIfAbsent.YES);

Here, CreateIfAbsent would be a static nested type-safe enumeration with two elements, YES and NO.
This style has these advantages:
the method is forced to be more intelligible at the point of call
the need to look up the specification is greatly reduced
it serves as a reminder to those who are already familiar with the specification
it has compile-time safety (not an issue with a boolean argument, but important if the argument is
a String or int )
This style can be a bit verbose. It might be one of those rare occasions where a static import of the enum
class is appropriate, to avoid using fully qualified names.
Example 1
Here's a stub class illustrating an alternate form of createSession :
import javax.servlet.http.HttpSession;
/** Clarify method call with enum argument.
public final class HttpRequestWithEnum {

*/

/**
* Example of calling the method with an enum arg.
*/
public static void main(String... aArgs){
HttpRequestWithEnum request = new HttpRequestWithEnum();
HttpSession session = request.createSession(CreateIfAbsent.YES);
}
public HttpSession createSession(CreateIfAbsent aCreateIfNeeded){
return null; //stubbed out
}
/** The enumeration itself. */
public enum CreateIfAbsent {YES, NO};
}

Example 2
397

Collected Java Practices

Here's a second example that uses an enum instead of a boolean literal.


import java.io.*;
public final class LawnMower {
enum Power{ON, OFF};
void switchPower(Power aPower){
if (Power.ON == aPower){
//elided..
}
else if (Power.OFF == aPower){
//elided..
}
}
//..elided
public static void main(String... args){
LawnMower mower = new LawnMower();
//this is clearer than passing a boolean literal:
mower.switchPower(Power.ON);
}
}

See Also :
Type-Safe Enumerations

Validate state with class invariants


Class invariants are methods which check the validity of an object's state (its data). The idea is to define
validation methods for fields, and to perform these validations whenever the fields change. As usual, this
should be done without repeating any code.
An object's state may become invalid for various reasons:
An invalid argument is passed by the caller.
To ensure that the caller fulfills the requirements of a method or constructor, all arguments to non-private
methods should be explicitly checked. An exception should be thrown if a problem is detected. A special
case is deserialization, which should treat readObject like a constructor. (Assertions should not be used
for these types of checks.)
The implementation of the class is defective.
As a defensive measure, a method which changes the state of an object can include an assertion at its
end, to check that the object has indeed remained in a valid state. Here, such assertions verify correctness
of internal implementation details - they do not check arguments.
Example 1
is an immutable Model Object (MO). Its class invariant is defined by the validateState method.
In this particular case, if validation fails a checked exception is thrown, and the end user is presented
with their original input, along with associated error messages.
Resto

398

Collected Java Practices

Such immutable classes represent the simplest case, since validation is performed only once, during
construction (and deserialization, if necessary). By definition, an immutable object cannot change state
after construction, so performing validations at other times in the object's life is never necessary.
package hirondelle.fish.main.resto;
import
import
import
import
import
import
import
import
import

hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Id;
hirondelle.web4j.security.SafeText;
hirondelle.web4j.model.Decimal;
static hirondelle.web4j.model.Decimal.ZERO;
hirondelle.web4j.model.Check;
hirondelle.web4j.model.Validator;
static hirondelle.web4j.util.Consts.FAILS;

/** Model Object for a Restaurant. */


public final class Resto {
/**
Full constructor.
@param aId underlying database internal identifier (optional) 1..50 characters
@param aName of the restaurant (required), 2..50 characters
@param aLocation street address of the restaurant (optional), 2..50 characters
@param aPrice of the fish and chips meal (optional) $0.00..$100.00
@param aComment on the restaurant in general (optional) 2..50 characters
*/
public Resto(
Id aId, SafeText aName, SafeText aLocation, Decimal aPrice, SafeText aComment
) throws ModelCtorException {
fId = aId;
fName = aName;
fLocation = aLocation;
fPrice = aPrice;
fComment = aComment;
validateState();
}
public
public
public
public
public

Id getId() { return fId; }


SafeText getName() { return fName; }
SafeText getLocation() { return fLocation;
Decimal getPrice() { return fPrice; }
SafeText getComment() { return fComment; }

@Override public String toString(){


return ModelUtil.toStringFor(this);
}
@Override public boolean equals(Object aThat){
Boolean result = ModelUtil.quickEquals(this, aThat);
if (result == null) {
Resto that = (Resto) aThat;
result = ModelUtil.equalsFor(
this.getSignificantFields(), that.getSignificantFields()
);
}
return result;
}
@Override public int hashCode(){
if (fHashCode == 0){
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE
private final Id fId;
private final SafeText fName;
private final SafeText fLocation;
private final Decimal fPrice;
private final SafeText fComment;
private int fHashCode;
private static final Decimal HUNDRED = Decimal.from("100");
399

Collected Java Practices

private void validateState() throws ModelCtorException {


ModelCtorException ex = new ModelCtorException();
if (FAILS == Check.optional(fId, Check.range(1,50))) {
ex.add("Id is optional, 1..50 chars.");
}
if (FAILS == Check.required(fName, Check.range(2,50))) {
ex.add("Restaurant Name is required, 2..50 chars.");
}
if (FAILS == Check.optional(fLocation, Check.range(2,50))) {
ex.add("Location is optional, 2..50 chars.");
}
Validator[] priceChecks = {Check.range(ZERO, HUNDRED), Check.numDecimalsAlways(2)};
if (FAILS == Check.optional(fPrice, priceChecks)) {
ex.add("Price is optional, 0.00 to 100.00.");
}
if (FAILS == Check.optional(fComment, Check.range(2,50))) {
ex.add("Comment is optional, 2..50 chars.");
}
if ( ! ex.isEmpty() ) throw ex;
}
private Object[] getSignificantFields(){
return new Object[] {fName, fLocation, fPrice, fComment};
}
}

Example 2
Here's an example of a mutable, Serializable class which defines class invariants.
Items to note:
the assertion at the end of the close method
the call to validateState at the end of the readObject method
the implementation is significantly more complex, since the class is mutable

import java.text.StringCharacterIterator;
import java.util.*;
import java.io.*;
/**
* In this style of implementation, both the entire state of the object
* and its individual fields are validated without duplicating any code.
*
* Argument validation usually has if's and thrown exceptions at the
* start of a method. Here, these are replaced with a simple
* call to validateXXX. Validation is separated cleanly from the
* regular path of execution, improving legibility.
* JDK 7+.
*/
public final class BankAccount implements Serializable {
/**
* @param aFirstName contains only letters, spaces, and apostrophes.
* @param aLastName contains only letters, spaces, and apostrophes.
* @param aAccountNumber is non-negative.
*
* @throws IllegalArgumentException if any param does not comply.
*/
public BankAccount(String aFirstName, String aLastName, int aAccountNumber) {
//don't call an overridable method in a constructor
setFirstName(aFirstName);
setLastName(aLastName);
setAccountNumber(aAccountNumber);
}
/**
400

Collected Java Practices

* All "secondary" constructors call the "primary" constructor, such that


* validations are always performed.
*/
public BankAccount() {
this("FirstName", "LastName", 0);
}
public String getFirstName() {
return fFirstName;
}
public String getLastName(){
return fLastName;
}
public int getAccountNumber() {
return fAccountNumber;
}
/**
* This method changes state internally, and may use an assert to
* implement a post-condition on the object's state.
*/
public void close(){
//valid:
fAccountNumber = 0;
//this invalid value will fire the assertion:
//fAccountNumber = -2;
assert hasValidState(): this;
}
/**
* Names must contain only letters, spaces, and apostrophes.
*
* @throws IllegalArgumentException if any param does not comply.
*/
public void setFirstName(String aNewFirstName) {
validateName(aNewFirstName);
fFirstName = aNewFirstName;
}
/**
* Names must contain only letters, spaces, and apostrophes.
*
* @throws IllegalArgumentException if any param does not comply.
*/
public void setLastName (String aNewLastName) {
validateName(aNewLastName);
fLastName = aNewLastName;
}
/**
* AccountNumber must be non-negative.
*
* @throws IllegalArgumentException if any param does not comply.
*/
public void setAccountNumber(int aNewAccountNumber) {
validateAccountNumber(aNewAccountNumber);
fAccountNumber = aNewAccountNumber;
}
/**
* Can be used to easily pass object description to an assertion,
* using a "this" reference.
*/
public String toString(){
final StringBuilder result = new StringBuilder();
final String SPACE = " ";
result.append(fFirstName);
result.append(SPACE);
result.append(fLastName);
result.append(SPACE);
result.append(fAccountNumber);
return result.toString();
}
// PRIVATE
401

Collected Java Practices

private String fFirstName;


private String fLastName;
private int fAccountNumber;
/**
* Verify that all fields of this object take permissible values; that is,
* this method defines the class invariant.
*
* Call after deserialization.
* @throws IllegalArgumentException if any field takes an unpermitted value.
*/
private void validateState() {
validateAccountNumber(fAccountNumber);
validateName(fFirstName);
validateName(fLastName);
}
/**
* Return true if <code>validateState</code> does not throw
* an IllegalArgumentException, otherwise return false.
*
* Call at the end of any public method which has changed
* state (any "mutator" method). This is usually done in
* an assertion, since it corresponds to a post-condition.
* For example,
* <pre>
* assert hasValidState() : this;
* </pre>
* This method is provided since <code>validateState</code> cannot be used
* in an assertion.
*/
private boolean hasValidState() {
boolean result = true;
try {
validateState();
}
catch (IllegalArgumentException ex){
result = false;
}
return result;
}
/**
* Ensure names contain only letters, spaces, and apostrophes.
*
* @throws IllegalArgumentException if argument does not comply.
*/
private void validateName(String aName){
boolean nameHasContent = (aName != null) && (!aName.equals(""));
if (!nameHasContent){
throw new IllegalArgumentException("Names must be non-null and non-empty.");
}
StringCharacterIterator iterator = new StringCharacterIterator(aName);
char character = iterator.current();
while (character != StringCharacterIterator.DONE ){
boolean isValidChar = (Character.isLetter(character)
|| Character.isSpaceChar(character)
|| character =='\'');
if (isValidChar) {
//do nothing
}
else {
String message = "Names can contain only letters, spaces, and apostrophes.";
throw new IllegalArgumentException(message);
}
character = iterator.next();
}
}
/**
* AccountNumber must be non-negative.
*
* @throws IllegalArgumentException if argument does not comply.
*/
private void validateAccountNumber(int aAccountNumber){
if (aAccountNumber < 0) {
String message = "Account Number must be greater than or equal to 0.";
throw new IllegalArgumentException(message);
402

Collected Java Practices

}
}
private static final long serialVersionUID = 7526472295622776147L;
/**
* Always treat de-serialization as a full-blown constructor, by
* validating the final state of the de-serialized object.
*/
private void readObject(ObjectInputStream aInputStream)
throws ClassNotFoundException, IOException {
//always perform the default de-serialization first
aInputStream.defaultReadObject();
//ensure that object state has not been corrupted or
//tampered with maliciously
validateState();
}
/** Test harness. */
public static void main (String... aArguments) {
BankAccount account = new BankAccount("Joe", "Strummer", 532);
//exercise specific validations.
account.setFirstName("John");
account.setAccountNumber(987);
//exercise the post-condition assertion
//requires enabled assertions: "java -ea"
account.close();
//exercise the serialization
try (
OutputStream file = new FileOutputStream("account.ser");
OutputStream buffer = new BufferedOutputStream(file);
ObjectOutput output = new ObjectOutputStream(buffer);
){
output.writeObject(account);
}
catch(IOException exception){
System.err.println(exception);
}
//exercise the deserialization
try (
InputStream file = new FileInputStream("account.ser");
InputStream buffer = new BufferedInputStream(file);
ObjectInput input = new ObjectInputStream(buffer);
){
BankAccount recoveredAccount = (BankAccount)input.readObject();
System.out.println("Recovered account: " + recoveredAccount);
}
catch(IOException | ClassNotFoundException exception ){
System.err.println(exception);
}
}
}

See Also :
Validate method arguments
Immutable objects
Implementing Serializable
Conditional compile
Assert is for private arguments only
Assert use cases
Model Objects

403

Collected Java Practices

Validation belongs in a Model Object


An application should perform data validation in a Model Object, where it can validate both user input,
and the data being fetched from a database. This validation should be performed in a class, and not in an
.xml configuration file or some similar tool.
The role of a Model Object is to implement business logic, and validation is the single most common
(and arguably the most important) kind of business logic. The fundamental reason for creating any class
is often stated as bringing together data and closely related operations on that data. This is often the first
idea taught in courses on object programming.
Surprisingly, there are many web presentation frameworks that do not follow this simple guiding
principle of lasting value. Instead, they encourage the application programmer to separate data and logic,
by doing validation either in Java Server Pages, or in configuration files (typically an .xml file). What is
the justification for this? There does not seem to be any. If the reason is that "it allows validation to be
configured", then this seems like a dubious argument. Is it the job of a deployer or a page author to make
decisions regarding important business logic? No, it's not. On the contrary, for most organizations,
allowing such casual changes to business logic in a production environment is regarded as highly
undesirable, and is thus an argument against such techniques, not for them.
Fundamentally, validation is a programming task, and it always will be: it involves logic. It's not a
presentation task, and it's not a deployment task. Presentation frameworks which provide validation tools
are "leaking over" into the domain of business logic, in an apparent attempt to increase their list of
features. However, this style is inappropriate for the application programmer, since it breaks a
fundamental rule of object programming. Thus, such style of validation should likely be avoided.
Implementing validation in Model Objects is simple and natural. As usual, any common validations can
be factored out into utility classes. In addition, writing test cases is simple, and can be executed standalone, outside of the web environment.
Example
To assist the application programmer with common validations, the WEB4J tool defines a Validator
interface, and a Check class which returns some common implementations of that interface. Validation is
implemented entirely in code, and never with JSPs or configuration files. Here, a Resto Model Object
uses Check to perform all required validation in its constructor, by calling validateState :
package hirondelle.fish.main.resto;
import
import
import
import
import
import
import
import
import

hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Id;
hirondelle.web4j.security.SafeText;
hirondelle.web4j.model.Decimal;
static hirondelle.web4j.model.Decimal.ZERO;
hirondelle.web4j.model.Check;
hirondelle.web4j.model.Validator;
static hirondelle.web4j.util.Consts.FAILS;

/** Model Object for a Restaurant. */


public final class Resto {
/**
Full constructor.
@param aId underlying database internal identifier (optional) 1..50 characters
@param aName of the restaurant (required), 2..50 characters
@param aLocation street address of the restaurant (optional), 2..50 characters
@param aPrice of the fish and chips meal (optional) $0.00..$100.00
@param aComment on the restaurant in general (optional) 2..50 characters
*/
public Resto(
404

Collected Java Practices

Id aId, SafeText aName, SafeText aLocation, Decimal aPrice, SafeText aComment


) throws ModelCtorException {
fId = aId;
fName = aName;
fLocation = aLocation;
fPrice = aPrice;
fComment = aComment;
validateState();
}
public
public
public
public
public

Id getId() { return fId; }


SafeText getName() { return fName; }
SafeText getLocation() { return fLocation;
Decimal getPrice() { return fPrice; }
SafeText getComment() { return fComment; }

@Override public String toString(){


return ModelUtil.toStringFor(this);
}
@Override public boolean equals(Object aThat){
Boolean result = ModelUtil.quickEquals(this, aThat);
if (result == null) {
Resto that = (Resto) aThat;
result = ModelUtil.equalsFor(
this.getSignificantFields(), that.getSignificantFields()
);
}
return result;
}
@Override public int hashCode(){
if (fHashCode == 0){
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE
private final Id fId;
private final SafeText fName;
private final SafeText fLocation;
private final Decimal fPrice;
private final SafeText fComment;
private int fHashCode;
private static final Decimal HUNDRED = Decimal.from("100");
private void validateState() throws ModelCtorException {
ModelCtorException ex = new ModelCtorException();
if (FAILS == Check.optional(fId, Check.range(1,50))) {
ex.add("Id is optional, 1..50 chars.");
}
if (FAILS == Check.required(fName, Check.range(2,50))) {
ex.add("Restaurant Name is required, 2..50 chars.");
}
if (FAILS == Check.optional(fLocation, Check.range(2,50))) {
ex.add("Location is optional, 2..50 chars.");
}
Validator[] priceChecks = {Check.range(ZERO, HUNDRED), Check.numDecimalsAlways(2)};
if (FAILS == Check.optional(fPrice, priceChecks)) {
ex.add("Price is optional, 0.00 to 100.00.");
}
if (FAILS == Check.optional(fComment, Check.range(2,50))) {
ex.add("Comment is optional, 2..50 chars.");
}
if ( ! ex.isEmpty() ) throw ex;
}
private Object[] getSignificantFields(){
return new Object[] {fName, fLocation, fPrice, fComment};
}
}

405

Collected Java Practices

See Also :
JSPs should contain only presentation logic
Model Objects

Assert is for private arguments only


Most validity checks in a program are checks on parameters passed to non-private methods. The assert
keyword is not meant for these types of validations. (Some may find this surprising; here is the Oracle
guideline.)
Assertions can be disabled. Since checks on parameters to non-private methods implement the
requirements demanded of the caller, turning off such checks at runtime would mean that part of the
contract is no longer being enforced.
Conversely, checks on arguments to private methods can indeed use assert. These checks are made to
verify assumptions about internal implementation details, and not to check that the caller has followed the
requirements of a non-private method's contract.
In addition, if an AssertionError can be thrown by a method, then this fact should not appear in the
method's javadoc comment. The specification of a method's behaviour should never mention assertions,
since assertions can be disabled.
Example
public final class SpaceShuttle {
/**
* The non-nullity of aName is part of the contract of the
* constructor.
*
* @param aName is non-null.
*/
public SpaceShuttle(String aName) {
//WRONG way:
assert aName != null : "Name must not be null";
//RIGHT way:
if (aName == null) {
throw new IllegalArgumentException("Name must not be null.");
}
fName = aName;
}
public String getName() {
return fName;
}
// PRIVATE
private final String fName;
}

See Also :
Validate method arguments
406

Collected Java Practices

Design by Contract

Assert use cases


Here are some common examples of using the assert keyword:
pre-conditions (in private methods only) - the requirements which a method requires its caller to
fulfill
post-conditions - verify the promises made by a method to its caller
class invariants - validate object state
unreachable-at-runtime code - parts of your program which you expect to be unreachable, but
which cannot be verified as such at compile-time (often else clauses and default cases in switch
statements)
Example
import java.util.Random;
public final class Flower {
public static void main(String... arguments) {
final Flower tulip = new Flower("Tulip", 1);
tulip.grow();
tulip.grow();
System.out.println(tulip);
tulip.randomGrowOrWither();
System.out.println(tulip);
tulip.wither();
tulip.wither();
System.out.println(tulip);
}
/**
* @param aSpecies must have content.
* @param aInitialLength must be greater than 0.
*/
public Flower(final String aSpecies, final int aInitialLength) {
//assert is NOT used to validate params of public methods
if (!isValidSpecies(aSpecies)) {
throw new IllegalArgumentException("Species must have content.");
}
if (!isValidLength(aInitialLength)) {
throw new IllegalArgumentException("Initial length must be positive.");
}
fSpecies = aSpecies;
fLength = aInitialLength;
//check the class invariant
assert hasValidState(): "Construction failed - not valid state.";
}
public boolean isMature() {
return fLength > 5 ;
//not necessary to assert valid state here, since
//the state has not changed.
}
/**
* Increase the length by at least one unit.
*/
public void grow(){
//this style of checking post-conditions is NOT recommended,
//since the copy of fLength is always made, even when
//assertions are disabled.
//See <tt>wither</tt> (below) for an example with an improved style.
final int oldLength = fLength;
407

Collected Java Practices

fLength += getLengthIncrease(fLength);
//post-condition: length has increased
assert fLength > oldLength;
//check the class invariant
assert hasValidState(): this;
}
/**
* Decrease the length by one unit, but only if the resulting length
* will still be greater than 0.
*/
public void wither(){
//this local class exists only to take a snapshot of the current state.
//although bulky, this style allows post-conditions of arbitrary complexity.
class OriginalState {
OriginalState() {
fOriginalLength = fLength;
}
int getLength() {
return fOriginalLength;
}
private final int fOriginalLength;
}
OriginalState originalState = null;
//construct object inside an assertion, in order to ensure that
//no construction takes place when assertions are disabled.
//this assert is rather unusual in that it will always succeed, and in that
//it has side-effects - it creates an object and sets a reference
assert (originalState = new OriginalState()) != null;
if (fLength > 1) {
--fLength;
}
//post-condition: length has decreased by one or has remained the same
assert fLength <= originalState.getLength();
//check the class invariant
assert hasValidState(): this;
}
/**
* Randomly select one of three actions
* <ul>
* <li>do nothing
* <li>grow
* <li>wither
* </ul>
*/
public void randomGrowOrWither() {
//(magic numbers are used here instead of symbolic constants
//to slightly clarify the example)
Random generator = new Random();
int action = generator.nextInt(3);
//according to the documentation for the Random class, action
//should take one of the values 0,1,2.
if (action == 0) {
//do nothing
}
else if (action == 1) {
grow();
}
else if (action == 2) {
wither();
}
else {
//this is still executed if assertions are disabled
throw new AssertionError("Unexpected value for action: " + action);
}
//check the class invariant
assert hasValidState(): this;
}
/** Use for debugging only. */
public String toString(){
final StringBuilder result = new StringBuilder();
result.append(this.getClass().getName());
408

Collected Java Practices

result.append(": Species=");
result.append(fSpecies);
result.append(" Length=");
result.append(fLength);
return result.toString();
}
// PRIVATE
private final String fSpecies;
private int fLength;
/**
* Implements the class invariant.
*
* Perform all checks on the state of the object.
* One may assert that this method returns true at the end
* of every public method.
*/
private boolean hasValidState(){
return isValidSpecies(fSpecies) && isValidLength(fLength);
}
/** Species must have content. */
private boolean isValidSpecies(final String aSpecies) {
return aSpecies != null && aSpecies.trim().length()>0;
}
/** Length must be greater than 0. */
private boolean isValidLength(final int aLength) {
return aLength > 0;
}
/** Length increase depends on current length. */
private int getLengthIncrease(int aOriginalLength) {
//since this is a private method, an assertion
//may be used to validate the argument
assert aOriginalLength > 0: this;
int result = 0;
if (aOriginalLength > 10) {
result = 2;
}
else {
result = 1;
}
assert result > 0 : result;
return result;
}
}

See Also :
Validate state with class invariants
Design by Contract

Assertions in general
Assertions in general:
when an assertion is fired, pass the offending piece of data to AssertionError
the checks performed inside an assertion are usually simple boolean conditions - side effects are
usually to be avoided
since assertions can be disabled, the behaviour of a program must not depend upon assertions
being executed
409

Collected Java Practices

if desired, the conditional compilation idiom can still be used


you can almost always ignore the methods in ClassLoader regarding assertions - they are intended
for interpreters such as java , and are not used in a typical program
Reminders regarding use :
at runtime, assertions are disabled by default
the -enableassertions and -disableassertions runtime parameters (or their synonyms -ea and
-da ) control assertions to any degree of granularity
AssertionError behaves as any other Error , and is not affected by enabling or disabling of
assertions.
to compile code containing assertions, older JDKs need to add -source 1.4 as a command line
argument to javac
Examples of typical styles, which affect non-system classes:

Runtime command line


Classes with assertions enabled
java -ea Main

all classes

java -ea:com.Blah Main

one particular class named Blah

java -ea:com.data... Main

all classes in the com.data package, and all of its sub packages

java -ea -da:com.Blah Main

all but one class named Blah

See Also :
Conditional compile
Pass all pertinent data to exceptions

Force enabling of assertions


It's sometimes useful to require assertions to be always enabled for a particular class at runtime.
Example
If NuclearReactor is loaded into an environment which has disabled assertions, then its static initializer
will throw a RuntimeException.
Two example runs of NuclearReactor give :
>java -cp . -enableassertions NuclearReactor
Proceeding normally...
>java -cp . NuclearReactor
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: Asserts must be enabled for NuclcearReactor.
at NuclearReactor.<clinit>(NuclearReactor.java:18)
public final class NuclearReactor {
public static void main(String... arguments){
NuclearReactor reactor = new NuclearReactor("Fermi");
System.out.println("Proceeding normally...");
}
/**
410

Collected Java Practices

* Prevent this class from running with assertions disabled.


*
* If assertions are disabled, then a RuntimeException will be
* thrown upon class loading.
*/
static {
boolean hasAssertEnabled = false;
assert hasAssertEnabled = true; // rare - an intentional side effect!
if(!hasAssertEnabled){
throw new RuntimeException("Asserts must be enabled for NuclearReactor.");
}
}
/**
* @param aName is non-null.
*/
public NuclearReactor(String aName){
//do NOT use assert to verify parameters passed to
//non-private methods
if(aName == null){
throw new IllegalArgumentException("Name cannot be null.");
}
fName = aName;
}
public String getName(){
return fName;
}
// PRIVATE
private final String fName;
}

Avoid JavaBeans style of construction


Some argue that to construct a Model Object in many steps, first by calling a no-argument constructor,
and then by calling a series of setXXX methods, is something to be avoided, if possible:
it's more complex, since one call is replaced with many
there is no simple way to ensure that all necessary setXXX methods are called
there is no simple way to implement a class invariant
it allows the object to be in intermediate states which are not immediately useful, and perhaps even
invalid
it's not compatible with the (very useful) immutable object pattern
it does not follow the style of many design patterns, in which concrete classes usually have
arguments passed to their constructor. This is a recurring theme in the Design Patterns book.
Constructors usually act as a "back door" for data which is needed by an implementation, but
which cannot appear in the signature of some higher-level abstraction (since it would pollute such
an abstraction with data specific to an implementation).
Of course, JavaBeans follow this pattern of "no-argument constructor plus setXXX". However, JavaBeans
were originally intended for a very narrow problem domain - manipulation of graphical components in
an IDE. As a model for the construction of typical data-centric objects (Model Objects), JavaBeans seem
completely inappropriate. One can even argue strongly that, for Model Objects, the JavaBeans style is an
anti-pattern, and should be avoided unless absolutely necessary.
"The JavaBeans pattern has serious disadvantages." - Joshua Bloch, Effective Java
411

Collected Java Practices

If you need more convincing, read on.


Here is the definition of a Java Bean, directly from the original specification:
"A Java Bean is a reusable software component that can be manipulated visually in a builder tool."
This is very clear and simple. No mention is made here, or anywhere else in the specification, of Model
Objects used to encapsulate database records. The whole idea of a Model Object is entirely absent from
the Java Bean specification, and for good reason: the manipulation of graphical controls and the
representation of database records are unrelated issues.
The life cycle of a Java Bean, starting with its no-argument constructor, is derived mainly from the task
of configuring a graphical control having some default initial state. For the intended domain, this is
indeed a reasonable design. For Model Objects, however, the idea of configuration in this sense is
entirely meaningless.
The role of a Model Object is to carry data. Since a no-argument constructor can only build an object that
is initially empty, then it cannot contribute directly to this aim. It does not form a natural candidate for
the design of a Model Object.
To put data into empty Java Beans, frameworks typically call various setXXX methods using reflection.
There is nothing to prevent them from calling constructors in exactly the same way.
A demonstration that a framework does not need JavaBeans for its Model Objects is found in the WEB4J
tool. WEB4J calls (surprise, surprise) a constructor to build Model Objects. It recommends implementing
Model Objects as immutable objects, not Java Beans.
The widespread adoption of Java Beans as a supposedly appropriate design for Model Objects seems to
be an error.
See Also :
Validate state with class invariants
Immutable objects
Model Objects

Beware of mistaken field redeclares


Beware of this simple mistake, which can be hard to track down: if a field is mistakenly redeclared
within the body of a method, then the field is not being referenced, but rather a temporary local variable
with the same name.
A common symptom of this problem is that a stack trace indicates that a field is null , but a cursory
examination of the code makes it seem as if the field has been correctly initialized.
Naming conventions can reduce the frequency of this error, but they cannot eliminate it.
Example
public final class MistakenRedeclare {
public static void main(String... aArguments ) {
MistakenRedeclare mistake = new MistakenRedeclare("blah");
412

Collected Java Practices

//Throws NullPointerException, since the return value of


//getName is null.
System.out.println("Length of name: " + mistake.getName().length());
}
public MistakenRedeclare(String aName) {
//Oops! Setting a local variable, not the field!
String fName = aName;
//this style may help to eliminate such errors:
//this.fName = aName;
}
public String getName(){
return fName;
}
//..elided
// PRIVATE
private String fName;
}

Example run of this class:


>java -cp . MistakenRedeclare
Exception in thread "main" java.lang.NullPointerException
at MistakenRedeclare.main(MistakenRedeclare.java:7)

See Also :
Naming conventions

Construct Object using class name


You can create an object dynamically at runtime using only the name of the class, input as a simple
string. This is done using a part of the Java language called reflection.
Reflection allows old code to call new code, without needing to recompile.
If a class has a no-argument constructor, then creating an object from its package-qualified class name
(for example, "java.lang.Integer") is usually done using these methods:
Class.forName
Class.newInstance

If arguments need to be passed to the constructor, then these alternatives may be used instead:
Class.getConstructor
Constructor.newInstance

The most common use of reflection is to instantiate a class whose generic type is known at design-time,
but whose specific implementation class is not. See the plugin topic for an example. Other uses of
reflection are rather rare, and appear mostly in special-purpose programs.
Example
Interpreter

is an interface used by a simple command line application to interpret user input:


413

Collected Java Practices

import java.util.*;
/**
* Parse a line of text and return a result.
*/
public interface Interpreter {
/**
* @param aLine is non-null.
* @param aResult is a non-null, empty List which acts as an "out"
* parameter; when returned, aResult must contain a non-null, non-empty
* List of items which all have a <code>toString</code> method, to be used
* for displaying a result to the user.
*
* @return true if the user has requested to quit the Interpreter.
* @exception IllegalArgumentException if a param does not comply.
*/
boolean parseInput(String aLine, List<Object> aResult);
/**
* Return the text to be displayed upon start-up of the Interpreter.
*/
String getHelloPrompt();
}

The task is to create a concrete implementation of this interface at runtime, using only the name of a
class as input. In this example, the user inputs a package-qualified class name directly on the command
line. Then, a corresponding Object is created and cast to the expected type (here, Interpreter). The
object can then be used in the same way as any other object.
import
import
import
import
import
import

java.io.BufferedReader;
java.io.IOException;
java.io.InputStreamReader;
java.io.Reader;
java.util.ArrayList;
java.util.List;

/**
* Sends text back and forth between the command line and an
* Interpreter. JDK less than 6.
*/
public final class Console {
/**
* Build and launch a specific <code>Interpreter</code>, whose
* package-qualified name is passed in on the command line.
*/
public static void main(String... aArguments) {
try {
Class theClass = Class.forName(aArguments[0]);
Interpreter interpreter = (Interpreter)theClass.newInstance();
Console console = new Console(interpreter);
console.run();
}
catch (ClassNotFoundException ex){
System.err.println(ex + " Interpreter class must be in class path.");
}
catch(InstantiationException ex){
System.err.println(ex + " Interpreter class must be concrete.");
}
catch(IllegalAccessException ex){
System.err.println(ex + " Interpreter class must have a no-arg constructor.");
}
}
public Console(Interpreter aInterpreter) {
if (aInterpreter == null) {
throw new IllegalArgumentException("Cannot be null.");
}
fInterpreter = aInterpreter;
}
/**
* Display a prompt, wait for a full line of input, and then parse
414

Collected Java Practices

* the input using an Interpreter.


*
* Exit when <code>Interpreter.parseInput</code> returns true.
*/
public void run() {
display(fInterpreter.getHelloPrompt());
//pass each line of input to fInterpreter, and display
//fInterpreter's result
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
BufferedReader stdin = new BufferedReader(inputStreamReader);
boolean hasRequestedQuit = false;
String line = null;
List<Object> result = new ArrayList<Object>();
try {
while(!hasRequestedQuit){
line = stdin.readLine();
//note that "result" is passed as an "out" parameter
hasRequestedQuit = fInterpreter.parseInput(line, result);
display(result);
result.clear();
}
}
catch (IOException ex) {
System.err.println(ex);
}
finally {
display(fBYE);
shutdown(stdin);
}
}
// PRIVATE
private static final String fBYE = "Bye.";
private Interpreter fInterpreter;
/**
* Display some text to stdout.
* The result of toString() is used.
*/
private void display(Object aText){
System.out.print(aText.toString());
System.out.flush();
}
/**
* Display a List of objects as text in stdout, in the order returned
* by the iterator of aText.
*/
private void display(List<Object> aText) {
for(Object item : aText){
display(item);
}
}
private void shutdown(Reader aStdin){
try {
aStdin.close();
}
catch (IOException ex){
System.err.println(ex);
}
}
}

See Also :
Console input
Abstract Factory
Plugin Factory
415

Collected Java Practices

Constructors in general
Constructors:
if super or this are called, they can only be called on the first line of the constructor
an exception can be thrown if a parameter is invalid
you should ensure that the constructed object is in a valid state
constructors should never call an overridable method (an overridable method is one which is
neither private, static, nor final)
constructors are never synchronized
constructors can create thread objects, but the thread should not be started within a constructor
constructors shouldn't pass a this reference to other objects
constructors can be private, in order to restrict construction
Note that a conventional distinction is made in Java between the "default constructor" and a "noargument constructor":
the default constructor is the constructor provided by the system in the absence of any constructor
provided by the programmer. Once a programmer supplies any constructor whatsoever, the default
constructor is no longer supplied.
a no-argument constructor, on the other hand, is a constructor provided by the programmer which
takes no arguments.
This distinction is necessary because the behavior of the two kinds of constructor are unrelated: a default
constructor has a fixed behavior defined by Java, while the behavior of a no-argument constructor is
defined by the application programmer.
Example
Resto

is an immutable class, since its state cannot change after construction. Note that:
all validation is performed in its constructor
almost all of its javadoc is concerned with stating precisely what is to be passed to the constructor

package hirondelle.fish.main.resto;
import
import
import
import
import
import
import
import
import

hirondelle.web4j.model.ModelCtorException;
hirondelle.web4j.model.ModelUtil;
hirondelle.web4j.model.Id;
hirondelle.web4j.security.SafeText;
hirondelle.web4j.model.Decimal;
static hirondelle.web4j.model.Decimal.ZERO;
hirondelle.web4j.model.Check;
hirondelle.web4j.model.Validator;
static hirondelle.web4j.util.Consts.FAILS;

/** Model Object for a Restaurant. */


public final class Resto {
/**
Full constructor.
@param
@param
@param
@param
@param

aId underlying database internal identifier (optional) 1..50 characters


aName of the restaurant (required), 2..50 characters
aLocation street address of the restaurant (optional), 2..50 characters
aPrice of the fish and chips meal (optional) $0.00..$100.00
aComment on the restaurant in general (optional) 2..50 characters
416

Collected Java Practices

*/
public Resto(
Id aId, SafeText aName, SafeText aLocation, Decimal aPrice, SafeText aComment
) throws ModelCtorException {
fId = aId;
fName = aName;
fLocation = aLocation;
fPrice = aPrice;
fComment = aComment;
validateState();
}
public
public
public
public
public

Id getId() { return fId; }


SafeText getName() { return fName; }
SafeText getLocation() { return fLocation;
Decimal getPrice() { return fPrice; }
SafeText getComment() { return fComment; }

@Override public String toString(){


return ModelUtil.toStringFor(this);
}
@Override public boolean equals(Object aThat){
Boolean result = ModelUtil.quickEquals(this, aThat);
if (result == null) {
Resto that = (Resto) aThat;
result = ModelUtil.equalsFor(
this.getSignificantFields(), that.getSignificantFields()
);
}
return result;
}
@Override public int hashCode(){
if (fHashCode == 0){
fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
}
return fHashCode;
}
// PRIVATE
private final Id fId;
private final SafeText fName;
private final SafeText fLocation;
private final Decimal fPrice;
private final SafeText fComment;
private int fHashCode;
private static final Decimal HUNDRED = Decimal.from("100");
private void validateState() throws ModelCtorException {
ModelCtorException ex = new ModelCtorException();
if (FAILS == Check.optional(fId, Check.range(1,50))) {
ex.add("Id is optional, 1..50 chars.");
}
if (FAILS == Check.required(fName, Check.range(2,50))) {
ex.add("Restaurant Name is required, 2..50 chars.");
}
if (FAILS == Check.optional(fLocation, Check.range(2,50))) {
ex.add("Location is optional, 2..50 chars.");
}
Validator[] priceChecks = {Check.range(ZERO, HUNDRED), Check.numDecimalsAlways(2)};
if (FAILS == Check.optional(fPrice, priceChecks)) {
ex.add("Price is optional, 0.00 to 100.00.");
}
if (FAILS == Check.optional(fComment, Check.range(2,50))) {
ex.add("Comment is optional, 2..50 chars.");
}
if ( ! ex.isEmpty() ) throw ex;
}
private Object[] getSignificantFields(){
return new Object[] {fName, fLocation, fPrice, fComment};
}
}

417

Collected Java Practices

See Also :
Validate method arguments
Validate state with class invariants
Initializing fields to 0-false-null is redundant
Immutable objects
Private constructor
Avoid JavaBeans style of construction
Constructors shouldn't call overridables
Don't pass 'this' out of a constructor
Constructors shouldn't start threads

Constructors shouldn't call overridables


Constructors should not call an overridable method - that is, they should only call methods that are
private , static, or final .
The reason why is best illustrated with a bit of code:
public final class ConstructorOverridable {
public static void main(String... aArgs){
log("BUILD AN ANIMAL :");
Animal animal = new Animal();
log(" ");
log("BUILD A FISH :");
Animal fish = new Fish();
}
// PRIVATE
private static void log(Object aObject){
System.out.println(String.valueOf(aObject));
}
private static class Animal{
Animal(){
log("Animal ctor");
//call an overridable method from ctor - dangerous
perform();
}
void perform(){
log("Animal perform.");
}
}
private static class Fish extends Animal {
Fish(){
//every constructor has an implicit call to super(); for first line
super();
log("Fish ctor. This is the earliest Tail Shape can be initialized.");
}
void perform(){
//in general, this method can depend on Fish data
log("Fish perform. Can fail since Tail Shape is : " + fTailShape);
}
private String fTailShape;
}
}

418

Collected Java Practices

An example run of this class demonstrates the problem:


BUILD AN ANIMAL :
Animal ctor
Animal perform.
BUILD A FISH :
Animal ctor
Fish perform. Can fail since Tail Shape is : null
Fish ctor. This is the earliest Tail Shape can be initialized.

See Also :
Constructors in general
Designing for subclassing
Overridable methods need special care

Constructors shouldn't start threads


Constructors shouldn't start threads. According to Java Concurrency in Practice, by Brian Goetz and
others (an excellent book), there are two problems with starting a thread in a constructor (or static
initializer):
in a non-final class, it increases the danger of problems with subclasses
it opens the door for allowing the this reference to be passed to another object before the object is
fully constructed
There's nothing wrong with creating a thread object in a constructor or static initializer - just don't start it
there.
The alternative to starting a thread in the constructor is to simply start the thread in an ordinary method
instead.

See Also :
Constructors in general
Don't pass 'this' out of a constructor

Copy constructors
Copy constructors:
provide an attractive alternative to the rather pathological clone method
are easily implemented
simply extract the argument's data, and forward to a regular constructor
are unnecessary for immutable objects
Example
public final class Galaxy {

419

Collected Java Practices

/**
* Regular constructor.
*/
public Galaxy(double aMass, String aName) {
fMass = aMass;
fName = aName;
}
/**
* Copy constructor.
*/
public Galaxy(Galaxy aGalaxy) {
this(aGalaxy.getMass(), aGalaxy.getName());
//no defensive copies are created here, since
//there are no mutable object fields (String is immutable)
}
/**
* Alternative style for a copy constructor, using a static newInstance
* method.
*/
public static Galaxy newInstance(Galaxy aGalaxy) {
return new Galaxy(aGalaxy.getMass(), aGalaxy.getName());
}
public double getMass() {
return fMass;
}
/**
* This is the only method which changes the state of a Galaxy
* object. If this method were removed, then a copy constructor
* would not be provided either, since immutable objects do not
* need a copy constructor.
*/
public void setMass(double aMass){
fMass = aMass;
}
public String getName() {
return fName;
}
// PRIVATE
private double fMass;
private final String fName;
/** Test harness. */
public static void main (String... aArguments){
Galaxy m101 = new Galaxy(15.0, "M101");
Galaxy m101CopyOne = new Galaxy(m101);
m101CopyOne.setMass(25.0);
System.out.println("M101 mass: " + m101.getMass());
System.out.println("M101Copy mass: " + m101CopyOne.getMass());
Galaxy m101CopyTwo = Galaxy.newInstance(m101);
m101CopyTwo.setMass(35.0);
System.out.println("M101 mass: " + m101.getMass());
System.out.println("M101CopyTwo mass: " + m101CopyTwo.getMass());
}
}

Example run of this class:


>java -cp . Galaxy
M101 mass: 15.0
M101Copy mass: 25.0
M101 mass: 15.0
M101CopyTwo mass: 35.0

420

Collected Java Practices

See Also :
Defensive copying
Immutable objects
Avoid clone

Don't pass 'this' out of a constructor


Within a class, the 'this' Java keyword refers to the native object, the current instance of the class.
Within a constructor, you can use the this keyword in 3 different ways:
on the first line, you can call another constructor, using this(...);
as a qualifier when referencing fields, as in this.fName
as a reference passed to a method of some other object, as in blah.operation(this);
You can get into trouble with the last form. The problem is that, inside a constructor, the object is not yet
fully constructed. An object is only fully constructed after its constructor completely returns, and not
before. But when passed as a parameter to a method of some other object, the this reference should
always refer to a fully-formed object.
This problem occurs mainly with listeners. Here's an example which illustrates the point:
import java.util.Observable;
import java.util.Observer;
/**
@author javapractices.com
@author Andrew Sackett
*/
public final class EscapingThisReference {
/** A RadioStation is observed by the people listening to it. */
static final class RadioStation extends Observable {
//elided
}
/**
A listener which waits until this object is fully-formed before
it lets it be referenced by the outside world.
Uses a private constructor to first build the object, and then
configures the fully-formed object as a listener.
*/
static final class GoodListener implements Observer {
/** Factory method. */
static GoodListener buildListener(String aPersonsName, RadioStation aStation){
//first call the private constructor
GoodListener result = new GoodListener(aPersonsName);
//the 'result' object is now fully constructed, and can now be
//passed safely to the outside world
aStation.addObserver(result);
return result;
}
@Override public void update(Observable aStation, Object aData) {
//..elided
}
private String fPersonsName;
/** Private constructor. */
private GoodListener(String aPersonsName){
this.fPersonsName = aPersonsName; //ok
}
}
/**
A listener which incorrectly passes a 'this' reference to the outside world
before construction is completed.
*/
421

Collected Java Practices

static final class BadListenerExplicit implements Observer {


/** Ordinary constructor. */
BadListenerExplicit(String aPersonsName, RadioStation aStation){
this.fPersonsName = aPersonsName; //OK
//DANGEROUS - the 'this' reference shouldn't be passed to the listener,
//since the constructor hasn't yet completed; it doesn't matter if
//this is the last statement in the constructor!
aStation.addObserver(this);
}
@Override public void update(Observable aStation, Object aData) {
//..elided
}
private String fPersonsName;
}
/**
Another listener that passes out a 'this' reference to the outside
world before construction is completed; here, the 'this' reference
is implicit, via the anonymous inner class.
*/
static final class BadListenerImplicit {
/** Ordinary constructor. */
BadListenerImplicit(String aPersonsName, RadioStation aStation){
this.fPersonsName = aPersonsName; //OK
//DANGEROUS
aStation.addObserver(
new Observer(){
@Override public void update(Observable aObservable, Object aData) {
doSomethingUseful();
}
}
);
}
private void doSomethingUseful() {
//..elided
}
private String fPersonsName;
}
}

See Also :
Constructors in general
Constructors shouldn't start threads

Initializing fields to 0-false-null is redundant


One of the most fundamental aspects of a programming language is how it initializes data. For Java, this
is defined explicitly in the language specification. For fields and array components, when items are
created, they are automatically set to the following default values by the system:
numbers: 0 or 0.0
booleans: false
object references: null
This means that explicitly setting fields to 0, false, or null (as the case may be) is unnecessary and
redundant. Since this language feature was included in order to, in part, reduce repetitive coding, it's a
good idea to take full advantage of it. Insisting that fields should be explicitly initialized to 0, false, or
null is an idiom which is likely inappropriate to the Java programming language.
Furthermore, setting a field explicitly to 0, false, or null may even cause the same operation to be
422

Collected Java Practices

performed twice (depending on your compiler).


Example
public final class Quark {
public Quark(String aName, double aMass){
fName = aName;
fMass = aMass;
}
//PRIVATE
//WITHOUT redundant initialization to default values
//private String fName;
//private double fMass;
//WITH redundant initialization to default values
private String fName = null;
private double fMass = 0.0d;
}

If the bytecode of the Quark class is examined, the duplicated operations become clear (here, Oracle's
javac compiler was used):

WITHOUT redundant init


WITH redundant init
>javap -c -classpath . Quark
Compiled from Quark.java
public final class Quark extends
java.lang.Object {
public Quark(java.lang.String,double);
}

>javap -c -classpath . Quark


Compiled from Quark.java
public final class Quark extends
java.lang.Object {
public Quark(java.lang.String,double);
}

Method Quark(java.lang.String,double)
0 aload_0
1 invokespecial #1 <Method
java.lang.Object()>
4 aload_0
5 aload_1
6 putfield #2 <Field java.lang.String
fName>
9 aload_0
10 dload_2
11 putfield #3 <Field double fMass>
14 return

Method Quark(java.lang.String,double)
0 aload_0
1 invokespecial #1 <Method
java.lang.Object()>
4 aload_0
5 aconst_null
6 putfield #2 <Field java.lang.String
fName>
9 aload_0
10 dconst_0
11 putfield #3 <Field double fMass>
14 aload_0
15 aload_1
16 putfield #2 <Field java.lang.String
fName>
19 aload_0
20 dload_2
21 putfield #3 <Field double fMass>
24 return

See Also :
Constructors in general
Don't declare local variables before use
423

Collected Java Practices

Examine bytecode

Implementing Serializable
Do not implement Serializable lightly, since it restricts future flexibility, and publicly exposes class
implementation details which are usually private. As well, implementing Serializable correctly is not
trivial.
The serialVersionUID is a universal version identifier for a Serializable class. Deserialization uses
this number to ensure that a loaded class corresponds exactly to a serialized object. If no match is found,
then an InvalidClassException is thrown.
Guidelines for serialVersionUID:
always include it as a field, for example:
private static final long serialVersionUID = 7526472295622776147L;

include this field even in the first version of the class, as a reminder of its importance
don't change the value of this field in future versions, unless you are knowingly making changes to
the class which will render it incompatible with old serialized objects
new versions of Serializable classes may or may not be able to read old serialized objects; it
depends upon the nature of the change; provide a pointer to Oracle's guidelines for what constitutes
a compatible change, as a convenience to future maintainers
Modern IDE's can generate a value of serialVersionUID for you. In addition, the JDK includes the
tool for generating these values. Here are the docs for both Win and Unix. (The class name
you pass to this tool doesn't include the .class extension.)
serialver

readObject

and writeObject:

implementations always start by calling default methods


deserialization must be treated as any constructor: validate the object state at the end of
deserializing - this implies that readObject should almost always be implemented in
Serializable classes, such that this validation is performed.
deserialization must be treated as any constructor: if constructors make defensive copies for
mutable object fields, so must readObject
when serializing a Collection , store the number of objects in the Collection as well, and use this
number to read them back in upon deserialization; avoid tricks using null
readObject

Other points:
use javadoc's @serial tag to denote Serializable fields
the .ser extension is conventionally used for files representing serialized objects
no static or transient fields undergo default serialization
extendable classes should not be Serializable, unless necessary
inner classes should rarely, if ever, implement Serializable
container classes should usually follow the style of Hashtable , which implements Serializable
by storing keys and values, as opposed to a large hash table data structure
Example
import java.io.Serializable;
import java.text.StringCharacterIterator;
import java.util.*;
424

Collected Java Practices

import java.io.*;
public final class SavingsAccount implements Serializable {
/**
* This constructor requires all fields to be passed as parameters.
*
* @param aFirstName contains only letters, spaces, and apostrophes.
* @param aLastName contains only letters, spaces, and apostrophes.
* @param aAccountNumber is non-negative.
* @param aDateOpened has a non-negative number of milliseconds.
*/
public SavingsAccount(
String aFirstName, String aLastName, int aAccountNumber, Date aDateOpened
){
setFirstName(aFirstName);
setLastName(aLastName);
setAccountNumber(aAccountNumber);
//make a defensive copy of the mutable Date passed to the constructor
setDateOpened(new Date(aDateOpened.getTime()));
//there is no need here to call validateState.
}
public SavingsAccount(){
this("FirstName", "LastName", 0, new Date(System.currentTimeMillis()));
}
public String getFirstName(){
return fFirstName;
}
public String getLastName(){
return fLastName;
}
public int getAccountNumber(){
return fAccountNumber;
}
/**
* Returns a defensive copy of the field.
* The caller may change the state of the returned object in any way,
* without affecting the internals of this class.
*/
public Date getDateOpened() {
return new Date(fDateOpened.getTime());
}
/**
* Names must contain only letters, spaces, and apostrophes.
* Validate before setting field to new value.
*
* @throws IllegalArgumentException if the new value is not acceptable.
*/
public void setFirstName(String aNewFirstName) {
validateName(aNewFirstName);
fFirstName = aNewFirstName;
}
/**
* Names must contain only letters, spaces, and apostrophes.
* Validate before setting field to new value.
*
* @throws IllegalArgumentException if the new value is not acceptable.
*/
public void setLastName (String aNewLastName) {
validateName(aNewLastName);
fLastName = aNewLastName;
}
/**
* Validate before setting field to new value.
*
* @throws IllegalArgumentException if the new value is not acceptable.
*/
public void setAccountNumber(int aNewAccountNumber){
validateAccountNumber(aNewAccountNumber);
fAccountNumber = aNewAccountNumber;
}
425

Collected Java Practices

public void setDateOpened(Date aNewDate){


//make a defensive copy of the mutable date object
Date newDate = new Date(aNewDate.getTime());
validateDateOpened(newDate);
fDateOpened = newDate;
}
// PRIVATE
/**
* The client's first name.
* @serial
*/
private String fFirstName;
/**
* The client's last name.
* @serial
*/
private String fLastName;
/**
* The client's account number.
* @serial
*/
private int fAccountNumber;
/**
* The date the account was opened.
* @serial
*/
private Date fDateOpened;
/**
* Determines if a de-serialized file is compatible with this class.
*
* Maintainers must change this value if and only if the new version
* of this class is not compatible with old versions. See Sun docs
* for <a href=http://java.sun.com/products/jdk/1.1/docs/guide
* /serialization/spec/version.doc.html> details. </a>
*
* Not necessary to include in first version of the class, but
* included here as a reminder of its importance.
*/
private static final long serialVersionUID = 7526471155622776147L;
/**
* Verify that all fields of this object take permissible values; that is,
* this method defines the class invariant.
*
* In this style of implementation, both the entire state of the object
* and its individual fields can be validated without repeating or
* duplicating code.
* Each condition is defined in one place. Checks on the entire
* object are performed at the end of object construction, and at
* the end of de-serialization. Checks on individual fields are
* performed at the start of the corresponding setXXX method.
* As well, this style replaces the if's and throwing
* of exceptions at the start of a setXXX, with a simple call to validateXXX.
* Validation is separated from the regular path of execution,
* which leads to improved legibility.
*
* @throws IllegalArgumentException if any field takes an unpermitted value.
*/
private void validateState() {
validateAccountNumber(fAccountNumber);
validateName(fFirstName);
validateName(fLastName);
validateDateOpened(fDateOpened);
}
/**
* Ensure names contain only letters, spaces, and apostrophes.
*
* @throws IllegalArgumentException if field takes an unpermitted value.
*/
private void validateName(String aName){
boolean nameHasContent = (aName != null) && (!aName.equals(""));
if (!nameHasContent){
426

Collected Java Practices

throw new IllegalArgumentException("Names must be non-null and non-empty.");


}
StringCharacterIterator iterator = new StringCharacterIterator(aName);
char character = iterator.current();
while (character != StringCharacterIterator.DONE){
boolean isValidChar =
(Character.isLetter(character) ||
Character.isSpaceChar(character) ||
character =='\''
);
if (isValidChar) {
//do nothing
}
else {
String message = "Names can contain only letters, spaces, and apostrophes.";
throw new IllegalArgumentException(message);
}
character = iterator.next();
}
}
/**
* AccountNumber must be non-negative.
* @throws IllegalArgumentException if field takes an unpermitted value.
*/
private void validateAccountNumber(int aAccountNumber){
if (aAccountNumber < 0) {
String message = "Account Number must be greater than or equal to 0.";
throw new IllegalArgumentException(message);
}
}
/**
* DateOpened must be after 1970.
* @throws IllegalArgumentException if field takes an unpermitted value.
*/
private void validateDateOpened(Date aDateOpened) {
if(aDateOpened.getTime() < 0) {
throw new IllegalArgumentException("Date Opened must be after 1970.");
}
}
/**
* Always treat de-serialization as a full-blown constructor, by
* validating the final state of the de-serialized object.
*/
private void readObject(
ObjectInputStream aInputStream
) throws ClassNotFoundException, IOException {
//always perform the default de-serialization first
aInputStream.defaultReadObject();
//make defensive copy of the mutable Date field
fDateOpened = new Date(fDateOpened.getTime());
//ensure that object state has not been corrupted or tampered with maliciously
validateState();
}
/**
* This is the default implementation of writeObject.
* Customise if necessary.
*/
private void writeObject(
ObjectOutputStream aOutputStream
) throws IOException {
//perform the default serialization for all non-transient, non-static fields
aOutputStream.defaultWriteObject();
}
}

See Also :
427

Collected Java Practices

Type-Safe Enumerations
Singleton
Serialization and subclassing

Serialization and subclassing


Interfaces and classes designed for inheritance should rarely implement Serializable, since this would
force a significant and (often unwanted) task on their implementors and subclasses.
However, even though most abstract classes don't implement Serializable, they may still need to allow
their subclasses to do so, if desired.
There are two cases. If the abstract class has no state, then it can simply provide a no-argument
constructor to its subclasses. If the abstract class has state, however, then there's more work to be done,
as demonstrated in the following example.
Example
public abstract class Government {
public Government(String aWinningParty) {
init(aWinningParty);
//never invoke an overridable method in a constructor
}
public final void raiseTaxes() {
validateInit(); //always called by public methods
//..elided
}
public final void lowerTaxes() {
validateInit();
//..elided
}
/**
* A template method which calls a protected abstract method, to
* be overridden by subclasses.
*/
public boolean makeLaw(){
validateInit();
String bill = draftLegislation(); //protected, abstract method
return vote(bill);
}
// PROTECTED
/**
* Each governament will draft legislation in a particular way.
* A tyranny, for example, would not have wide debate between its
* citizens.
*/
protected abstract String draftLegislation();
/**
* This no-argument constructor is needed by subclass's Serialization
* mechanism.
*/
protected Government() {
//empty
}
/**
* Called by both this class's public constructor, and by a subclass's
* readObject method. Note this method is final.
*/
protected final void init(String aWinningParty){
if (fIsInitialized) {
428

Collected Java Practices

throw new IllegalStateException("Cannot call init twice.");


}
fWinningParty = aWinningParty;
fIsInitialized = true;
}
/**
* Subclass will need access to the superclass's internal state,
* either through public or protected gets. Note that the
* get needs to be final.
*/
protected final String getWinningParty() {
return fWinningParty;
}
// PRIVATE
private String fWinningParty;
private boolean fIsInitialized = false;
/**
* Must be called by public methods.
*/
private void validateInit() throws IllegalStateException {
if (!fIsInitialized) {
throw new IllegalStateException("Uninitialized object");
}
}
private boolean vote(String aBill){
return true; //toy implentation
}
}

import java.io.*;
/** A Serializable subclass. */
public final class Tyranny extends Government implements Serializable {
public Tyranny(String aWinningParty) {
super(aWinningParty);
}
// PROTECTED
protected String draftLegislation() {
return "Forbid all use of C++.";
}
// PRIVATE
/**
* Custom deserialization is needed.
*/
private void readObject(
ObjectInputStream aStream
) throws IOException, ClassNotFoundException {
aStream.defaultReadObject();
//manually deserialize and init superclass
String winningParty = (String)aStream.readObject();
init(winningParty);
}
/**
* Custom serialization is needed.
*/
private void writeObject(ObjectOutputStream aStream) throws IOException {
aStream.defaultWriteObject();
//manually serialize superclass
aStream.writeObject(getWinningParty());
}
}

429

Collected Java Practices

See Also :
Implementing Serializable
Designing for subclassing

Some classes need readResolve


There are two common cases in which a readResolve method is required for proper deserialization. Both
cases use specific instances of objects to define their behaviour:
older forms of type-safe enumerations, that don't use enum
singletons
See the links below for examples of Serializable versions of these types of classes.
See Also :
Type-Safe Enumerations
Singleton

Always shut down an ExecutorService


The ExecutorService interface is among the most commonly used items in the java.util.concurrent
package. When finished using an ExecutorService , you need to shut it down explicitly. From its
javadoc:
"An unused ExecutorService should be shut down to allow reclamation of its resources."
Calling shutdown initiates a gradual and orderly shutdown. Submitted tasks are still executed, but no new
tasks will be accepted. If you then want to wait until shutdown has completed, call awaitTermination.
Alternatively, call shutdownNow to terminate in a more aggressive manner.

Avoid ThreadGroup
was originally intended for applet security, and should almost always be ignored. The
services it provides are limited, and sometimes even flawed.
ThreadGroup

The only method which could be useful is ThreadGroup.uncaughtException. An override of this


method will define customized behaviour for uncaught exceptions.
See Effective Java for a detailed discussion.
See Also :

430

Collected Java Practices

Provide an uncaught exception handler

Data integrity first


Multi-threaded code can be complex. With the advent of java.util.concurrent , multi-threaded code
can now usually be written at a higher level than was previously possible. However, multi-threaded code
can still be complex.
One should likely treat the basic thread-safety of a class as being fundamental. Performance
optimizations and "tricky" code should likely be avoided unless they are really necessary.
As usual, when faced with various implementation decisions, one should weigh carefully the pros and
cons, such as:
will the maintainer be comfortable maintaining the code? Would a simpler implementation suffice?
is each performance optimization really worth it? Has its benefit been measured, and is it
significant?
For example, using a read-write lock with a Map might be tempting in a given circumstance, but you'll
likely find that a ConcurrentHashMap works just fine.

Document thread safety


Javadoc comments should describe how the responsibilities for ensuring correct multi-threaded behaviour
are shared between a class and its caller. This is equivalent to stating the circumstances, if any, in which
the caller must explicitly obtain an external lock before performing certain operations. The central
questions to be answered by the documentation are:
"When do these objects require external synchronization?"
"Which lock is needed for which operations?"
Here, "operations" on an object might come in two flavours:
single-call operations, where only a single method is called
multiple-call operations, where more than one method is called, and they are treated as a single
unit; the classic example is iteration over a collection
The naming convention described below is a modified version of that found in Effective Java, by Joshua
Bloch. The reason for the changes are:
it focuses more on the point of view of the caller, instead of class internals
the language is simpler and clearer
The goal of this scheme is simply to tell the caller when they need to obtain an external lock before
performing an operation in a multithreaded environment.
lock-never - the caller never needs to obtain an external lock before performing any operation of any
kind. The class may be immutable, or it may perform all necessary synchronization internally. From the
point of view of the caller, these two cases are functionally the same (in the context of documenting
thread-safety).
431

Collected Java Practices

lock-always - the caller always needs to obtain an external lock before performing any operation.
Instances are mutable, and the implementation of the class performs no internal synchronization.
lock-sometimes - the caller sometimes needs to obtain a lock externally before performing some
operations. Instances are mutable, and the implementation of the class performs most synchronization
internally. The caller usually doesn't need to obtain an external lock, but there are some operations in
which the caller does indeed need to obtain an external lock. The classic example of this is iteration over
a collection.
lock-hostile - operations cannot be performed safely in a multi-threaded environment at all, even when
an external lock is always obtained. This is a rare pathological case. It usually occurs when static data is
modified without proper synchronization.
In practice, lock-always and lock-never are likely the most common cases.
To help you with documenting thread-safety, you might consider using:
annotations, for example those defined by Brian Goetz
javadoc's -tag option, which lets you define simple custom javadoc tags
Another reasonable option is to state in the javadoc overview that, unless stated otherwise, a class is to be
considered lock-always.
See Also :
Immutable objects
Use javadoc liberally
Synchronized is implementation detail

Dump thread information


There's a way of asking a Java Runtime to output a "thread dump" for all currently running threads (both
system threads and application threads). For each thread, the thread dump will include a stack trace, and
information on any locks currently held by the thread. Thread dumps don't cause your application to halt.
They're like a snapshot: your application pauses execution, the running threads are queried, the
information is output, and then your application simply resumes execution.
To cause a thread dump from the console that launched the application:
on Windows, press Control + Break
on Unix, press Control + Backslash
On Unix, you can also generate a thread dump from any console using these commands:
find the application's process id, using ps x | grep java
kill -3 <java-process-id>

Note that this doesn't actually kill the process.


The precise syntax and content of a thread dump is dependent on the Java Runtime.
The JConsole and Visual VM tools also let you view thread information.
432

Collected Java Practices

See Also :
Stack trace as String

Handle InterruptedException
In thread-related code, you will often need to handle an InterruptedException . There are two common
ways of handling it:
just throw the exception up to the caller (perhaps after doing some clean up)
call the interrupt method on the current thread
In the second case, the standard style is:
try {
...thread-related code...
}
catch(InterruptedException ex){
...perhaps some error handling here...
//re-interrupt the current thread
Thread.currentThread().interrupt();
}

Background
In multi-threaded code, threads can often block; the thread pauses execution until some external
condition is met, such as :
a lock is released
another thread completes an operation
some I/O operation completes
and so on...
Threads can be interrupted. An interrupt asks a thread to stop what it's doing in an orderly fashion.
However, the exact response to the interrupt depends on the thread's state, and how the thread is
implemented:
if the thread is currently blocking
stop blocking early
throw InterruptedException
else thread is doing real work
thread's interrupted status is set to true
if thread polls isInterrupted() periodically
(which is preferred)
orderly cleanup and stop execution
throw InterruptedException
else
regular execution continues

The main idea here is that an interrupt should not shutdown a thread immediately, in the middle of a
computation. Rather, a thread and it's caller communicate with each other to allow for an orderly
shutdown.
See also this article by Brian Goetz.

Launch thread is just another user thread


433

Collected Java Practices

Java programs always begin with a main method, called in a user thread.
There are two kinds of threads in Java. They are distinguished soley by how they affect program
termination:
user threads, which prevent a program from terminating.
daemon threads, which don't prevent a program from terminating.
A program terminates when there are no more user threads left to execute.
In addition, all user threads are created equal. In particular, the first user thread created upon launch of a
program has no special status, and its termination does not necessarily imply that the entire program will
terminate.
Example
The FlightSimulator class exists only to launch two other (user) threads. The thread which launches
FlightSimulator itself quickly ends, but the program still continues, since there are two other user
threads still running.
Output from a sample run of FlightSimulator :
Running Flight Simulator.
Terminating the original user thread.
Running Charles de Gaulle Airport.
Charles de Gaulle Has Available Runway:
Flight 8875: waiting for runway...
Charles de Gaulle Has Available Runway:
Flight 8875: taking off now...
Flight 8875: flying now...
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Flight 8875: waiting for runway...
Charles de Gaulle Has Available Runway:
Flight 8875: landing now...
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:

false
true

false
true
false
true
false
true
false
true
false
true
false
true

/** Builds and starts threads for Airport and Airplanes. */


public final class FlightSimulator {
public static void main(String... arguments) {
System.out.println("Running Flight Simulator.");
//build an airport and start it running
Airport charlesDeGaulle = new Airport("Charles de Gaulle");
Thread airport = new Thread(charlesDeGaulle);
airport.start();
//build a plane and start it running
Thread planeOne = new Thread(new Airplane(charlesDeGaulle, "Flight 8875"));
434

Collected Java Practices

planeOne.start();
//notice that this user thread now ends, but the program itself does
//NOT end since the threads created above are also user
//threads. All user threads have equal status, and there
//is nothing special about the thread which launches a program.
System.out.println("Terminating the original user thread.");
}
}

See Also :
Objects communicating across threads
Use System.exit with care

Objects communicating across threads


Since version 1.5, the JDK has included the java.util.concurrent package. It was developed by
experienced gurus, and includes a number of items for creating many kinds of multi-threaded programs.
Java Concurrency in Practice by Goetz and others is a good reference for this package.
Here's a basic example of using ExecutorService and CountDownLatch from java.util.concurrent to
communicate between threads. The example has the following context: in client side programming, it's
common to show the user a list of their available printers. However, the JDK's tool for fetching this
information can have poor performance - sometimes resulting in delays of several seconds or more. So,
one option is to fetch the list of available printers early upon startup of the application, in a separate
worker thread. Later, when the user wishes to print something, the list of available printers will already
be pre-fetched.
import java.util.List;
import javax.print.PrintService;
/**
Toy client application.
Needs to present a list of printers to the end user.
The problem is that fetching the list of printers can take several seconds.
So, the idea is to start fetching the list in a worker thread upon startup.
*/
public final class LaunchApplication {
public static void main(String... aArgs){
log("Launching application.");
PrinterListDAO.init(); //launches worker thread
//construct the user interface, other start tasks, etc...
//use the list of printers later in processing
List<PrintService> printers = new PrinterListDAO().getPrinters();
log("Seeing this many printers:" + printers.size());
}
private static void log(String aMsg){
System.out.println(aMsg);
}
}

import java.util.List;
import java.util.concurrent.CountDownLatch;
435

Collected Java Practices

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.print.PrintService;
final class PrinterListDAO {
/** This must be called early upon startup. */
static void init(){
fLatch = new CountDownLatch(1);
fWorker = new PrinterListWorker(fLatch);
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(fWorker);
executor.shutdown();//reclaims resources
}
/** Return the list of printers that can print PDFs (double-sided, portrait).*/
List<PrintService> getPrinters(){
try {
//block until the worker has set the latch to 0:
fLatch.await();
}
catch (InterruptedException ex){
log(ex.toString());
Thread.currentThread().interrupt();
}
return fWorker.getPrinterList();
}
// PRIVATE
/** Used to communicate between threads. */
static private CountDownLatch fLatch;
static private PrinterListWorker fWorker;
private static void log(String aMsg){
System.out.println(aMsg);
}
}

import
import
import
import
import
import
import
import
import

java.util.*;
java.util.concurrent.CountDownLatch;
javax.print.PrintService;
javax.print.PrintServiceLookup;
javax.print.attribute.HashPrintRequestAttributeSet;
javax.print.attribute.PrintRequestAttributeSet;
javax.print.attribute.standard.OrientationRequested;
javax.print.attribute.standard.Sides;
javax.print.DocFlavor;

/** Look up the list of printers. */


final class PrinterListWorker implements Runnable {
/** When the work is done, the latch will count down to 0. */
PrinterListWorker(CountDownLatch aLatch){
fLatch = aLatch;
}
@Override public void run() {
log("Worker thread started...");
long start = System.nanoTime();
//double-sided, portrait, for PDF files.
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(Sides.DUPLEX);
attrs.add(OrientationRequested.PORTRAIT);
//this can take several seconds in some environments:
fPrintServices = Arrays.asList(
PrintServiceLookup.lookupPrintServices(DocFlavor.INPUT_STREAM.PDF, attrs)
);
long end = System.nanoTime();
log("Finished fetching list of printers. Nanos: " + (end-start));
log("Num printers found:" + fPrintServices.size());
436

Collected Java Practices

fLatch.countDown();
}
/** Return an unmodifiable list of printers. */
List<PrintService> getPrinterList(){
return Collections.unmodifiableList(fPrintServices);
}
// PRIVATE
/** Used to communicate between threads. */
private CountDownLatch fLatch;
private List<PrintService> fPrintServices;
private static void log(String aMsg){
System.out.println(aMsg);
}
}

Here's an example run:


Launching application.
Worker thread started...
Finished fetching list of printers. Nanos: 586542213
Num printers found:0
Seeing this many printers:0

Older JDKs
If a modern JDK is not available to you, then you can't use the services of java.util.concurrent .
Instead, you will need to rely on other means - often the Thread class, and the wait and notify methods
of the Object class.
The following technique uses the wait-loop idiom and notifyAll . To be safe, always use notifyAll
instead of notify. (As an optimization, notify can be used instead of notifyAll , but only if you know
exactly what you are doing.)
Important points:
wait
wait

and notifyAll must always occur within a synchronized block


must always be invoked within a while loop, and not in an if statement

Example
In this example, the Airplane always needs to check with the Airport to see if it has an available
runway before it's able to take off or land.
/** Uses wait loop idiom for inter-thread communication. */
public final class Airplane implements Runnable {
public Airplane (Airport aAirport, String aFlightId){
fAirport = aAirport;
fFlightId = aFlightId;
}
@Override public void run() {
takeOff();
fly();
land();
}
// PRIVATE
private Airport fAirport;
private String fFlightId;
private void takeOff() {
synchronized( fAirport ) {
437

Collected Java Practices

//always use a while loop, never an if-statement:


while (!fAirport.hasAvailableRunway()) {
System.out.println( fFlightId + ": waiting for runway..." );
try {
//wait for notification from the airport that
//the state of the airport has changed.
//wait must always occur within a synchronized block
fAirport.wait();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
System.err.println( ex );
}
}
//there is an available runway now, so we may take off
System.out.println(fFlightId + ": taking off now...");
}
}
private void fly() {
System.out.println(fFlightId + ": flying now...");
try {
//do nothing for several seconds
Thread.sleep(10000);
}
catch (InterruptedException ex){
System.err.println(ex);
Thread.currentThread().interrupt();
}
}
private void land() {
synchronized(fAirport) {
while (!fAirport.hasAvailableRunway()) {
//wait for notification from the airport that
//the state of the airport has changed.
System.out.println(fFlightId + ": waiting for runway...");
try {
fAirport.wait();
}
catch (InterruptedException ex) {
System.err.println( ex );
Thread.currentThread().interrupt();
}
}
//there is an available runway now, so we may take off
System.out.println(fFlightId + ": landing now...");
}
}
}

/** Notifies Airplanes when its state changes. */


public final class Airport implements Runnable {
public Airport(String aName) {
super();
fName = aName;
}
public synchronized boolean hasAvailableRunway() {
return fHasAvailableRunway;
}
@Override public void run() {
System.out.println("Running " + fName + " Airport.");
while (true) {
try {
synchronized(this) {
//simply toggle the state between available and not available
fHasAvailableRunway = !fHasAvailableRunway;
System.out.println(fName + " Has Available Runway: " + fHasAvailableRunway);
//notify all waiters of the change of state
notifyAll();
}
//pause execution for a few seconds
438

Collected Java Practices

Thread.sleep(1000);
}
catch (InterruptedException ex){
System.err.println(ex);
Thread.currentThread().interrupt();
}
}
}
//PRIVATE
private boolean fHasAvailableRunway = true;
private String fName;
}

An example run of the following FlightSimulator class resulted in this output:


Running Flight Simulator.
Terminating the original user thread.
Running Charles de Gaulle Airport.
Charles de Gaulle Has Available Runway:
Flight 8875: waiting for runway...
Charles de Gaulle Has Available Runway:
Flight 8875: taking off now...
Flight 8875: flying now...
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:
Flight 8875: waiting for runway...
Charles de Gaulle Has Available Runway:
Flight 8875: landing now...
Charles de Gaulle Has Available Runway:
Charles de Gaulle Has Available Runway:

false
true

false
true
false
true
false
true
false
true
false
true
false
true

/** Builds and starts threads for Airport and Airplanes. */


public final class FlightSimulator {
public static void main(String... arguments) {
System.out.println("Running Flight Simulator.");
//build an airport and start it running
Airport charlesDeGaulle = new Airport("Charles de Gaulle");
Thread airport = new Thread(charlesDeGaulle);
airport.start();
//build a plane and start it running
Thread planeOne = new Thread(new Airplane(charlesDeGaulle, "Flight 8875"));
planeOne.start();
//notice that this user thread now ends, but the program itself does
//NOT end since the threads created above are also user
//threads. All user threads have equal status, and there
//is nothing special about the thread which launches a program.
System.out.println("Terminating the original user thread.");
}
}

439

Collected Java Practices

See Also :
Launch thread is just another user thread

Perform N tasks in parallel


There are some cases in which performing tasks in parallel can be useful. This option is especially
attractive when:
the tasks are independent of each other, but do essentially the same thing
the tasks are limited by slow operations - typically, network access or disk access
A good example of this comes from web site administration. Administrators need to monitor a set of N
web sites, so they might send a 'ping' of some sort to each site, to see if a healthy response is returned in
each case. Note that these pings fit the above criteria - each ping is independent of the others, and yet is
essentially the same task.
Below is an example implementation of this idea. For purposes of comparison, this example actually has
3 different implementations:
execute tasks in parallel, and report the result of each task as soon as it completes.
execute tasks in parallel, but report the results of all tasks only at the end, after they have all
completed.
execute tasks serially, one after the other, and all within a single thread; report each task as it
completes.
You can compare the performance of these 3 implementations, to see which works best for you.
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

java.io.IOException;
java.net.MalformedURLException;
java.net.URL;
java.net.URLConnection;
java.util.ArrayList;
java.util.Arrays;
java.util.Collection;
java.util.List;
java.util.concurrent.Callable;
java.util.concurrent.CompletionService;
java.util.concurrent.ExecutionException;
java.util.concurrent.ExecutorCompletionService;
java.util.concurrent.ExecutorService;
java.util.concurrent.Executors;
java.util.concurrent.Future;

/**
Ping N web sites in parallel.
The ping simply does a GET, and looks at the first header line.
This example could be applied to many sorts of similar tasks.
<P>No time-out is used here. As usual, be wary of warm-up
of the just-in-time compiler. You might want to use -Xint.
*/
public final class CheckSites {
/** Run this tool. */
public static final void main(String... aArgs) {
CheckSites checker = new CheckSites();
try {
log("Parallel, report each as it completes:");
440

Collected Java Practices

checker.pingAndReportEachWhenKnown();
log("Parallel, report all at end:");
checker.pingAndReportAllAtEnd();
log("Sequential, report each as it completes:");
checker.pingAndReportSequentially();
}
catch(InterruptedException ex){
Thread.currentThread().interrupt();
}
catch(ExecutionException ex){
log("Problem executing worker: " + ex.getCause());
}
catch(MalformedURLException ex){
log("Bad URL: " + ex.getCause());
}
log("Done.");
}
/**
Check N sites, in parallel, using up to 4 threads.
Report the result of each 'ping' as it comes in.
(This is likely the style most would prefer.)
*/
void pingAndReportEachWhenKnown() throws InterruptedException, ExecutionException
int numThreads = URLs.size() > 4 ? 4 : URLs.size(); //max 4 threads
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
CompletionService<PingResult> compService = new
ExecutorCompletionService<>(executor);
for(String url : URLs){
Task task = new Task(url);
compService.submit(task);
}
for(String url : URLs){
Future<PingResult> future = compService.take();
log(future.get());
}
executor.shutdown(); //always reclaim resources
}
/**
Check N sites, in parallel, using up to 4 threads.
Report the results only when all have completed.
*/
void pingAndReportAllAtEnd() throws InterruptedException, ExecutionException {
Collection<Callable<PingResult>> tasks = new ArrayList<>();
for(String url : URLs){
tasks.add(new Task(url));
}
int numThreads = URLs.size() > 4 ? 4 : URLs.size(); //max 4 threads
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
List<Future<PingResult>> results = executor.invokeAll(tasks);
for(Future<PingResult> result : results){
PingResult pingResult = result.get();
log(pingResult);
}
executor.shutdown(); //always reclaim resources
}
/**
Check N sites, but sequentially, not in parallel.
Does not use multiple threads at all.
*/
void pingAndReportSequentially() throws MalformedURLException {
for(String url : URLs){
PingResult pingResult = pingAndReportStatus(url);
log(pingResult);
}
}
// PRIVATE
private static final List<String> URLs = Arrays.asList(
"http://www.youtube.com/", "http://www.google.ca/",
"http://www.date4j.net", "http://www.web4j.com"
);
private static void log(Object aMsg){
441

Collected Java Practices

System.out.println(String.valueOf(aMsg));
}
/** Try to ping a URL. Return true only if successful. */
private final class Task implements Callable<PingResult> {
Task(String aURL){
fURL = aURL;
}
/** Access a URL, and see if you get a healthy response. */
@Override public PingResult call() throws Exception {
return pingAndReportStatus(fURL);
}
private final String fURL;
}
private PingResult pingAndReportStatus(String aURL) throws MalformedURLException {
PingResult result = new PingResult();
result.URL = aURL;
long start = System.currentTimeMillis();
URL url = new URL(aURL);
try {
URLConnection connection = url.openConnection();
int FIRST_LINE = 0;
String firstLine = connection.getHeaderField(FIRST_LINE);
result.SUCCESS = true;
long end = System.currentTimeMillis();
result.TIMING = end - start;
}
catch(IOException ex){
//ignore - fails
}
return result;
}
/** Simple struct to hold all the date related to a ping. */
private static final class PingResult {
String URL;
Boolean SUCCESS;
Long TIMING;
@Override public String toString(){
return "Result:" + SUCCESS + " " +TIMING + " msecs " + URL;
}
}
}

Prefer modern libraries for concurrency


Version 1.5 of the JDK included a new package called java.util.concurrent. It contains modern tools for
operations involving threads. You should usually prefer using this package instead of older, more lowlevel techniques.
The java.util.concurrent package is fairly large. The best guide to its contents is likely the excellent book
Java Concurrency in Practice, written by Brian Goetz and other experts. The following is a very brief
sketch of the most important elements in java.util.concurrent.
The most important idea is the separation of tasks and the policy for their execution. A task (an informal
term) comes in two flavours:
java.lang.Runnable - a task which doesn't return a value (like a void method).
Callable - a task which can return a value (this is the more flexible form)

For executing tasks, there are 3 important interfaces, which are linked in an inheritance chain. Starting at
the top level, they are:
442

Collected Java Practices

Executor - a single execute method


ExecutorService - common workhorse; submit and
ScheduledExecutorService - executes tasks after a

invoke methods; includes lifecycle methods


specific time interval, or periodically

The Executors factory class returns implementations of ExecutorService and


ScheduledExecutorService .
When you need to be informed of the result of a task after it completes, you will likely use these items:
Future - see the result of a task, or cancel it
CompletionService - accepts tasks, and returns their Futures
ExecutorCompletionService - an implementation of CompletionService

Finally, the following are used to synchronize between threads:


CountDownLatch
CyclicBarrier
FutureTask

Some example code:


pinging N web sites in parallel
fetching the list of available printers
Concurrency uses many specialized terms. Some definitions:
user thread, daemon thread - in Java, the JRE terminates when all user threads terminate; a single
user thread will prevent the JRE from terminating. A daemon thread, on the other hand, will not
prevent the JRE from terminating.
blocking - if a thread pauses execution until a certain condition is met, then it's said to be blocking.
safety - when nothing unpleasant happens in a multithreaded environment (this is admittedly
vague).
liveness - execution continues, eventually; there may be significant pauses in a thread, but the
application never completely hangs.
deadlock - execution hangs, since a pair of threads each holds (and keeps) a lock that the other
thread needs in order to continue.
race condition - if you are unlucky with the timing of how threads interleave their execution, then
bad things will happen.
re-entrant - if a thread already holds a re-entrant lock, and if it needs to re-acquire the lock, then
the re-acquisition always succeeds without blocking. Most locks are re-entrant, including Java's
intrinsic locks (see below).
intrinsic lock - the built-in locks of the Java language. These correspond to uses of the
synchronized keyword.
mutex lock - mutually exclusive locks are held by at most one thread at a time. For example,
intrinsic locks are mutexes. Read-write locks are not mutexes, since they allow N readers to access
the same data at once.
thread confinement - data that is only accessed by a single thread is always safe, since it's confined
to a single thread.
semaphore - object pools containing a limited number of resources use a semaphore to keep track
of how many of the resources are currently in use.
Here's the lifecycle of a task submitted to an Executor :
created - the task object has been created, but not yet submitted to an Executor .
443

Collected Java Practices

submitted - the task has been submitted to an Executor , but hasn't been started yet. In this state,
the task can always be cancelled.
started - the task has begun. The task may be cancelled, if the task is responsive to interruption.
completed - the task has finished. Cancelling a completed task has no effect.

See Also :
Modernize old code

Query host for the number of processors


When deciding on an execution policy for your multi-threaded tasks, you sometimes need to decide how
many threads to use. Often, you may want to base this upon the number of processors available.
Instead of hard-coding such values, you should likely query the host for the number of available
processors, using
Runtime.getRuntime().availableProcessors();

Read-write locks
In the great majority of database applications, the frequency of read operations greatly exceeds the
frequency of write operations. This is why databases implement read-write locks for their records, which
allow for concurrent reading, but still demand exclusive writing. This can markedly increase
performance.
Occasionally, a class may benefit as well from a read-write lock, for exactly the same reasons - reads are
much more frequent than writes. As usual, you should measure performance to determine if a read-write
lock is really improving performance.
Here's a sketch of how to use such locks. It uses the ReentrantReadWriteLock of the
java.util.concurrent package.
import
import
import
import

java.util.LinkedHashMap;
java.util.Map;
java.util.concurrent.locks.Lock;
java.util.concurrent.locks.ReentrantReadWriteLock;

/**
User preferences, using a read-write lock.
<P>The context: preference information is read in upon startup.
The config data is 'read-mostly': usually, a caller simply reads the
information. It gets updated only occasionally.
<P>Using a read-write lock means that multiple readers can access the
same data simultaneously. If a single writer gets the lock, however, then
all other callers (either reader or writer) will block until the lock is
released by the writer.
444

Collected Java Practices

<P>(In practice, Brian Goetz reports that the implementation of ConcurrentHashMap


is so good that it would likely suffice in many cases, instead of a read-write
lock.)
*/
public final class Preferences {
/** Fetch a setting - this is the more common operation.
public String fetch(String aName){
String result = "";
fReadLock.lock();
try {
result = fPreferences.get(aName);
}
finally {
fReadLock.unlock();
}
return result;
}

*/

/** Change a setting - this is the less common operation. */


public void update(String aName, String aValue){
fWriteLock.lock();
try {
fPreferences.put(aName, aValue);
}
finally {
fWriteLock.unlock();
}
}
//...elided
// PRIVATE
/** Holds the
private final
private final
private final
private final

preferences as simple name-value pairs of Strings. */


Map<String, String> fPreferences = new LinkedHashMap<>();
ReentrantReadWriteLock fLock = new ReentrantReadWriteLock();
Lock fReadLock = fLock.readLock();
Lock fWriteLock = fLock.writeLock();

See Also :
Use finally to unlock

Remember the types of intrinsic lock


In Java, an intrinsic lock is implied by each use of the synchronized keyword. In this case, the locking is
performed by Java behind the scenes. (This is distinct from the programmer using or defining an explicit
lock object themselves.)
Each use of the synchronized keyword is associated with one of the two types of intrinsic lock:
an "instance lock", attached to a single object
a "static lock", attached to a class
If a method is declared as synchronized, then it will acquire either the instance lock or the static lock
when it is invoked, according to whether it is an instance method or a static method.
The two types of lock have similar behaviour, but are completely independent of each other.
445

Collected Java Practices

Acquiring the instance lock only blocks other threads from invoking a synchronized instance method; it
does not block other threads from invoking an un-synchronized method, nor does it block them from
invoking a static synchronized method.
Similarly, acquiring the static lock only blocks other threads from invoking a static synchronized
method; it does not block other threads from invoking an un-synchronized method, nor does it block them
from invoking a synchronized instance method.
Outside of a method header, synchronized(this) acquires the instance lock.
The static lock can be acquired outside of a method header in two ways:
synchronized(Blah.class) , using
synchronized(this.getClass()) ,

the class literal


if an object is available

See Also :
Synchronize access to mutable fields

Schedule periodic tasks


Tasks can be scheduled for execution in the future. Such tasks can be performed either periodically, or
just once. As well, the first execution can be delayed to a specific time in the future.
There are two different styles of implementing a scheduled task:
ScheduledExecutorService
Timer and TimerTask

and ScheduledFuture

The first pair is the more modern API. As usual, the more modern API is usually the preferred one. The
main difference between these two APIs is that the first always uses relative times, while the second does
not. If needed, you always can transform a Date into a relative time:
Date futureDate = ...
long startTime = futureDate.getTime() - System.currentTimeMillis();

(This will only work if the system clock is not reset.)


Example 1
Here's an example of an AlarmClock class, which uses a ScheduledExecutorService :
import
import
import
import

java.util.concurrent.Executors;
java.util.concurrent.ScheduledExecutorService;
java.util.concurrent.ScheduledFuture;
java.util.concurrent.TimeUnit;

/**
Run a simple task once every second, starting 3 seconds from now.
Cancel the task after 20 seconds.
*/
public final class AlarmClock {
/** Run the example. */
public static void main(String... aArgs) throws InterruptedException {
log("Main started.");
446

Collected Java Practices

AlarmClock alarmClock = new AlarmClock(3, 1, 20);


alarmClock.activateAlarmThenStop();
/*
To start the alarm at a specific date in the future, the initial delay
needs to be calculated relative to the current time, as in :
Date futureDate = ...
long startTime = futureDate.getTime() - System.currentTimeMillis();
AlarmClock alarm = new AlarmClock(startTime, 1, 20);
This works only if the system clock isn't reset.
*/
log("Main ended.");
}
AlarmClock(long aInitialDelay, long aDelayBetweenBeeps, long aStopAfter){
fInitialDelay = aInitialDelay;
fDelayBetweenRuns = aDelayBetweenBeeps;
fShutdownAfter = aStopAfter;
fScheduler = Executors.newScheduledThreadPool(NUM_THREADS);
}
/** Sound the alarm for a few seconds, then stop. */
void activateAlarmThenStop(){
Runnable soundAlarmTask = new SoundAlarmTask();
ScheduledFuture<?> soundAlarmFuture = fScheduler.scheduleWithFixedDelay(
soundAlarmTask, fInitialDelay, fDelayBetweenRuns, TimeUnit.SECONDS
);
Runnable stopAlarm = new StopAlarmTask(soundAlarmFuture);
fScheduler.schedule(stopAlarm, fShutdownAfter, TimeUnit.SECONDS);
}
// PRIVATE
private final
private final
private final
private final

ScheduledExecutorService fScheduler;
long fInitialDelay;
long fDelayBetweenRuns;
long fShutdownAfter;

private static void log(String aMsg){


System.out.println(aMsg);
}
/** If invocations might overlap, you can specify more than a single thread.*/
private static final int NUM_THREADS = 1;
private static final boolean DONT_INTERRUPT_IF_RUNNING = false;
private static final class SoundAlarmTask implements Runnable {
@Override public void run() {
++fCount;
log("beep " + fCount);
}
private int fCount;
}
private final class StopAlarmTask implements Runnable {
StopAlarmTask(ScheduledFuture<?> aSchedFuture){
fSchedFuture = aSchedFuture;
}
@Override public void run() {
log("Stopping alarm.");
fSchedFuture.cancel(DONT_INTERRUPT_IF_RUNNING);
/*
Note that this Task also performs cleanup, by asking the
scheduler to shutdown gracefully.
*/
fScheduler.shutdown();
}
private ScheduledFuture<?> fSchedFuture;
}
}

Example 2
This example uses the older classes, Timer and TimerTask .
Here, a task is performed once a day at 4 a.m., starting tomorrow morning.
447

Collected Java Practices

import
import
import
import
import

java.util.Timer;
java.util.TimerTask;
java.util.Calendar;
java.util.GregorianCalendar;
java.util.Date;

public final class FetchMail extends TimerTask {


/** Construct and use a TimerTask and Timer. */
public static void main (String... arguments ) {
TimerTask fetchMail = new FetchMail();
//perform the task once a day at 4 a.m., starting tomorrow morning
//(other styles are possible as well)
Timer timer = new Timer();
timer.scheduleAtFixedRate(fetchMail, getTomorrowMorning4am(), fONCE_PER_DAY);
}
/**
* Implements TimerTask's abstract run method.
*/
@Override public void run(){
//toy implementation
System.out.println("Fetching mail...");
}
// PRIVATE
//expressed in milliseconds
private final static long fONCE_PER_DAY = 1000*60*60*24;
private final static int fONE_DAY = 1;
private final static int fFOUR_AM = 4;
private final static int fZERO_MINUTES = 0;
private static Date getTomorrowMorning4am(){
Calendar tomorrow = new GregorianCalendar();
tomorrow.add(Calendar.DATE, fONE_DAY);
Calendar result = new GregorianCalendar(
tomorrow.get(Calendar.YEAR),
tomorrow.get(Calendar.MONTH),
tomorrow.get(Calendar.DATE),
fFOUR_AM,
fZERO_MINUTES
);
return result.getTime();
}
}

See Also :
Timers

Stop threads through cooperation


In general, you should use modern libraries such as java.util.concurrent when dealing with threads.
If you're forced to use a low-level Thread object, for some reason, then stopping those threads should be
done with care. Threads should not be stopped using stop or suspend. These methods are deprecated.
Instead, threads should use the following simple idiom to stop in a well-behaved manner.
public final class SimpleThread implements Runnable {
@Override public void run() {
448

Collected Java Practices

boolean isDone = false;


while (!isStopRequested() && !isDone){
//perform the work
//if finished, set isDone to true
}
}
/** Request that this thread stop running.
public synchronized void requestStop(){
fIsStopRequested = true;
}

*/

// PRIVATE
private boolean fIsStopRequested;
private synchronized boolean isStopRequested() {
return fIsStopRequested;
}
}

See Also :
Prefer modern libraries for concurrency

Synchronize access to mutable fields


In a multi-threaded environment, accessing mutable data (data that can change) must always be
coordinated between readers and writers. The task of making sure that readers and writers don't interfere
with each other in undesirable ways is called synchronization. Synchronization can be done with an
explicit lock object, but a more common style is to use the intrinsic locks implied by the synchronized
keyword.
For example, in a multi-threaded environment, all get and set methods for mutable fields should usually
be synchronized methods. This includes primitive fields.
Most classes do not need to be designed for operation in a multi-threaded environment, and can ignore
these considerations.
If an object does need to live in a multi-threaded environment, however, then a significant amount of
care must be taken to ensure that it is correctly designed.
If an object is immutable, then it's automatically thread-safe. If it's mutable, then extra steps must be
taken to ensure thread-safety: every use of every mutable field must be synchronized in some way
(usually with using the synchronized keyword).
Here, mutable field simply means a field which might change in any way, after the initial construction of
the object. (Objects are never shared between threads until after the object is fully created.) For example,
an int field that changes its value some time after the constructor completes
a Date field that changes state some time after the constructor completes, to represent a different
date from the original
any object field that is "pointed" to a new object, some time after the constructor completes
Remember that all local variables declared in the body of a method are never shared between threads, and
449

Collected Java Practices

so have no thread-safety considerations.


It's a misconception that all mutable primitives except long and double do not need synchronized
access.
Example
Note that even the get of the int field is a synchronized method.
import java.util.Date;
/**
* This class is mutable, but thread-safe : the caller never
* needs to perform external synchronization, except when multiple calls
* need to be treated as a single unit.
*
* This class illustrates the three possible cases for fields :
*<ul>
* <li> a primitive (int)
* <li> an immutable object (String)
* <li> a mutable object (Date)
*</ul>
*/
public final class MutablePlanet {
public MutablePlanet(int aId, String aName, Date aDateOfDiscovery) {
fId = aId;
fName = aName;
//Make a private copy of aDateOfDiscovery.
//This is the only way to keep the fDateOfDiscovery
//field private, and shields this class from any changes
//to the original aDateOfDiscovery object which might be
//made by the caller.
fDateOfDiscovery = new Date(aDateOfDiscovery.getTime());
}
public synchronized int getId() {
return fId;
}
public synchronized void setId(int aNewId) {
fId = aNewId;
}
public synchronized String getName() {
return fName;
}
public synchronized void setName(String aNewName) {
fName = aNewName;
}
/**
* Returns a defensive copy of the field.
* The caller of this method can do anything they want with the
* returned Date object, without affecting the internals of this
* class in any way.
*/
public synchronized Date getDateOfDiscovery() {
return new Date(fDateOfDiscovery.getTime());
}
public synchronized void setDateOfDiscovery(Date aNewDiscoveryDate) {
//change the state of the mutable Date field
fDateOfDiscovery.setTime(aNewDiscoveryDate.getTime());
}
// PRIVATE
/**
* A primitive field.
*/
private int fId;
/**
* An immutable object field.
* Strings never alter state after construction.
*/
private String fName;
450

Collected Java Practices

/**
* A mutable object field.
* The state of a Date can change after construction.
*/
private Date fDateOfDiscovery;
}

See Also :
Immutable objects
Remember the types of intrinsic lock
Document thread safety

Synchronized is implementation detail


Although the synchronized keyword can appear in a method header, it does not form a part of a
method's API or contract. Synchronization is part of the implementation, not part of the interface.
Note: some early Sun documentation (javadoc) included the synchronized keyword in the description of
methods. This error was corrected in Javadoc 1.2.
Example
Here, a concrete implementation of an interface uses synchronized for some methods, even though the
interface methods themselves make no mention of it.
public interface ScientificTheory {
void publish();
void predict();
boolean falsifyThroughMeasurement();
}

public final class LoopQuantumGravity implements ScientificTheory {


@Override public synchronized void publish() {
//..elided
}
@Override public synchronized void predict() {
//..elided
}
@Override public boolean falsifyThroughMeasurement() {
return true; //stub
}
}

451

Collected Java Practices

See Also :
Document thread safety

Thread priorities are not portable


The behaviour of the thread scheduler, thread priorities, and Thread.yield are highly dependent on the
Java Runtime implementation you happen to be using. You cannot rely on them to define the logic of
your application.
See Also :
Do not break portability

Use finally to unlock


Sometimes Lock objects are used to control access to mutable data in a multithreaded environment. When
a thread is finished with the data, you must ensure the lock is released.
Brian Goetz in Java Concurrency in Practice strongly recommends the following technique for ensuring
that a lock is released. It uses a try..finally style; this ensures that no matter what happens during the
time the lock is held, it will eventually be unlocked. Even if an unexpected RuntimeException is thrown,
the lock will still be released.
Lock myLock = ...
myLock.lock();
try {
//interact with mutable data
}
finally{
myLock.unlock();
}

Here's an example which follows the above pattern, with a read-write lock.
import
import
import
import

java.util.LinkedHashMap;
java.util.Map;
java.util.concurrent.locks.Lock;
java.util.concurrent.locks.ReentrantReadWriteLock;

/**
User preferences, using a read-write lock.
<P>The context: preference information is read in upon startup.
The config data is 'read-mostly': usually, a caller simply reads the
information. It gets updated only occasionally.
<P>Using a read-write lock means that multiple readers can access the
same data simultaneously. If a single writer gets the lock, however, then
all other callers (either reader or writer) will block until the lock is
released by the writer.
<P>(In practice, Brian Goetz reports that the implementation of ConcurrentHashMap
is so good that it would likely suffice in many cases, instead of a read-write
lock.)
*/
public final class Preferences {
452

Collected Java Practices

/** Fetch a setting - this is the more common operation.


public String fetch(String aName){
String result = "";
fReadLock.lock();
try {
result = fPreferences.get(aName);
}
finally {
fReadLock.unlock();
}
return result;
}

*/

/** Change a setting - this is the less common operation. */


public void update(String aName, String aValue){
fWriteLock.lock();
try {
fPreferences.put(aName, aValue);
}
finally {
fWriteLock.unlock();
}
}
//...elided
// PRIVATE
/** Holds the
private final
private final
private final
private final

preferences as simple name-value pairs of Strings. */


Map<String, String> fPreferences = new LinkedHashMap<>();
ReentrantReadWriteLock fLock = new ReentrantReadWriteLock();
Lock fReadLock = fLock.readLock();
Lock fWriteLock = fLock.writeLock();

See Also :
Finally and catch
Read-write locks
Copyright Hirondelle Systems.

453

You might also like