Thứ Năm, 23 tháng 1, 2014

Tài liệu Addison Wesley - Effective Java Programming Language Guide pptx

Effective Java: Programming Language Guide
1
Foreword

If a colleague were to say to you, “Spouse of me this night today manufactures the unusual
meal in a home. You will join?” three things would likely cross your mind: third, that you had
been invited to dinner; second, that English was not your colleague's first language; and first,
a good deal of puzzlement.
If you have ever studied a second language yourself and then tried to use it outside the
classroom, you know that there are three things you must master: how the language is
structured (grammar), how to name things you want to talk about (vocabulary), and the
customary and effective ways to say everyday things (usage). Too often only the first two are
covered in the classroom, and you find native speakers constantly suppressing their laughter
as you try to make yourself understood.
It is much the same with a programming language. You need to understand the core language:
is it algorithmic, functional, object-oriented? You need to know the vocabulary: what data
structures, operations, and facilities are provided by the standard libraries? And you need to
be familiar with the customary and effective ways to structure your code. Books about
programming languages often cover only the first two, or discuss usage only spottily. Maybe
that's because the first two are in some ways easier to write about. Grammar and vocabulary
are properties of the language alone, but usage is characteristic of a community that uses it.
The Java programming language, for example, is object-oriented with single inheritance and
supports an imperative (statement-oriented) coding style within each method. The libraries
address graphic display support, networking, distributed computing, and security. But how is
the language best put to use in practice?
There is another point. Programs, unlike spoken sentences and unlike most books and
magazines, are likely to be changed over time. It's typically not enough to produce code that
operates effectively and is readily understood by other persons; one must also organize the
code so that it is easy to modify. There may be ten ways to write code for some task T. Of
those ten ways, seven will be awkward, inefficient, or puzzling. Of the other three, which is
most likely to be similar to the code needed for the task T' in next year's software release?
There are numerous books from which you can learn the grammar of the Java Programming
Language, including The Java Programming Language by Arnold, Gosling, and Holmes
[Arnold00] or The Java Language Specification by Gosling, Joy, yours truly, and Bracha
[JLS]. Likewise, there are dozens of books on the libraries and APIs associated with the Java
programming language.

This book addresses your third need: customary and effective usage. Joshua Bloch has spent
years extending, implementing, and using the Java programming language at Sun
Microsystems; he has also read a lot of other people's code, including mine. Here he offers
good advice, systematically organized, on how to structure your code so that it works well, so
that other people can understand it, so that future modifications and improvements are less
Effective Java: Programming Language Guide
2
likely to cause headaches—perhaps, even, so that your programs will be pleasant, elegant, and
graceful.
Guy L. Steele Jr.
Burlington, Massachusetts
April 2001
Effective Java: Programming Language Guide
3
Preface
In 1996 I pulled up stakes and headed west to work for JavaSoft, as it was then known,
because it was clear that that was where the action was. In the intervening five years I've
served as Java platform libraries architect. I've designed, implemented, and maintained many
of the libraries and served as a consultant for many others. Presiding over these libraries as the
Java platform matured was a once-in-a-lifetime opportunity. It is no exaggeration to say that
I had the privilege to work with some of the great software engineers of our generation. In the
process, I learned a lot about the Java programming language—what works, what doesn't, and
how to use the language and its libraries to best effect.
This book is my attempt to share my experience with you so that you can imitate my
successes while avoiding my failures. I borrowed the format from Scott Meyers's Effective
C++ [Meyers98], which consists of fifty items, each conveying one specific rule for
improving your programs and designs. I found the format to be singularly effective, and
I hope you do too.
In many cases, I took the liberty of illustrating the items with real-world examples from
the Java platform libraries. When describing something that could have been done better,
I tried to pick on code that I wrote myself, but occasionally I pick on something written by
a colleague. I sincerely apologize if, despite my best efforts, I've offended anyone. Negative
examples are cited not to cast blame but in the spirit of cooperation, so that all of us can
benefit from the experience of those who've gone before.
While this book is not targeted solely at developers of reusable components, it is inevitably
colored by my experience writing such components over the past two decades. I naturally
think in terms of exported APIs (Application Programming Interfaces), and I encourage you
to do likewise. Even if you aren't developing reusable components, thinking in these terms
tends to improve the quality of the software you write. Furthermore, it's not uncommon to
write a reusable component without knowing it: You write something useful, share it with
your buddy across the hall, and before long you have half a dozen users. At this point, you no
longer have the flexibility to change the API at will and are thankful for all the effort that you
put into designing the API when you first wrote the software.
My focus on API design may seem a bit unnatural to devotees of the new lightweight
software development methodologies, such as Extreme Programming [Beck99]. These
methodologies emphasize writing the simplest program that could possibly work. If you're
using one of these methodologies, you'll find that a focus on API design serves you well in
the refactoring process. The fundamental goals of refactoring are the improvement of system
structure and the avoidance of code duplication. These goals are impossible to achieve in
the absence of well-designed APIs for the components of the system.
No language is perfect, but some are excellent. I have found the Java programming language
and its libraries to be immensely conducive to quality and productivity, and a joy to work
with. I hope this book captures my enthusiasm and helps make your use of the language more
effective and enjoyable.
Joshua Bloch
Cupertino, California
April 2001
Effective Java: Programming Language Guide
4
Acknowledgments
I thank Patrick Chan for suggesting that I write this book and for pitching the idea to Lisa
Friendly, the series managing editor; Tim Lindholm, the series technical editor; and Mike
Hendrickson, executive editor of Addison-Wesley Professional. I thank Lisa, Tim, and Mike
for encouraging me to pursue the project and for their superhuman patience and unyielding
faith that I would someday write this book.
I thank James Gosling and his original team for giving me something great to write about, and
I thank the many Java platform engineers who followed in James's footsteps. In particular,
I thank my colleagues in Sun's Java Platform Tools and Libraries Group for their insights,
their encouragement, and their support. The team consists of Andrew Bennett, Joe Darcy,
Neal Gafter, Iris Garcia, Konstantin Kladko, Ian Little, Mike McCloskey, and Mark Reinhold.
Former members include Zhenghua Li, Bill Maddox, and Naveen Sanjeeva.
I thank my manager, Andrew Bennett, and my director, Larry Abrahams, for lending their full
and enthusiastic support to this project. I thank Rich Green, the VP of Engineering at Java
Software, for providing an environment where engineers are free to think creatively and to
publish their work.
I have been blessed with the best team of reviewers imaginable, and I give my sincerest
thanks to each of them: Andrew Bennett, Cindy Bloch, Dan Bloch, Beth Bottos, Joe Bowbeer,
Gilad Bracha, Mary Campione, Joe Darcy, David Eckhardt, Joe Fialli, Lisa Friendly, James
Gosling, Peter Haggar, Brian Kernighan, Konstantin Kladko, Doug Lea, Zhenghua Li, Tim
Lindholm, Mike McCloskey, Tim Peierls, Mark Reinhold, Ken Russell, Bill Shannon, Peter
Stout, Phil Wadler, and two anonymous reviewers. They made numerous suggestions that led
to great improvements in this book and saved me from many embarrassments. Any remaining
embarrassments are my responsibility.
Numerous colleagues, inside and outside Sun, participated in technical discussions that
improved the quality of this book. Among others, Ben Gomes, Steffen Grarup, Peter Kessler,
Richard Roda, John Rose, and David Stoutamire contributed useful insights. A special thanks
is due Doug Lea, who served as a sounding board for many of the ideas in this book. Doug
has been unfailingly generous with his time and his knowledge.
I thank Julie Dinicola, Jacqui Doucette, Mike Hendrickson, Heather Olszyk, Tracy Russ, and
the whole team at Addison-Wesley for their support and professionalism. Even under
an impossibly tight schedule, they were always friendly and accommodating.
I thank Guy Steele for writing the foreword. I am honored that he chose to participate in this
project.
Finally, I thank my wife, Cindy Bloch, for encouraging and occasionally threatening me to
write this book, for reading each item in its raw form, for helping me with Framemaker, for
writing the index, and for putting up with me while I wrote.
Effective Java: Programming Language Guide
5
Chapter 1. Introduction
This book is designed to help you make the most effective use of the Java™ programming
language and its fundamental libraries,
java.lang, java.util, and, to a lesser extent,
java.io. The book discusses other libraries from time to time, but it does not cover graphical
user interface programming or enterprise APIs.
This book consists of fifty-seven items, each of which conveys one rule. The rules capture
practices generally held to be beneficial by the best and most experienced programmers.
The items are loosely grouped into nine chapters, each concerning one broad aspect of
software design. The book is not intended to be read from cover to cover: Each item stands on
its own, more or less. The items are heavily cross-referenced so you can easily plot your own
course through the book.
Most items are illustrated with program examples. A key feature of this book is that it
contains code examples illustrating many design patterns and idioms. Some are old, like
Singleton (Item 2), and others are new, like Finalizer Guardian (Item 6) and Defensive
readResolve (Item 57). A separate index is provided for easy access to these patterns and
idioms (page 239). Where appropriate, they are cross-referenced to the standard reference
work in this area [Gamma95].
Many items contain one or more program examples illustrating some practice to be avoided.
Such examples, sometimes known as antipatterns, are clearly labeled with a comment such as

// Never do this!” In each case, the item explains why the example is bad and suggests an
alternative approach.
This book is not for beginners: it assumes that you are already comfortable with the Java
programming language. If you are not, consider one of the many fine introductory texts
[Arnold00, Campione00]. While the book is designed to be accessible to anyone with
a working knowledge of the language, it should provide food for thought even for advanced
programmers.
Most of the rules in this book derive from a few fundamental principles. Clarity and
simplicity are of paramount importance. The user of a module should never be surprised by its
behavior. Modules should be as small as possible but no smaller. (As used in this book,
the term module refers to any reusable software component, from an individual method to
a complex system consisting of multiple packages.) Code should be reused rather than copied.
The dependencies between modules should be kept to a minimum. Errors should be detected
as soon as possible after they are made, ideally at compile time.
While the rules in this book do not apply 100 percent of the time, they do characterize best
programming practices in the great majority of cases. You should not slavishly follow these
rules, but you should violate them only occasionally and with good reason. Learning the art of
programming, like most other disciplines, consists of first learning the rules and then learning
when to violate them.
For the most part, this book is not about performance. It is about writing programs that are
clear, correct, usable, robust, flexible, and maintainable. If you can do that, it's usually
a relatively simple matter to get the performance you need (Item 37). Some items do discuss
performance concerns, and a few of these items provide performance numbers. These
Effective Java: Programming Language Guide
6
numbers, which are introduced with the phrase “On my machine,” should be regarded as
approximate at best.
For what it's worth, my machine is an aging homebuilt 400 MHz Pentium® II with 128
megabytes of RAM, running Sun's 1.3 release of the Java 2 Standard Edition Software
Development Kit (SDK) atop Microsoft Windows NT® 4.0. This SDK includes Sun's Java
HotSpot™ Client VM, a state-of-the-art JVM implementation designed for client use.
When discussing features of the Java programming language and its libraries, it is sometimes
necessary to refer to specific releases. For brevity, this book uses “engineering version
numbers” in preference to official release names. Table 1.1 shows the correspondence
between release names and engineering version numbers.
Table 1.1. Java Platform Versions
Official Release Name Engineering Version Number
JDK 1.1.x / JRE 1.1.x 1.1
Java 2 Platform, Standard Edition, v 1.2 1.2
Java 2 Platform, Standard Edition, v 1.3 1.3
Java 2 Platform, Standard Edition, v 1.4

1.4

While features introduced in the 1.4 release are discussed in some items, program examples,
with very few exceptions, refrain from using these features. The examples have been tested on
releases 1.3. Most, if not all, of them should run without modification on release 1.2.
The examples are reasonably complete, but they favor readability over completeness. They
freely use classes from the packages
java.util and java.io. In order to compile
the examples, you may have to add one or both of these import statements:

import java.util.*;
import java.io.*;
Other boilerplate is similarly omitted. The book's Web site,
http://java.sun.com/docs/books/effective, contains an expanded version of each example,
which you can compile and run.
For the most part, this book uses technical terms as they are defined in The Java Language
Specification, Second Edition [JLS]. A few terms deserve special mention. The language
supports four kinds of types: interfaces, classes, arrays, and primitives. The first three are
known as reference types. Class instances and arrays are objects; primitive values are not.
A class's members consist of its fields, methods, member classes, and member interfaces.
A method's signature consists of its name and the types of its formal parameters; the signature
does not include the method's return type.
This book uses a few terms differently from the The Java Language Specification. Unlike
The Java Language Specification, this book uses inheritance as a synonym for subclassing.
Instead of using the term inheritance for interfaces, this book simply states that a class
implements an interface or that one interface extends another. To describe the access level that
applies when none is specified, this book uses the descriptive term package-private instead of
the technically correct term default access [JLS, 6.6.1].
Effective Java: Programming Language Guide
7
This book uses a few technical terms that are not defined in The Java Language Specification.
The term exported API, or simply API, refers to the classes, interfaces, constructors, members,
and serialized forms by which a programmer accesses a class, interface, or package. (The term
API, which is short for application programming interface, is used in preference to the
otherwise preferable term interface to avoid confusion with the language construct of that
name.) A programmer who writes a program that uses an API is referred to as a user of the
API. A class whose implementation uses an API is a client of the API.
Classes, interfaces, constructors, members, and serialized forms are collectively known as API
elements. An exported API consists of the API elements that are accessible outside of
the package that defines the API. These are the API elements that any client can use and
the author of the API commits to support. Not coincidentally, they are also the elements for
which the Javadoc utility generates documentation in its default mode of operation. Loosely
speaking, the exported API of a package consists of the public and protected members and
constructors of every public class or interface in the package.
Effective Java: Programming Language Guide
8
Chapter 2. Creating and Destroying Objects
This chapter concerns creating and destroying objects: when and how to create objects, when
and how to avoid creating them, how to ensure that objects are destroyed in a timely manner,
and how to manage any cleanup actions that must precede object destruction.
Item 1: Consider providing static factory methods instead of
constructors
The normal way for a class to allow a client to obtain an instance is to provide a public
constructor. There is another, less widely known technique that should also be a part of every
programmer's toolkit. A class can provide a public static factory method, which is simply
a static method that returns an instance of the class. Here's a simple example from the class
Boolean (the wrapper class for the primitive type boolean). This static factory method, which
was added in the 1.4 release, translates a
boolean primitive value into a Boolean object
reference:

public static Boolean valueOf(boolean b) {
return (b ? Boolean.TRUE : Boolean.FALSE);
}
A class can provide its clients with static factory methods instead of, or in addition to,
constructors. Providing a static factory method instead of a public constructor has both
advantages and disadvantages.
One advantage of static factory methods is that, unlike constructors, they have names.
If the parameters to a constructor do not, in and of themselves, describe the object being
returned, a static factory with a well-chosen name can make a class easier to use and the
resulting client code easier to read. For example, the constructor
BigInteger(int
,
int
,
Random)
, which returns a
BigInteger
that is probably prime, would have been better
expressed as a static factory method named
BigInteger.probablePrime
. (This static factory
method was eventually added in the 1.4 release.)
A class can have only a single constructor with a given signature. Programmers have been
known to get around this restriction by providing two constructors whose parameter lists
differ only in the order of their parameter types. This is a bad idea. The user of such an API
will never be able to remember which constructor is which and will end up calling the wrong
one by mistake. People reading code that uses these constructors will not know what the code
does without referring to the class documentation.
Because static factory methods have names, they do not share with constructors the restriction
that a class can have only one with a given signature. In cases where a class seems to require
multiple constructors with the same signature, you should consider replacing one or more
constructors with static factory methods whose carefully chosen names highlight their
differences.
A second advantage of static factory methods is that, unlike constructors, they are not
required to create a new object each time they're invoked.
This allows immutable classes
(Item 13) to use preconstructed instances or to cache instances as they're constructed and to
Effective Java: Programming Language Guide
9
dispense these instances repeatedly so as to avoid creating unnecessary duplicate objects.
The
Boolean.valueOf(boolean) method illustrates this technique: It never creates an object.
This technique can greatly improve performance if equivalent objects are requested
frequently, especially if these objects are expensive to create.
The ability of static factory methods to return the same object from repeated invocations can
also be used to maintain strict control over what instances exist at any given time. There are
two reasons to do this. First, it allows a class to guarantee that it is a singleton (Item 2).
Second, it allows an immutable class to ensure that no two equal instances exist:
a.equals(b)
if and only if
a==b
. If a class makes this guarantee, then its clients can use
the
==
operator instead of the
equals(Object)
method, which may result in a substantial
performance improvement. The typesafe enum pattern, described in Item 21, implements this
optimization, and the
String.intern method implements it in a limited form.
A third advantage of static factory methods is that, unlike constructors, they can return
an object of any subtype of their return type. This gives you great flexibility in choosing
the class of the returned object.
One application of this flexibility is that an API can return objects without making their
classes public. Hiding implementation classes in this fashion can lead to a very compact API.
This technique lends itself to interface-based frameworks, where interfaces provide natural
return types for static factory methods.
For example, the Collections Framework has twenty convenience implementations of its
collection interfaces, providing unmodifiable collections, synchronized collections, and the
like. The great majority of these implementations are exported via static factory methods in
a single, noninstantiable class (
java.util.Collections
). The classes of the returned objects
are all nonpublic.
The Collections Framework API is much smaller than it would be if it had exported twenty
separate public classes for the convenience implementations. It is not just the bulk of the API
that is reduced, but the “conceptual weight.” The user knows that the returned object has
precisely the API specified by the relevant interface, so there is no need to read additional
class documentation. Furthermore, using such a static factory method mandates that the client
refer to the returned object by its interface rather than by its implementation class, which is
generally a good practice (Item 34).
Not only can the class of an object returned by a public static factory method be nonpublic,
but the class can vary from invocation to invocation depending on the values of the
parameters to the static factory. Any class that is a subtype of the declared return type is
permissible. The class of the returned object can also vary from release to release, for
enhanced software maintainability.
The class of the object returned by a static factory method need not even exist at the time the
class containing the static factory method is written. Such flexible static factory methods form
the basis of service provider frameworks like the Java Cryptography Extension (JCE). A
service provider framework is a system wherein providers make multiple implementations of
an API available to users of the framework. A mechanism is provided to register these
implementations, making them available for use. Clients of the framework use the API
without worrying about which implementation they are using.
Effective Java: Programming Language Guide
10
In the JCE, the system administrator registers an implementation class by editing a well-
known
Properties file, adding an entry that maps a string key to the corresponding class
name. Clients use a static factory method that takes the key as a parameter. The static factory
method looks up the
Class
object in a map initialized from the
Properties
file and
instantiates the class using the
Class
.
newInstance
method. The following implementation
sketch illustrates this technique:

// Provider framework sketch
public abstract class Foo {
// Maps String key to corresponding Class object
private static Map implementations = null;

// Initializes implementations map the first time it's called
private static synchronized void initMapIfNecessary() {
if (implementations == null) {
implementations = new HashMap();

// Load implementation class names and keys from
// Properties file, translate names into Class
// objects using Class.forName and store mappings.

}

}

public static Foo getInstance(String key) {
initMapIfNecessary();
Class c = (Class) implementations.get(key);
if (c == null)
return new DefaultFoo();

try {
return (Foo) c.newInstance();
} catch (Exception e) {
return new DefaultFoo();
}
}
}
The main disadvantage of static factory methods is that classes without public or
protected constructors cannot be subclassed.
The same is true for nonpublic classes
returned by public static factories. For example, it is impossible to subclass any of the
convenience implementation classes in the Collections Framework. Arguably this can be a
blessing in disguise, as it encourages programmers to use composition instead of inheritance
(Item 14).
A second disadvantage of static factory methods is that they are not readily
distinguishable from other static methods. They do not stand out in API documentation in
the way that constructors do. Furthermore, static factory methods represent a deviation from
the norm. Thus it can be difficult to figure out from the class documentation how to instantiate
a class that provides static factory methods instead of constructors. This disadvantage can be
reduced by adhering to standard naming conventions. These conventions are still evolving,
but two names for static factory methods are becoming common:

Không có nhận xét nào:

Đăng nhận xét