2009 JavaOne PDF
2009 JavaOne PDF
Best Sellers
Sample Excerpts from 2009 JavaOne Best Selling Titles
informit.com/learnjava
Best Sellers
eBOOK TABLE OF CONTENTS
Google
Bookmarks Delicious Digg Facebook StumbleUpon Reddit Twitter
The authors and publisher have taken care in the preparation of this book, but make no expressed
or implied warranty of any kind and assume no responsibility for errors or omissions. No liability
is assumed for incidental or consequential damages in connection with or arising out of the use of
the information or programs contained herein.
BROUGHT TO YOU BY
UPPER SADDLE RIVER, NJ | BOSTON | INDIANAPOLIS | SAN FRANCISCO | NEW YORK | TORONTO | MONTREAL | LONDON | MUNICH
PARIS | MADRID | CAPETOWN | SYDNEY | TOKYO | SINGAPORE | MEXICO CITY
Are you looking for a deeper understanding of the Java™ programming language so
that you can write code that is clearer, more correct, more robust, and more reusable? AVAILABLE
Look no further! Effective Java™, Second Edition, brings together seventy-eight • BOOK: 9780321356680
indispensable programmer’s rules of thumb: working, best-practice solutions for the • SAFARI ONLINE
programming challenges you encounter every day. • EBOOK: 0132345285
This highly anticipated new edition of the classic, Jolt Award-winning work has been • KINDLE: 0137150059
thoroughly updated to cover Java SE 5 and Java SE 6 features introduced since the first
edition. Bloch explores new design patterns and language idioms, showing you how to
About the Author
make the most of features ranging from generics to enums, annotations to autoboxing.
Each chapter in the book consists of several “items” presented in the form of a short, Joshua Bloch is chief Java architect
standalone essay that provides specific advice, insight into Java platform subtleties, and at Google and a Jolt Award winner.
outstanding code examples. The comprehensive descriptions and explanations for each He was previously a distinguished
item illuminate what to do, what not to do, and why. engineer at Sun Microsystems
and a senior systems designer at
Highlights include:
Transarc. Bloch led the design and
• New coverage of generics, enums, annotations, autoboxing, the for-each loop, implementation of numerous Java
varargs, concurrency utilities, and much more platform features, including JDK
• Updated techniques and best practices on classic topics, including objects, classes, 5.0 language enhancements and
libraries, methods, and serialization the award-winning Java Collections
• How to avoid the traps and pitfalls of commonly misunderstood subtleties of the Framework. He coauthored Java™
language Puzzlers (Addison-Wesley, 2005)
• Focus on the language and its most fundamental libraries: java.lang, java.util, and, and Java™ Concurrency in Practice
to a lesser extent, java.util.concurrent and java.io (Addison-Wesley, 2006).
Simply put, Effective Java™, Second Edition, presents the most practical,
authoritative guidelines available for writing efficient, well-designed programs.
informit.com/aw
C H A P T E R 2
Creating and Destroying Objects
THIS chapter concerns creating and destroying objects: when and how to create
them, when and how to avoid creating them, how to ensure they are destroyed in a
timely manner, and how to manage any cleanup actions that must precede their
destruction.
The normal way for a class to allow a client to obtain an instance of itself is to pro-
vide a public constructor. There is another technique that should 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 exam-
ple from Boolean (the boxed primitive class for the primitive type boolean). This
method translates a boolean primitive value into a Boolean object reference:
Note that a static factory method is not the same as the Factory Method pattern
from Design Patterns [Gamma95, p. 107]. The static factory method described in
this item has no direct equivalent in Design Patterns.
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 is eas-
ier to use and the resulting client code easier to read. For example, the constructor
3
effective-java.book Page 6 Friday, May 23, 2008 1:27 PM
Interfaces can’t have static methods, so by convention, static factory methods for
an interface named Type are put in a noninstantiable class (Item 4) named Types.
For example, the Java Collections Framework has thirty-two convenience
implementations of its collection interfaces, providing unmodifiable collections,
synchronized collections, and the like. Nearly all of these implementations are
exported via static factory methods in one noninstantiable class (java.util.Col-
lections). The classes of the returned objects are all nonpublic.
The Collections Framework API is much smaller than it would have been had
it exported thirty-two separate public classes, one for each convenience imple-
mentation. 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
its interface, so there is no need to read additional class documentation for the
implementation classes. Furthermore, using such a static factory method requires
the client to refer to the returned object by its interface rather than its implementa-
tion class, which is generally good practice (Item 52).
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 and performance.
The class java.util.EnumSet (Item 32), introduced in release 1.5, has no
public constructors, only static factories. They return one of two implementations,
depending on the size of the underlying enum type: if it has sixty-four or fewer
elements, as most enum types do, the static factories return a RegularEnumSet
instance, which is backed by a single long; if the enum type has sixty-five or more
elements, the factories return a JumboEnumSet instance, backed by a long array.
The existence of these two implementation classes is invisible to clients. If
RegularEnumSet ceased to offer performance advantages for small enum types, it
could be eliminated from a future release with no ill effects. Similarly, a future
release could add a third or fourth implementation of EnumSet if it proved benefi-
cial for performance. Clients neither know nor care about the class of the object
they get back from the factory; they care only that it is some subclass of EnumSet.
The class of the object returned by a static factory method need not even exist
at the time the class containing the method is written. Such flexible static factory
methods form the basis of service provider frameworks, such as the Java Database
Connectivity API (JDBC). A service provider framework is a system in which
multiple service providers implement a service, and the system makes the imple-
mentations available to its clients, decoupling them from the implementations.
effective-java.book Page 8 Wednesday, April 23, 2008 4:18 AM
// Service interface
public interface Service {
... // Service-specific methods go here
}
A fourth advantage of static factory methods is that they reduce the ver-
bosity of creating parameterized type instances. Unfortunately, you must spec-
ify the type parameters when you invoke the constructor of a parameterized class
even if they’re obvious from context. This typically requires you to provide the
type parameters twice in quick succession:
Map<String, List<String>> m =
new HashMap<String, List<String>>();
This redundant specification quickly becomes painful as the length and complex-
ity of the type parameters increase. With static factories, however, the compiler
can figure out the type parameters for you. This is known as type inference. For
example, suppose that HashMap provided this generic static factory (Item 27):
Then you could replace the wordy declaration above with this succinct alternative:
Someday the language may perform this sort of type inference on constructor
invocations as well as method invocations, but as of release 1.6, it does not.
effective-java.book Page 10 Wednesday, April 23, 2008 4:18 AM
• valueOf—Returns an instance that has, loosely speaking, the same value as its
parameters. Such static factories are effectively type-conversion methods.
• of—A concise alternative to valueOf, popularized by EnumSet (Item 32).
• getInstance—Returns an instance that is described by the parameters but
cannot be said to have the same value. In the case of a singleton, getInstance
takes no parameters and returns the sole instance.
• newInstance—Like getInstance, except that newInstance guarantees that
each instance returned is distinct from all others.
• getType—Like getInstance, but used when the factory method is in a differ-
ent class. Type indicates the type of object returned by the factory method.
• newType—Like newInstance, but used when the factory method is in a differ-
ent class. Type indicates the type of object returned by the factory method.
In summary, static factory methods and public constructors both have their
uses, and it pays to understand their relative merits. Often static factories are pref-
erable, so avoid the reflex to provide public constructors without first considering
static factories.
effective-java.book Page 11 Wednesday, April 23, 2008 4:18 AM
Static factories and constructors share a limitation: they do not scale well to large
numbers of optional parameters. Consider the case of a class representing the
Nutrition Facts label that appears on packaged foods. These labels have a few
required fields—serving size, servings per container, and calories per serving—
and over twenty optional fields—total fat, saturated fat, trans fat, cholesterol,
sodium, and so on. Most products have nonzero values for only a few of these
optional fields.
What sort of constructors or static factories should you write for such a class?
Traditionally, programmers have used the telescoping constructor pattern, in
which you provide a constructor with only the required parameters, another with a
single optional parameter, a third with two optional parameters, and so on, culmi-
nating in a constructor with all the optional parameters. Here’s how it looks in
practice. For brevity’s sake, only four optional fields are shown:
When you want to create an instance, you use the constructor with the shortest
parameter list containing all the parameters you want to set:
NutritionFacts cocaCola =
new NutritionFacts(240, 8, 100, 0, 35, 27);
Typically this constructor invocation will require many parameters that you don’t
want to set, but you’re forced to pass a value for them anyway. In this case, we
passed a value of 0 for fat. With “only” six parameters this may not seem so bad,
but it quickly gets out of hand as the number of parameters increases.
In short, the telescoping constructor pattern works, but it is hard to write
client code when there are many parameters, and harder still to read it. The
reader is left wondering what all those values mean and must carefully count
parameters to find out. Long sequences of identically typed parameters can cause
subtle bugs. If the client accidentally reverses two such parameters, the compiler
won’t complain, but the program will misbehave at runtime (Item 40).
A second alternative when you are faced with many constructor parameters is
the JavaBeans pattern, in which you call a parameterless constructor to create the
object and then call setter methods to set each required parameter and each
optional parameter of interest:
public NutritionFacts() { }
effective-java.book Page 13 Wednesday, April 23, 2008 4:18 AM
// Setters
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories(int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
This pattern has none of the disadvantages of the telescoping constructor pattern.
It is easy, if a bit wordy, to create instances, and easy to read the resulting code:
// Builder Pattern
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
Note that NutritionFacts is immutable, and that all parameter default values
are in a single location. The builder’s setter methods return the builder itself so
that invocations can be chained. Here’s how the client code looks:
This client code is easy to write and, more importantly, to read. The Builder pat-
tern simulates named optional parameters as found in Ada and Python.
Like a constructor, a builder can impose invariants on its parameters. The
build method can check these invariants. It is critical that they be checked after
copying the parameters from the builder to the object, and that they be checked on
the object fields rather than the builder fields (Item 39). If any invariants are vio-
lated, the build method should throw an IllegalStateException (Item 60). The
exception’s detail message should indicate which invariant is violated (Item 63).
Another way to impose invariants involving multiple parameters is to have
setter methods take entire groups of parameters on which some invariant must
hold. If the invariant isn’t satisfied, the setter method throws an IllegalArgumen-
tException. This has the advantage of detecting the invariant failure as soon as
the invalid parameters are passed, instead of waiting for build to be invoked.
A minor advantage of builders over constructors is that builders can have mul-
tiple varargs parameters. Constructors, like methods, can have only one varargs
parameter. Because builders use separate methods to set each parameter, they can
have as many varargs parameters as you like, up to one per setter method.
The Builder pattern is flexible. A single builder can be used to build multiple
objects. The parameters of the builder can be tweaked between object creations to
vary the objects. The builder can fill in some fields automatically, such as a serial
number that automatically increases each time an object is created.
A builder whose parameters have been set makes a fine Abstract Factory
[Gamma95, p. 87]. In other words, a client can pass such a builder to a method to
enable the method to create one or more objects for the client. To enable this
usage, you need a type to represent the builder. If you are using release 1.5 or a
later release, a single generic type (Item 26) suffices for all builders, no matter
what type of object they’re building:
The traditional Abstract Factory implementation in Java has been the Class
object, with the newInstance method playing the part of the build method. This
usage is fraught with problems. The newInstance method always attempts to
invoke the class’s parameterless constructor, which may not even exist. You don’t
get a compile-time error if the class has no accessible parameterless constructor.
Instead, the client code must cope with InstantiationException or IllegalAc-
cessException at runtime, which is ugly and inconvenient. Also, the newIn-
stance method propagates any exceptions thrown by the parameterless
constructor, even though newInstance lacks the corresponding throws clauses. In
other words, Class.newInstance breaks compile-time exception checking. The
Builder interface, shown above, corrects these deficiencies.
The Builder pattern does have disadvantages of its own. In order to create an
object, you must first create its builder. While the cost of creating the builder is
unlikely to be noticeable in practice, it could be a problem in some performance-
critical situations. Also, the Builder pattern is more verbose than the telescoping
constructor pattern, so it should be used only if there are enough parameters, say,
four or more. But keep in mind that you may want to add parameters in the future.
If you start out with constructors or static factories, and add a builder when the
class evolves to the point where the number of parameters starts to get out of hand,
the obsolete constructors or static factories will stick out like a sore thumb. There-
fore, it’s often better to start with a builder in the first place.
In summary, the Builder pattern is a good choice when designing classes
whose constructors or static factories would have more than a handful of
parameters, especially if most of those parameters are optional. Client code is
much easier to read and write with builders than with the traditional telescoping
constructor pattern, and builders are much safer than JavaBeans.
effective-java.book Page 17 Wednesday, April 23, 2008 4:18 AM
ITEM 3: ENFORCE THE SINGLETON PROPERTY WITH A PRIVATE CONSTRUCTOR OR AN ENUM TYPE 15
The private constructor is called only once, to initialize the public static final field
Elvis.INSTANCE. The lack of a public or protected constructor guarantees a
“monoelvistic” universe: exactly one Elvis instance will exist once the Elvis
class is initialized—no more, no less. Nothing that a client does can change this,
with one caveat: a privileged client can invoke the private constructor reflectively
(Item 53) with the aid of the AccessibleObject.setAccessible method. If you
need to defend against this attack, modify the constructor to make it throw an
exception if it’s asked to create a second instance.
In the second approach to implementing singletons, the public member is a
static factory method:
All calls to Elvis.getInstance return the same object reference, and no other
Elvis instance will ever be created (with the same caveat mentioned above).
effective-java.book Page 18 Friday, May 23, 2008 2:00 PM
The main advantage of the public field approach is that the declarations make
it clear that the class is a singleton: the public static field is final, so it will always
contain the same object reference. There is no longer any performance advantage
to the public field approach: modern Java virtual machine (JVM) implementations
are almost certain to inline the call to the static factory method.
One advantage of the factory-method approach is that it gives you the flexibil-
ity to change your mind about whether the class should be a singleton without
changing its API. The factory method returns the sole instance but could easily be
modified to return, say, a unique instance for each thread that invokes it. A second
advantage, concerning generic types, is discussed in Item 27. Often neither of
these advantages is relevant, and the public field approach is simpler.
To make a singleton class that is implemented using either of the previous
approaches serializable (Chapter 11), it is not sufficient merely to add imple-
ments Serializable to its declaration. To maintain the singleton guarantee, you
have to declare all instance fields transient and provide a readResolve method
(Item 77). Otherwise, each time a serialized instance is deserialized, a new
instance will be created, leading, in the case of our example, to spurious Elvis
sightings. To prevent this, add this readResolve method to the Elvis class:
This approach is functionally equivalent to the public field approach, except that it
is more concise, provides the serialization machinery for free, and provides an
ironclad guarantee against multiple instantiation, even in the face of sophisticated
serialization or reflection attacks. While this approach has yet to be widely
adopted, a single-element enum type is the best way to implement a singleton.
effective-java.book Page 19 Wednesday, April 23, 2008 4:18 AM
Occasionally you’ll want to write a class that is just a grouping of static methods
and static fields. Such classes have acquired a bad reputation because some people
abuse them to avoid thinking in terms of objects, but they do have valid uses. They
can be used to group related methods on primitive values or arrays, in the manner
of java.lang.Math or java.util.Arrays. They can also be used to group static
methods, including factory methods (Item 1), for objects that implement a particu-
lar interface, in the manner of java.util.Collections. Lastly, they can be used
to group methods on a final class, instead of extending the class.
Such utility classes were not designed to be instantiated: an instance would be
nonsensical. In the absence of explicit constructors, however, the compiler pro-
vides a public, parameterless default constructor. To a user, this constructor is
indistinguishable from any other. It is not uncommon to see unintentionally
instantiable classes in published APIs.
Attempting to enforce noninstantiability by making a class abstract does
not work. The class can be subclassed and the subclass instantiated. Furthermore,
it misleads the user into thinking the class was designed for inheritance (Item 17).
There is, however, a simple idiom to ensure noninstantiability. A default construc-
tor is generated only if a class contains no explicit constructors, so a class can be
made noninstantiable by including a private constructor:
The statement creates a new String instance each time it is executed, and
none of those object creations is necessary. The argument to the String construc-
tor ("stringette") is itself a String instance, functionally identical to all of the
objects created by the constructor. If this usage occurs in a loop or in a frequently
invoked method, millions of String instances can be created needlessly.
The improved version is simply the following:
String s = "stringette";
This version uses a single String instance, rather than creating a new one
each time it is executed. Furthermore, it is guaranteed that the object will be
reused by any other code running in the same virtual machine that happens to con-
tain the same string literal [JLS, 3.10.5].
You can often avoid creating unnecessary objects by using static factory meth-
ods (Item 1) in preference to constructors on immutable classes that provide both.
For example, the static factory method Boolean.valueOf(String) is almost
always preferable to the constructor Boolean(String). The constructor creates a
new object each time it’s called, while the static factory method is never required
to do so and won’t in practice.
In addition to reusing immutable objects, you can also reuse mutable objects
if you know they won’t be modified. Here is a slightly more subtle, and much
more common, example of what not to do. It involves mutable Date objects that
are never modified once their values have been computed. This class models a
person and has an isBabyBoomer method that tells whether the person is a “baby
boomer,” in other words, whether the person was born between 1946 and 1964:
// DON'T DO THIS!
public boolean isBabyBoomer() {
// Unnecessary allocation of expensive object
Calendar gmtCal =
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
Date boomStart = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd = gmtCal.getTime();
return birthDate.compareTo(boomStart) >= 0 &&
birthDate.compareTo(boomEnd) < 0;
}
}
/**
* The starting and ending dates of the baby boom.
*/
private static final Date BOOM_START;
private static final Date BOOM_END;
static {
Calendar gmtCal =
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = gmtCal.getTime();
}
The improved version of the Person class creates Calendar, TimeZone, and
Date instances only once, when it is initialized, instead of creating them every
time isBabyBoomer is invoked. This results in significant performance gains if the
effective-java.book Page 22 Wednesday, April 23, 2008 4:18 AM
values. To do this, the program has to use long arithmetic, because an int is not
big enough to hold the sum of all the positive int values:
This program gets the right answer, but it is much slower than it should be,
due to a one-character typographical error. The variable sum is declared as a Long
instead of a long, which means that the program constructs about 231 unnecessary
Long instances (roughly one for each time the long i is added to the Long sum).
Changing the declaration of sum from Long to long reduces the runtime from 43
seconds to 6.8 seconds on my machine. The lesson is clear: prefer primitives to
boxed primitives, and watch out for unintentional autoboxing.
This item should not be misconstrued to imply that object creation is expen-
sive and should be avoided. On the contrary, the creation and reclamation of small
objects whose constructors do little explicit work is cheap, especially on modern
JVM implementations. Creating additional objects to enhance the clarity, simplic-
ity, or power of a program is generally a good thing.
Conversely, avoiding object creation by maintaining your own object pool is a
bad idea unless the objects in the pool are extremely heavyweight. The classic
example of an object that does justify an object pool is a database connection. The
cost of establishing the connection is sufficiently high that it makes sense to reuse
these objects. Also, your database license may limit you to a fixed number of con-
nections. Generally speaking, however, maintaining your own object pools clut-
ters your code, increases memory footprint, and harms performance. Modern
JVM implementations have highly optimized garbage collectors that easily out-
perform such object pools on lightweight objects.
The counterpoint to this item is Item 39 on defensive copying. Item 5 says,
“Don’t create a new object when you should reuse an existing one,” while Item 39
says, “Don’t reuse an existing object when you should create a new one.” Note
that the penalty for reusing an object when defensive copying is called for is far
greater than the penalty for needlessly creating a duplicate object. Failing to make
defensive copies where required can lead to insidious bugs and security holes; cre-
ating objects unnecessarily merely affects style and performance.
effective-java.book Page 24 Wednesday, April 23, 2008 4:18 AM
When you switch from a language with manual memory management, such as C
or C++, to a garbage-collected language, your job as a programmer is made much
easier by the fact that your objects are automatically reclaimed when you’re
through with them. It seems almost like magic when you first experience it. It can
easily lead to the impression that you don’t have to think about memory manage-
ment, but this isn’t quite true.
Consider the following simple stack implementation:
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
There’s nothing obviously wrong with this program (but see Item 26 for a
generic version). You could test it exhaustively, and it would pass every test with
flying colors, but there’s a problem lurking. Loosely speaking, the program has a
“memory leak,” which can silently manifest itself as reduced performance due to
effective-java.book Page 25 Wednesday, April 23, 2008 4:18 AM
An added benefit of nulling out obsolete references is that, if they are subse-
quently dereferenced by mistake, the program will immediately fail with a
NullPointerException, rather than quietly doing the wrong thing. It is always
beneficial to detect programming errors as quickly as possible.
When programmers are first stung by this problem, they may overcompensate
by nulling out every object reference as soon as the program is finished using it.
This is neither necessary nor desirable, as it clutters up the program unnecessarily.
Nulling out object references should be the exception rather than the norm.
The best way to eliminate an obsolete reference is to let the variable that contained
the reference fall out of scope. This occurs naturally if you define each variable in
the narrowest possible scope (Item 45).
effective-java.book Page 26 Wednesday, April 23, 2008 4:18 AM
So when should you null out a reference? What aspect of the Stack class
makes it susceptible to memory leaks? Simply put, it manages its own memory.
The storage pool consists of the elements of the elements array (the object refer-
ence cells, not the objects themselves). The elements in the active portion of the
array (as defined earlier) are allocated, and those in the remainder of the array are
free. The garbage collector has no way of knowing this; to the garbage collector,
all of the object references in the elements array are equally valid. Only the pro-
grammer knows that the inactive portion of the array is unimportant. The pro-
grammer effectively communicates this fact to the garbage collector by manually
nulling out array elements as soon as they become part of the inactive portion.
Generally speaking, whenever a class manages its own memory, the pro-
grammer should be alert for memory leaks. Whenever an element is freed, any
object references contained in the element should be nulled out.
Another common source of memory leaks is caches. Once you put an
object reference into a cache, it’s easy to forget that it’s there and leave it in the
cache long after it becomes irrelevant. There are several solutions to this problem.
If you’re lucky enough to implement a cache for which an entry is relevant exactly
so long as there are references to its key outside of the cache, represent the cache
as a WeakHashMap; entries will be removed automatically after they become obso-
lete. Remember that WeakHashMap is useful only if the desired lifetime of cache
entries is determined by external references to the key, not the value.
More commonly, the useful lifetime of a cache entry is less well defined, with
entries becoming less valuable over time. Under these circumstances, the cache
should occasionally be cleansed of entries that have fallen into disuse. This can be
done by a background thread (perhaps a Timer or ScheduledThreadPoolExecu-
tor) or as a side effect of adding new entries to the cache. The LinkedHashMap
class facilitates the latter approach with its removeEldestEntry method. For
more sophisticated caches, you may need to use java.lang.ref directly.
A third common source of memory leaks is listeners and other callbacks.
If you implement an API where clients register callbacks but don’t deregister them
explicitly, they will accumulate unless you take some action. The best way to
ensure that callbacks are garbage collected promptly is to store only weak refer-
ences to them, for instance, by storing them only as keys in a WeakHashMap.
Because memory leaks typically do not manifest themselves as obvious fail-
ures, they may remain present in a system for years. They are typically discovered
only as a result of careful code inspection or with the aid of a debugging tool
known as a heap profiler. Therefore, it is very desirable to learn to anticipate prob-
lems like this before they occur and prevent them from happening.
effective-java.book Page 27 Wednesday, April 23, 2008 4:18 AM
all. It is entirely possible, even likely, that a program terminates without executing
finalizers on some objects that are no longer reachable. As a consequence, you
should never depend on a finalizer to update critical persistent state. For
example, depending on a finalizer to release a persistent lock on a shared resource
such as a database is a good way to bring your entire distributed system to a
grinding halt.
Don’t be seduced by the methods System.gc and System.runFinalization.
They may increase the odds of finalizers getting executed, but they don’t guaran-
tee it. The only methods that claim to guarantee finalization are System.runFi-
nalizersOnExit and its evil twin, Runtime.runFinalizersOnExit. These
methods are fatally flawed and have been deprecated [ThreadStop].
In case you are not yet convinced that finalizers should be avoided, here’s
another tidbit worth considering: if an uncaught exception is thrown during final-
ization, the exception is ignored, and finalization of that object terminates [JLS,
12.6]. Uncaught exceptions can leave objects in a corrupt state. If another thread
attempts to use such a corrupted object, arbitrary nondeterministic behavior may
result. Normally, an uncaught exception will terminate the thread and print a stack
trace, but not if it occurs in a finalizer—it won’t even print a warning.
Oh, and one more thing: there is a severe performance penalty for using
finalizers. On my machine, the time to create and destroy a simple object is about
5.6 ns. Adding a finalizer increases the time to 2,400 ns. In other words, it is about
430 times slower to create and destroy objects with finalizers.
So what should you do instead of writing a finalizer for a class whose objects
encapsulate resources that require termination, such as files or threads? Just pro-
vide an explicit termination method, and require clients of the class to invoke this
method on each instance when it is no longer needed. One detail worth mention-
ing is that the instance must keep track of whether it has been terminated: the
explicit termination method must record in a private field that the object is no
longer valid, and other methods must check this field and throw an Illegal-
StateException if they are called after the object has been terminated.
Typical examples of explicit termination methods are the close methods on
InputStream, OutputStream, and java.sql.Connection. Another example is
the cancel method on java.util.Timer, which performs the necessary state
change to cause the thread associated with a Timer instance to terminate itself
gently. Examples from java.awt include Graphics.dispose and Window.dis-
pose. These methods are often overlooked, with predictably dire performance
consequences. A related method is Image.flush, which deallocates all the
effective-java.book Page 29 Wednesday, April 23, 2008 4:18 AM
resources associated with an Image instance but leaves it in a state where it can
still be used, reallocating the resources if necessary.
Explicit termination methods are typically used in combination with the
try-finally construct to ensure termination. Invoking the explicit termination
method inside the finally clause ensures that it will get executed even if an
exception is thrown while the object is being used:
So what, if anything, are finalizers good for? There are perhaps two legitimate
uses. One is to act as a “safety net” in case the owner of an object forgets to call its
explicit termination method. While there’s no guarantee that the finalizer will be
invoked promptly, it may be better to free the resource late than never, in those
(hopefully rare) cases when the client fails to call the explicit termination method.
But the finalizer should log a warning if it finds that the resource has not been
terminated, as this indicates a bug in the client code, which should be fixed. If
you are considering writing such a safety-net finalizer, think long and hard about
whether the extra protection is worth the extra cost.
The four classes cited as examples of the explicit termination method pattern
(FileInputStream, FileOutputStream, Timer, and Connection) have finalizers
that serve as safety nets in case their termination methods aren’t called. Unfortu-
nately these finalizers do not log warnings. Such warnings generally can’t be
added after an API is published, as it would appear to break existing clients.
A second legitimate use of finalizers concerns objects with native peers. A
native peer is a native object to which a normal object delegates via native meth-
ods. Because a native peer is not a normal object, the garbage collector doesn’t
know about it and can’t reclaim it when its Java peer is reclaimed. A finalizer is an
appropriate vehicle for performing this task, assuming the native peer holds no
critical resources. If the native peer holds resources that must be terminated
promptly, the class should have an explicit termination method, as described
above. The termination method should do whatever is required to free the critical
resource. The termination method can be a native method, or it can invoke one.
effective-java.book Page 30 Wednesday, April 23, 2008 4:18 AM
Note that the public class, Foo, has no finalizer (other than the trivial one it
inherits from Object), so it doesn’t matter whether a subclass finalizer calls
super.finalize or not. This technique should be considered for every nonfinal
public class that has a finalizer.
In summary, don’t use finalizers except as a safety net or to terminate
noncritical native resources. In those rare instances where you do use a finalizer,
remember to invoke super.finalize. If you use a finalizer as a safety net,
remember to log the invalid usage from the finalizer. Lastly, if you need to
associate a finalizer with a public, nonfinal class, consider using a finalizer
guardian, so finalization can take place even if a subclass finalizer fails to invoke
super.finalize.
Google
Bookmarks Delicious Digg Facebook StumbleUpon Reddit Twitter
Essential JavaFx™
JavaFX is a rich-client platform for building applications and expressive content for the
desktop, browsers, and mobile devices. JavaFX applications are written with JavaFX AVAILABLE
Script, a simple, easy-to-learn declarative scripting language. Created to make it easy • BOOK: 9780137042791
for web developers and GUI designers to collaborate, JavaFX Script also includes a • SAFARI ONLINE
powerful data binding feature that allows graphical components to automatically change • EBOOK: 0137044550
state when underlying data is changed. • KINDLE: 0137044313
Essential JavaFX™ clearly explains everything you need to know to write JavaFX
scripts that leverage the language’s unique features for creating rich content. Starting About the Authors
with the fundamentals, this practical tutorial introduces the JavaFX scene graph and Gail Anderson is a software
covers the most effective uses of binding, event handlers, and animation, as well as specialist and author who has written
how to use mixin inheritance. Each chapter includes one or more JavaFX application numerous books on leading-edge
examples. Java technologies. Gail is a founding
member of the Anderson Software
Key topics covered include: Group, Inc., a leading provider
of software development training
• Shapes, Paths, and Path Elements courses.
• Layout Nodes and Bounding Rectangles
• JavaFX UI Controls and JavaFX Swing Components Paul Anderson is a founding
• Custom Skinnable Components member of the Anderson Software
• Images and ImageViewers Group, Inc., and a leading trainer in
• Using Web Services software technologies, such as Java,
• Targeting the Mobile Environment C++, C#, Perl, UML, and Linux. Paul
has taught courses for thousands
of developers and specializes in
making software engineering fun and
understandable.
informit.com/ph
2 A Taste of JavaFX
As the preface hints, JavaFX has a combination of features that makes it unique. This
chapter gives you a taste of the language and some of these features. Our goal is to
choose a representative example so you get a feel for the kinds of programs possible
with JavaFX. The example (a guitar tuner) illustrates language constructs while keep-
ing the discussion concrete. We’ll veer away from the example at times to illustrate
additional JavaFX features that are relevant. While this overview is in no way com-
plete (remember, it’s just a taste), we hope to entice you to explore JavaFX further.
The source code for GuitarTuner appears at the end of the chapter (see “Source Code
for Project GuitarTuner” on page 36). To keep the text flowing, we’ll show snippets
from this application throughout the overview.
13
JavaFX.book Page 14 Monday, May 18, 2009 12:09 PM
the system figures out how to do it for you.) JavaFX provides properties for manipu-
lating objects within a 2D coordinate system, specifying fill and pen stroke colors, and
creating special effects. You can create shapes and lines, manipulate images, play vid-
eos and sounds, and define animations.
Let’s begin exploring JavaFX by introducing the basics. Our introduction begins with
project GuitarTuner where you’ll see the main structure of a JavaFX program. Then,
you’ll explore a few JavaFX language constructs and see how to improve the appear-
ance of your applications. Finally, you’ll see how to make applications do things.
JavaFX in a Nutshell
JavaFX is statically typed, meaning program data types are known at compile time. JavaFX
also uses type inference. This means you don’t have to declare the type of every variable
because JavaFX can generally figure it out for you. This gives JavaFX the efficiency of a stati-
cally typed language combined with the ease of a declarative language.
GuitarString (6)
Project GuitarTuner 15
Figure 2.2 shows the scene graph for project GuitarTuner. Compare the visual graphi-
cal elements in Figure 2.1 with the scene graph depicted in Figure 2.2.
Scene
GuitarString
Group (CustomNode)
Rectangle Group
fret board
Rectangle
Line
(mouse detection)
frets
Line Rectangle
(normal)
GuitarString E string Rectangle
GuitarString (vibrating)
A string
. Text
. (note display)
.
GuitarString E string
In general, to construct a JavaFX application, you build the scene graph, specifying the
look and behavior of all its nodes. Then, your application just “runs.” Some applica-
tions need input to go—user actions that activate animations or affect component
JavaFX.book Page 16 Monday, May 18, 2009 12:09 PM
properties. Other applications just run on their own. (Building the scene graph is anal-
ogous to winding up a toy. When you’re done, the application just runs.)
The power of the scene graph is that, not only do you capture the entire structure of your appli-
cation in a data structure, but you can change the display simply by modifying properties of
the objects in the scene graph. (For example, if you change a node’s visible property to false,
that node, and any nodes it contains, disappears. If you change a node’s location, it moves.)
Within the scene graph for project GuitarTuner, you see the Scene at the top level,
which contains a Group. Within the Group there is a Rectangle for the fret board (the
guitar neck), two Line nodes representing frets, and six GuitarStrings. Each Guitar-
String is in turn its own Group consisting of three Rectangles and a Text node. Nodes
that contain other nodes (such as Scene and Group) include a content property that
holds subnodes. The hierarchical nature of the scene graph means that all nodes at the
same level share the same coordinate space. You therefore build node structures (such
as GuitarString) that use a relative coordinate system. You’ll see shortly why this is
useful.
JavaFX encourages you to think like a designer. As a first step, visualize the structure of your
application or widget and compose your scene out of simple shapes and other building blocks.
The order of nodes within a parent container affects their rendering. That is, the first
node in the container is “drawn” first. The final node is “drawn” last and is on top of
the view. Nodes (depending on their placement within the coordinate system) may
visually block or “clip” previously drawn nodes. In GuitarTuner, the nodes must be in
a specific order. You draw the fret board first, then the frets, and finally the guitar
strings, which appear on top.
Changing the relative order of nodes in a container is easy. The toFront() function
brings a node to the front (top) and the toBack() function sends a node to the back
(bottom).
Scene
Group
GuitarString (CustomNode)
Group
(mouse
detection) (normal) (vibrating) (note display)
Let’s see how the Stage and Scene form the JavaFX program structure.
Here is a top-level implementation of the scene graph for GuitarTuner from Figure 2.2
(or Figure 2.3). (We’ll look at GuitarString’s node graph shortly.)
JavaFX.book Page 18 Monday, May 18, 2009 12:09 PM
Object Literals
The Stage and Scene objects are instantiated with object literal expressions, or object
literals. Object literals provide a declarative style of programming. Intuitively, declara-
tive means “tell me what you want, not how to do it.” As you will see, the real declar-
ative part of JavaFX is binding. We show why this is so powerful later in the chapter.
Object literals require an object (or class) type (such as Stage or Scene) followed by
curly braces { }. Any properties you need to initialize appear inside the braces. (Stage
has a title property and Scene and Group both have content properties.) Each prop-
erty has a name, followed by a colon : and an initial value for the property. You sep-
arate properties with commas, line breaks, or white space. Here, for example is an
object literal that initializes a Rectangle (properties x and y designate the upper-left
corner origin).
Rectangle { x: 10, y: 20, height: 15, width: 150 }
The above Stage, Scene, and Group objects are defined with object literals. Note that
the Scene object nests inside the Stage object. Likewise, a Group nests inside the
Scene. Square brackets [ ] define a sequence of items for the content property in a
Scene or Group. Here, the Scene object’s content property is a sequence of all of the
top-level nodes of the Scene. In the GuitarTuner application, this is a Group node (see
Figure 2.2 or Figure 2.3). The Group node likewise includes a content property with
all of its subnodes (Rectangles, Lines, and a custom GuitarString). How you nest these
nodes determines the structure of the scene graph.
JavaFX.book Page 19 Monday, May 18, 2009 12:09 PM
Here’s the top-level implementation for GuitarString from its scene graph in
Figure 2.2 (and Figure 2.3).
// GuitarString - defined as custom class
Group {
content: [
Rectangle { ... }
Rectangle { ... }
Rectangle { ... }
Text { ... }
]
} // Group
The GuitarString consists of a Group node whose content property defines a sequence
containing three rectangles and a Text object. You’ll see how this fits into the Guitar-
Tuner application later on.
Included in any list of key JavaFX features are binding, node event handlers, and animation.
We discuss each of these important constructs in their own section (see “Doing Things” on
page 31).
Type Inference
JavaFX provides def for read-only variables and var for modifiable variables.
def numberFrets = 2; // read-only Integer
var x = 27.5; // variable Number
var y: Number; // default value is 0.0
var s: String; // default value is ""
The compiler infers types from the values you assign to variables. Read-only number-
Frets has inferred type Integer; variable x has inferred type Number (Float). This
means you don’t have to specify types everywhere (and the compiler tells you when a
type is required.)
JavaFX.book Page 20 Monday, May 18, 2009 12:09 PM
Strings
JavaFX supports dynamic string building. Curly braces { } within a String expression
evaluate to the contents of the enclosed variable. You can build Strings by concatenat-
ing these String expressions and String literals. For example, the following snippet
prints "Greetings, John Doe!".
def s1 = "John Doe";
println("Greetings, {s1}!"); // Greetings, John Doe!
Shapes
JavaFX has numerous shapes that help you create scene graph nodes. There are
shapes for creating lines (Line, CubicCurve, QuadCurve, PolyLine, Path) and shapes
for creating geometric figures (Arc, Circle, Ellipse, Rectangle, Polygon). The Guitar-
Tuner application uses only Rectangle and Line, but you’ll see other shape examples
throughout this book.
Let’s look at shapes Rectangle and Circle. They are both standard JavaFX shapes that
extend class Shape (in package javafx.scene.shape). You define a Circle by specifying
values for its radius, centerX, and centerY properties. With Rectangle, you specify val-
ues for properties height, width, x, and y.
Shapes share several properties in common, including properties fill (type Paint to
fill the interior of the shape), stroke (type Paint to provide the outline of the shape),
and strokeWidth (an Integer for the width of the outline).
Here, for example, is a Circle with its center at point (50,50), radius 30, and color
Color.RED.
Circle {
radius: 30
centerX: 50
centerY: 50
fill: Color.RED
}
Here is a Rectangle with its top left corner at point (30, 100), height 30, width 80, and
color Color.BLUE.
Rectangle {
x: 30, y: 100
height: 30, width: 80
fill: Color.BLUE
}
All shapes are also Nodes (javafx.scene.Node). Node is an all-important class that
provides local geometry for node elements, properties to specify transformations
JavaFX.book Page 21 Monday, May 18, 2009 12:09 PM
Sequences
Sequences let you define a collection of objects that you can access sequentially. You
must declare the type of object a sequence will hold or provide values so that its type
can be inferred. For example, the following statements define sequence variables of
GuitarString and Rectangle objects.
var guitarStrings: GuitarString[];
var rectangleSequence: Rectangle[];
These statements create read-only sequences with def. Here, sequence noteValues has
an inferred type of Integer[]; sequence guitarNotes has an inferred type of String[].
def noteValues = [ 40,45,50,55,59,64 ];
def guitarNotes = [ "E","A","D","G","B","E" ];
Sequences have specialized operators and syntax. You will use sequences in JavaFX
whenever you need to keep track of multiple items of the same object type. The
GuitarTuner application uses a sequence with a for loop to build multiple Line objects
(the frets) and GuitarString objects.
// Build Frets
for (i in [0..<numberFrets])
Line { . . . }
// Build Strings
for (i in [0..<numberStrings])
GuitarString { . . . }
The notation [0..<n] is a sequence literal and defines a range of numbers from 0 to
n-1, inclusive.
You can declare and populate sequences easily. The following declarative approach
inserts Rectangles into a sequence called rectangleSequence, stacking six Rectangles
vertically.
def rectangleSequence = for (i in [0..5])
Rectangle {
1. Cascading Style Sheets (CSS) help style web pages and let designers give a uniform look and
feel throughout an application, widget, or entire web site. You can use CSS to similarly style
JavaFX nodes. (See “Cascading Style Sheets (CSS)” on page 148 for details on applying styles
to JavaFX nodes.)
JavaFX.book Page 22 Monday, May 18, 2009 12:09 PM
x: 20
y: i * 30
height: 20
width: 40
}
You can also insert number values or objects into an existing sequence using the
insert operator. The following imperative approach inserts the six Rectangles into a
sequence called varRectangleSequence.
var varRectangleSequence: Rectangle[];
for (i in [0..5])
insert Rectangle {
x: 20
y: i * 30
height: 20
width: 40
} into varRectangleSequence;
JavaFX Tip
The declarative approach with rectangleSequence is always preferred (if possible). By using
def rather than var and declaring sequences rather than inserting objects into them, type
inference will more likely help you and the compiler can optimize the code more effectively.
Extending CustomNode
JavaFX offers developers such object-oriented features as user-defined classes, over-
riding virtual functions, and abstract base classes (there is also “mixin” inheritance).
JavaFX.book Page 23 Monday, May 18, 2009 12:09 PM
Extensible scene
CustomNode graph node
Encapsulates
GuitarString graphical structure
and behavior of
GuitarString node
This approach lets you build your own graphical objects. In order for a custom object
to fit seamlessly into a JavaFX scene graph, you base its behavior on a special class
provided by JavaFX, CustomNode. Class CustomNode is a scene graph node (a type of
Node, discussed earlier) that lets you specify new classes that extend from it. Just like
Java, “extends” is the JavaFX language construct that creates an inheritance relation-
ship. Here, GuitarString extends (inherits from) CustomNode. You then supply the
additional structure and behavior you need for GuitarString objects and override any
functions required by CustomNode. JavaFX class constructs are discussed in more
detail in Chapter 3 (see “Classes and Objects” on page 67).
Here is some of the code from GuitarTuner's GuitarString class. The create function
returns a Node defining the Group scene graph for GuitarString. (This scene graph
matches the node structure in Figure 2.2 on page 15 and Figure 2.3 on page 17.
Listing 2.2 on page 38 shows the create function in more detail.)
public class GuitarString extends CustomNode {
// properties, variables, functions
. . .
protected override function create(): Node {
return Group {
content: [
Rectangle { ... }
Rectangle { ... }
Rectangle { ... }
Text { ... }
]
} // Group
}
} // GuitarString
JavaFX.book Page 24 Monday, May 18, 2009 12:09 PM
Geometry System
In JavaFX, nodes are positioned on a two-dimensional coordinate system with the ori-
gin at the upper-left corner. Values for x increase horizontally from left to right and y
values increase vertically from top to bottom. The coordinate system is always relative
to the parent container.
Layout/Groups
Layout components specify how you want objects drawn relative to other objects. For
example, layout component HBox (horizontal box) evenly spaces its subnodes in a
single row. Layout component VBox (vertical box) evenly spaces its subnodes in a sin-
gle column. Other layout choices are Flow, Tile, and Stack (see “Layout Components”
on page 119). You can nest layout components as needed.
Grouping nodes into a single entity makes it straightforward to control event han-
dling, animation, group-level properties, and layout for the group as a whole. Each
group (or layout node) defines a coordinate system that is used by all of its children.
In GuitarTuner, the top level node in the scene graph is a Group which is centered ver-
tically within the scene. The subnodes are all drawn relative to the origin (0,0) within
the top-level Group. Centering the Group, therefore, centers its contents as a whole.
Nodes with the same parent share the same relative coordinate space. This keeps any coordinate
space calculations for subnodes separate from layout issues of the parent container. Then, when
you move the parent, everything under it moves, keeping relative positions intact.
Since JavaFX is statically typed, you must use either import statements or declare all
types that are not built-in. You’ll typically define a package and then specify import
statements. (We discuss working with packages in Chapter 3. See “Script Files and
Packages” on page 86.) Here is the package declaration and import statements for
GuitarTuner.
package guitartuner;
import javafx.scene.effect.DropShadow;
JavaFX.book Page 25 Monday, May 18, 2009 12:09 PM
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.stage.Stage;
import noteplayer.SingleNote;
If you’re using NetBeans, the IDE can generate import statements for you (type
Ctrl+Shift+I in the editor window).
You’ll need script-level variables to store data and read-only variables (def ) for values
that don’t change. In GuitarTuner, we define several read-only variables that help
build the guitar strings and a variable (singleNote) that communicates with the Java
midi API. Note that noteValues and guitarNotes are def sequence types.
def noteValues = [ 40,45,50,55,59,64 ];
def guitarNotes = [ "E","A","D","G","B","E" ];
def numberFrets = 2;
def numberStrings = 6;
var singleNote = SingleNote { };
When you declare a Stage, you define the nested nodes in the scene graph. Instead of
declaring nodes only as object literal expressions, it’s also possible to assign these
object literals to variables. This lets you refer to them later in your code. (For example,
the Scene object literal and the Group object literal are assigned to variables in order
to compute the offset for centering the group vertically in the scene.)
var scene: Scene;
var group: Group;
You may also need to execute JavaFX script statements or define utility functions.
Here’s how GuitarTuner makes the SingleNote object emit a “guitar” sound.
singleNote.setInstrument(27); // "Clean Guitar"
Once you set up the Stage and scene graph for an application, it’s ready to ready to
run.2 In GuitarTuner, the application waits for the user to pluck (click) a guitar string.
2. Java developers may wonder where function main() is. As it turns out, the JavaFX compiler
generates a main() for you, but from a developer’s view, you have just a script file.
JavaFX.book Page 26 Monday, May 18, 2009 12:09 PM
Gradients
Gradients lend a depth to surfaces and backgrounds by gradually varying the color of
the object’s fill property. In general, use linear gradients with rectangular shapes and
radial gradients with circles and ovals. In GuitarTuner, the background is a linear gra-
dient that transitions from Color.LIGHTGRAY (at the top) to the darker Color.GRAY (at
the bottom) as shown in Figure 2.5. The guitar fret board also uses a linear gradient.
Fret Board
Linear
Gradient
Background
Linear
Gradient
Here is the LinearGradient for the background scene in GuitarTuner, defined for
property fill. Note that specifying gradients is declarative; you identify the look you
want and the system figures out how to achieve it, independent of screen resolution,
color depth, etc.
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.LIGHTGRAY
},
Stop {
offset: 1.0
JavaFX.book Page 27 Monday, May 18, 2009 12:09 PM
color: Color.GRAY
}
]
}
The background gradient changes color along the y axis and the color is constant
along the x axis (properties startX and endX are the same). Property stops is a
sequence of Stop objects containing an offset and a color. The offset is a value
between 0 and 1 inclusive; each succeeding offset must have a higher value than the
preceding one.
Property proportional indicates whether start and end values are proportional
(defined between [0..1] if true) or absolute (absolute coordinates if false).
Radial gradients work well for circular shapes, as shown in Figure 2.6. Here you see
three Circle shapes, all with radial gradients. The first circle defines a gradient with its
center in the lower left quadrant (centerX is 0.25 and centerY is 0.75). The second cir-
cle’s gradient is centered (centerX and centerY are both 0.5), and the third circle’s gra-
dient appears in the upper right quadrant (centerX is 0.75 and centerY is 0.25).
fill: RadialGradient {
centerX: 0.5 // x center of gradient
centerY: 0.5 // y center of gradient
radius: 0.5 // radius of gradient
stops: [
Stop {
offset: 0
color: Color.WHITE
},
Stop {
offset: 1
JavaFX.book Page 28 Monday, May 18, 2009 12:09 PM
color: Color.DODGERBLUE
}
]
}
Note that the gradient is half the size of the circle (radius is 0.5). Making the gradient
less than the full size lets the last stop color appear more prominent (the dark color
predominates).
Color
You specify a shape’s color with property fill. JavaFX has many predefined colors
ranging alphabetically from Color.ALICEBLUE to Color.YELLOWGREEN. (In the NetBeans
IDE, press Ctrl+Space when the cursor is after the dot in Color to see a complete list,
as shown in Figure 2.7.)
You can also specify arbitrary colors with Color.rgb (each RGB value ranges from 0 to
255), Color.color (each RGB value ranges from 0 to 1), and Color.web (a String corre-
sponding to the traditional hexadecimal-based triad). An optional final argument sets
the opacity, where 1 is fully opaque and 0 is fully translucent. You can also make a
shape transparent by setting its fill property to Color.TRANSPARENT.
Here are several examples of color settings. Each example sets the opacity to .5, which
allows some of the background color to show through.
def c1 = Color.rgb(10, 255, 15, .5); // bright lime green
def c2 = Color.color(0.5, 0.1, 0.1, .5); // dark red
def c3 = Color.web("#546270", .5); // dark blue-gray
JavaFX.book Page 29 Monday, May 18, 2009 12:09 PM
Numeric-based color values (rather than hexadecimal strings or predefined colors) let
you write functions and animations that numerically manipulate gradients, colors, or
opacity. For example, the following fill property gets its Color.rgb values from a for
loop’s changing value i. The loop produces three different shades of green, depending
on the value of i.
def rectangleSequence = for (i in [0..2])
Rectangle {
x: 60 * i
y: 50
height: 50
width: 40
fill: Color.rgb(10 + (i*50), 100 + (i*40), i*50)
}
Figure 2.8 shows the resulting set of rectangles with different fill values.
Rectangle {
x: 180
y: 0
height: 70
width: 60
arcHeight: 30
arcWidth: 30
fill: LinearGradient { . . . }
}
JavaFX.book Page 30 Monday, May 18, 2009 12:09 PM
DropShadows
One of the many effects you can specify is DropShadow (effects are declarative).
Effect DropShadow applies a shadow to its node, giving the node a three-dimensional
look. In project GuitarTuner, the fret board (guitar neck) uses a default drop shadow,
as follows.
effect: DropShadow { }
The default object literal provides a drop shadow with these values.
effect: DropShadow {
offsetX: 0.0
offsetY: 0.0
radius: 10.0
color: Color.BLACK
spread: 0.0
}
You can manipulate the location of the shadow by changing offsetX and offsetY.
Negative values for offsetY set the shadow above the object and negative values for
offsetX set the shadow to the left. Positive values for offsetX and offsetY place the
shadow to the right and below, respectively. You can also change a shadow’s size
(radius), color, and spread (how “sharp” the shadow appears). A spread value of 1
means the shadow is sharply delineated. A value of 0 provides a “fuzzy” appearance.
Figure 2.10 shows three rectangles with drop shadows that fall below and to the right
of the rectangles, using these offsets.
effect: DropShadow {
// shadow appears below and to the right of object
offsetX: 5.0
offsetY: 5.0
}
JavaFX.book Page 31 Monday, May 18, 2009 12:09 PM
Doing Things 31
Binding
Binding in JavaFX is a powerful technique and a concise alternative to specifying tra-
ditional callback event handlers. Basically, binding lets you make a property or vari-
able depend on the value of an expression. When you update any of the “bound to”
objects in the expression, the dependent object automatically changes. Suppose, for
example, we bind area to height and width, as follows.
var height = 3.0;
var width = 4.0;
def area = bind height * width; // area = 12.0
When either height or width changes, so does area. Once you bind a property (or vari-
able), you can’t update it directly. For example, you get a compile-time error if you try
to directly update area.
area = 5; // compile time error
If you make area a var and provide a binding expression, you’ll get a runtime error if
you try to update it directly.
JavaFX.book Page 32 Monday, May 18, 2009 12:09 PM
In GuitarTuner, the vibrating string changes both its location (property translateY)
and its thickness (property height) at run time to give the appearance of vibration.
These properties are bound to other values that control how a guitar string node
changes.
var vibrateH: Number;
var vibrateY: Number;
Rectangle {
x: 0.0
y: yOffset
width: stringLength
height: bind vibrateH // change height when vibrateH changes
fill: stringColor
visible: false
translateY: bind vibrateY // change translateY when vibrateY changes
}
GuitarTuner also uses bind to keep the fret board centered vertically by binding prop-
erty layoutY in the top level group.
group = Group {
layoutY: bind (scene.height - group.layoutBounds.height) /
2 - group.layoutBounds.minY
. . .
}
Node property layoutBounds provides bounds information for its contents. If a user
resizes the window, the top level group is automatically centered vertically on the
screen. Binding helps reduce event processing code because (here, for example) you
don’t have to write an event handler to detect a change in the window size.
Binding is Good
Binding is good for many things. For example, you can change the appearance of a node based
on changes to the program’s state. You can make a component visible or hidden. You can also
use binding to declaratively specify layout constraints. Not only does binding produce less
code, but the code is less error-prone, easier to maintain, and often easier for the compiler to
optimize.
Mouse Events
JavaFX nodes have properties for handling mouse and key events. These properties
are set to callback functions that the system invokes when an event triggers. In Guitar-
Tuner, the “mouse detection” rectangle has the following event handler to detect a
mouse click event.
JavaFX.book Page 33 Monday, May 18, 2009 12:09 PM
Doing Things 33
The if statement checks for a click of the primary mouse button (generally the left
mouse button is primary) before processing the event. The event handler function
(shown in the next section) plays the note and vibrates the string.
Animations
JavaFX specializes in animations. (In fact, we dedicate an entire chapter to animation.
See Chapter 7 beginning on page 205.) You define animations with timelines and then
invoke Timeline functions play or playFromStart (there are also functions pause and
stop). Timelines consist of a sequence of key frame objects that define a frame at a spe-
cific time offset within the timeline. (Key frames are declarative. You say “this is the
state of the scene at this key time” and let the system figure out how to render the
affected objects.) Within each key frame, you specify values, an action, or both. Tradi-
tionally, people think of animations as a way to move objects. While this is true, you’ll
see that JavaFX lets you animate any writable object property. You could, for instance,
use animation to fade, rotate, resize, or even brighten an image.
Figure 2.11 shows a snapshot of a program with simple animation. It moves a circle
back and forth across its container.
Here is the timeline that implements this animation using a specialized shorthand
notation for KeyFrames. The timeline starts out by setting variable x to 0. In gradual,
linear increments, it changes x so that at four seconds, its value is 350. Now, it per-
forms the action in reverse, gradually changing x so that in four more seconds it is
back to 0 (autoReverse is true). This action is repeated indefinitely (or until the time-
line is stopped or paused). Constants 0s and 4s are Duration literals.
JavaFX.book Page 34 Monday, May 18, 2009 12:09 PM
var x: Number;
Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) { x => 0.0 }
at (4s) { x => 350 tween Interpolator.LINEAR }
]
}.play(); // start Timeline
. . .
Circle {
. . .
translateX: bind x
}
The JavaFX keyword tween is a key frame operator that lets you specify how a variable
changes. Here, we use Interpolator.LINEAR for a linear change. That is, x doesn’t
jump from 0 to 350, but gradually takes on values in a linear fashion. Linear interpola-
tion moves the Circle smoothly from 0 to 350, taking four seconds to complete one
iteration of the timeline.
JavaFX has other interpolators. Interpolator DISCRETE jumps from the value of one key
frame to the second. Interpolator EASEIN is similar to LINEAR, except the rate of change
is slower at the onset. Similarly, EASEOUT is slower at the finish and EASEBOTH provides
easing on both ends of the timeline.
To make this animation apply to the Circle node, you bind the Circle’s translateX
property to the variable manipulated by the timeline (x). Property translateX repre-
sents a node’s change in the x direction.
Now let’s examine how GuitarTuner uses animation to vibrate the guitar string and
play its note. Each GuitarString object uses two rectangles to implement its visible
behavior. One rectangle is a stationary, thin “string” and represents the string in a
static state. This motionless rectangle is always visible in the scene. The second rectan-
gle is only visible when the string is “played.” This rectangle expands and contracts
its height quickly using animation (a Timeline). This moving rectangle gives users the
illusion of a vibrating string.
To get a uniform vibrating effect, the rectangle must expand and contract evenly on
the top and bottom. The animation makes the string appear to vibrate by varying the
height of the rectangle from 1 to 3 while keeping it vertically centered by varying its
translateY property between 5 and 4. When the string is clicked, the string’s note
plays and the rectangle vibrates for the allotted time. When the timeline stops, only
the stationary rectangle is visible.
JavaFX.book Page 35 Monday, May 18, 2009 12:09 PM
Doing Things 35
Let’s first look at the timeline that plays the note. This timeline appears in the event
handler for the GuitarString node (see the code for GuitarString in Listing 2.2 on
page 38).
onMouseClicked: function(evt: MouseEvent): Void {
if (evt.button == MouseButton.PRIMARY) {
Timeline {
keyFrames: [
KeyFrame {
time: 0s
action: playNote // play note and start vibration
}
KeyFrame {
time: 2.8s
action: stopNote // stop playing note and stop vibration
}
]
}.play(); // start Timeline
}
}
Here, the timeline is an object literal defined inside the event handler, invoked with
function play. This timeline defines a sequence of KeyFrame objects, where function
playNote is invoked at time offset 0 seconds and function stopNote is invoked at time
offset 2.8 seconds (2.8s). Here are functions playNote and stopNote.
// play note and start vibration
function playNote(): Void {
synthNote.noteOn(note);
vibrateOn();
}
Function synthNote.noteOn calls a Java class API to play the guitar string. Function
vibrateOn causes the string vibration.
function vibrateOn(): Void {
play.visible = true; // make the vibrating rectangle visible
timeline.play(); // start the vibration timeline
}
This timeline uses the shorthand notation discussed earlier for key frames and ani-
mates two variables: vibrateH and vibrateY. Variable vibrateH changes the height of
the rectangle that represents the vibrating string. Variable vibrateY changes the verti-
cal position of the rectangle to keep it centered as the oscillating height changes.
Use def for read-only variables and var for modifiable variables. The GuitarString
class also provides utility functions that play a note (playNote) or stop playing a note
(stopNote). Along with the sound, guitar strings vibrate on and off with vibrateOn and
vibrateOff. These functions implement the behavior of the GuitarString class.
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.Cursor;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import noteplayer.SingleNote;
// read-only variables
def stringColor = Color.WHITESMOKE;
// "Strings" are oriented sideways, so stringLength is the
// Rectangle width and stringSize is the Rectangle height
def stringLength = 300;
def stringSize = 1;
def stringMouseSize = 15;
def timeline = Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) { vibrateH => 1.0 }
at (.01s) { vibrateH => 3.0 tween Interpolator.LINEAR }
at (0s) { vibrateY => 5.0 }
at (.01s) { vibrateY => 4.0 tween Interpolator.LINEAR }
]
};
// properties to be initialized
public-init var synthNote: SingleNote;
public-init var note: Integer;
public-init var yOffset: Number;
public-init var noteText: String;
// class variables
var vibrateH: Number;
var vibrateY: Number;
var play: Rectangle;
Listing 2.2 shows the second part of the code for the GuitarString class.
Every class that extends CustomNode must define a function create that returns a
Node object.3 Often the node you return will be a Group, since Group is the most gen-
eral Node type and can include subnodes. But, you can return other Node types, such
as Rectangle (Shape) or HBox (horizontal box) layout node.
The scene graph for GuitarString is interesting because it actually consists of three
Rectangle nodes and a Text node. The first Rectangle, used to detect mouse clicks, is
completely translucent (its opacity is 0). This Rectangle is wider than the guitar string
so the user can more easily select it with the mouse. Several properties implement its
behavior: property cursor lets a user know the string is selected and property
onMouseClicked provides the event handling code (play the note and vibrate the
string).
The second Rectangle node defines the visible string. The third Rectangle node
(assigned to variable play) “vibrates” by both moving and changing its height. This
rectangle is only visible when a note is playing and provides the vibration effect of
“plucking” a string. The movement and change in height are achieved with animation
and binding. The Text node simply displays the letter (E, A, D, etc.) associated with
the guitar string’s note.
3. Well, almost. If you don’t define function create, then you must declare the class abstract.
The Piano example (see “Project Piano” on page 167) uses an abstract class.
JavaFX.book Page 39 Monday, May 18, 2009 12:09 PM
time: 0s
action: playNote
}
KeyFrame {
time: 2.8s
action: stopNote
}
] // keyFrames
Listing 2.3 shows the code for Main.fx, the main program for GuitarTuner.
JavaFX.book Page 40 Monday, May 18, 2009 12:09 PM
2 - group.layoutBounds.minY
content: [
Rectangle { // guitar neck (fret board)
effect: DropShadow { }
x: 0
y: 0
width: 300
height: 121
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
proportional: true
stops: [
Stop {
offset: 0.0
color: Color.SADDLEBROWN
},
Stop {
offset: 1.0
color: Color.BLACK
}
]
}
} // Rectangle
for (i in [0..<numberFrets]) // two frets
Line {
startX: 100 * (i + 1)
startY: 0
endX: 100 * (i + 1)
endY: 120
stroke: Color.GRAY
}
for (i in [0..<numberStrings]) // six guitar strings
GuitarString {
yOffset: i * 20 + 5
note: noteValues[i]
noteText: guitarNotes[i]
synthNote: singleNote
}
]
}
]
}
}
Google
Bookmarks Delicious Digg Facebook StumbleUpon Reddit Twitter
informit.com/aw
25
puzzlers.book Page 26 Thursday, June 9, 2005 10:37 PM
This works, but it’s ugly. There are ways to avoid the verbosity of this
approach. You can force the + operator to perform string concatenation rather than
addition by ensuring that at least one of its operands is a string. The common
idiom is to begin a sequence of concatenations with the empty string (""), as fol-
lows:
This idiom ensures that subexpressions are converted to strings. Although useful it is
a bit ungainly and can lead to some confusion itself.
puzzlers.book Page 27 Thursday, June 9, 2005 10:37 PM
Can you guess what the following statement prints? If you aren’t sure, try it:
As of release 5.0, you also have the option of using the printf facility:
In summary, use the string concatenation operator with care. The + operator
performs string concatenation if and only if at least one of its operands is of
type String; otherwise, it performs addition. If none of the values to be concate-
nated are strings, you have several choices: prepend the empty string; convert the
first value to a string explicitly, using String.valueOf; use a string buffer; or if
you are using release 5.0, use the printf facility.
This puzzle also contains a lesson for language designers. Operator overload-
ing, even to the limited extent that it is supported in Java, can be confusing. It may
have been a mistake to overload the + operator for string concatenation.
ABC
}
puzzlers.book Page 28 Thursday, June 9, 2005 10:37 PM
If the reference is null, it is converted to the string "null". Otherwise, the con-
version is performed as if by an invocation of the toString method of the ref-
erenced object with no arguments; but if the result of invoking the toString
method is null, then the string "null" is used instead.
Note that these fixes work only if you invoke the correct overloading of the
valueOf or println method. In other words, they depend critically on the com-
pile-time type of the array reference. The following program illustrates this depen-
dency. It looks as though it incorporates the second fix described, but it produces
the same ugly output as the original program because it invokes the Object over-
loading of println instead of the char[] overloading:
The value of the boolean expression is, of course, false, and that is exactly
what the program prints. There is one surefire way to avoid this sort of difficulty:
When using the string concatenation operator, always parenthesize nontrivial
operands. More generally, when you are not sure whether you need parentheses,
err on the side of caution and include them. If you parenthesize the comparison in
the println statement as follows, it will produce the expected output of
Animals are equal: false:
Arguably, the program is still broken. Your code should rarely, if ever,
depend on the interning of string constants. Interning was designed solely to
puzzlers.book Page 31 Thursday, June 9, 2005 10:37 PM
reduce the memory footprint of the virtual machine, not as a tool for program-
mers. As this puzzle demonstrates, it isn’t always obvious which expressions will
result in string constants. Worse, if your code depends on interning for its correct
operation, you must carefully keep track of which fields and parameters must be
interned. The compiler can’t check these invariants for you, because interned and
noninterned strings are represented by the same type (String). The bugs that
result from the failure to intern a string are typically quite difficult to detect.
When comparing object references, you should use the equals method in
preference to the == operator unless you need to compare object identity rather
than value. Applying this lesson to our program, here is how the println state-
ment should look. It is clear that the program prints true when it is fixed in this
fashion:
This puzzle has two lessons for language designers. The natural precedence of
string concatenation might not be the same as that of addition. This implies that it
is problematic to overload the + operator to perform string concatenation, as men-
tioned in Puzzle 11. Also, reference equality is more confusing than value equality
for immutable types, such as String. Perhaps the == operator should perform
value comparisons when applied to immutable reference types. One way to
achieve this would be to make the == operator a shorthand for the equals method,
and to provide a separate method to perform reference identity comparison, akin
to System.identityHashCode.
System.out.println("a".length() + "b".length());
More likely, the author wanted to put the two double quote characters into the
string literal. You can’t do this with Unicode escapes, but you can do it with
escape sequences [JLS 3.10.6]. The escape sequence representing a double quote
is a backslash followed by a double quote (\"). If the Unicode escapes in the orig-
inal program are replaced with this escape sequence, it will print 16 as expected:
System.out.println("a\".length() + \"b".length());
There are escape sequences for many characters, including the single quote
(\’), linefeed (\n), tab (\t), and backslash (\\). You can use escape sequences in
character literals as well as in string literals. In fact, you can put any ASCII char-
acter into a string literal or a character literal by using a special kind of escape
sequence called an octal escape, but it is preferable to use normal escape
sequences where possible. Both normal escape sequences and octal escapes are far
preferable to Unicode escapes because unlike Unicode escapes, escape sequences
are processed after the program is parsed into tokens.
puzzlers.book Page 33 Thursday, June 9, 2005 10:37 PM
All the programs in this book are written using the ASCII subset of Unicode.
ASCII is the lowest common denominator of character sets. ASCII has only 128
characters, but Unicode has more than 65,000. A Unicode escape can be used to
insert any Unicode character into a program using only ASCII characters. A Uni-
code escape means exactly the same thing as the character that it represents.
Unicode escapes are designed for use when a programmer needs to insert a
character that can’t be represented in the source file’s character set. They are used
primarily to put non-ASCII characters into identifiers, string literals, character lit-
erals, and comments. Occasionally, a Unicode escape adds to the clarity of a pro-
gram by positively identifying one of several similar-looking characters.
In summary, prefer escape sequences to Unicode escapes in string and
character literals. Unicode escapes can be confusing because they are processed
so early in the compilation sequence. Do not use Unicode escapes to represent
ASCII characters. Inside of string and character literals, use escape sequences;
outside of these literals, insert ASCII characters directly into the source file.
/**
* Generated by the IBM IDL-to-Java compiler, version 1.0
* from F:\TestRoot\apps\a1\units\include\PolicyHome.idl
* Wednesday, June 17, 1998 6:44:40 o’clock AM GMT+00:00
*/
public class Test {
public static void main(String[] args) {
System.out.print("Hell");
System.out.println("o world");
}
}
puzzlers.book Page 34 Thursday, June 9, 2005 10:37 PM
/**
* This method calls itself recursively, causing a
* <tt>StackOverflowError</tt> to be thrown.
* The algorithm is due to Peter von der Ah\u00E9.
*/
/**
* This method calls itself recursively, causing a
* <tt>StackOverflowError</tt> to be thrown.
* The algorithm is due to Peter von der Ahé.
*/
Either of the preceding comments should cause the name to appear in the docu-
mentation as “Peter von der Ahé,” but the latter comment is also understandable in
the source file.
In case you were wondering, the comment in this puzzle was derived from an
actual bug report. The program was machine generated, which made it difficult to
track the problem down to its source, an IDL-to-Java compiler. To avoid placing
other programmers in this position, tools must not put Windows filenames into
puzzlers.book Page 35 Thursday, June 9, 2005 10:37 PM
comments in generated Java source files without first processing them to elimi-
nate backslashes.
In summary, ensure that the characters \u do not occur outside the context of a
valid Unicode escape, even in comments. Be particularly wary of this problem in
machine-generated code.
If you are like most people, this message did not help to clarify matters.
The key to this puzzle is the comment on the third line of the program. Like the
best of comments, this one is true. Unfortunately, this one is a bit too true. The com-
piler not only translates Unicode escapes into the characters they represent before
it parses a program into tokens (Puzzle 14), but it does so before discarding com-
ments and white space [JLS 3.2].
This program contains a single Unicode escape (\u000A), located in its sole
comment. As the comment tells you, this escape represents the linefeed character,
and the compiler duly translates it before discarding the comment. Unfortunately,
this linefeed character is the first line terminator after the two slash characters that
begin the comment (//) and so terminates the comment [JLS 3.4]. The words fol-
lowing the escape (is Unicode representation of linefeed (LF)) are therefore
not part of the comment; nor are they syntactically valid.
To make this more concrete, here is what the program looks like after the Uni-
code escape has been translated into the character it represents:
The easiest way to fix the program is to remove the Unicode escape from the
comment, but a better way is to initialize c with an escape sequence instead of a
hex integer literal, obviating the need for the comment:
Once this has been done, the program will compile and run, but it’s still a
questionable program. It is platform dependent for exactly the reason suggested in
the puzzle. On certain platforms, such as UNIX, it will print two complete line
separators; on others, such as Windows, it won’t. Although the output may look
the same to the naked eye, it could easily cause problems if it were saved in a file
or piped to another program for subsequent processing.
If you want to print two blank lines, you should invoke println twice. As of
release 5.0, you can use printf instead of println, with the format string
"%n%n". Each occurrence of the characters %n will cause printf to print the
appropriate platform-specific line separator.
Hopefully, the last three puzzles have convinced you that Unicode escapes can
be thoroughly confusing. The lesson is simple: Avoid Unicode escapes except
where they are truly necessary. They are rarely necessary.
\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
puzzlers.book Page 38 Thursday, June 9, 2005 10:37 PM
public
class Ugly
{public
static
void main(
String[]
args){
System.out
.println(
"Hello w"+
"orld");}}
The lesson of this puzzle is: Just because you can doesn’t mean you should.
Alternatively, If it hurts when you do it, don’t do it! More seriously, this puzzle
serves to reinforce the lessons of the previous three: Unicode escapes are essen-
tial when you need to insert characters that can’t be represented in any other
way into your program. Avoid them in all other cases. Unicode escapes reduce
program clarity and increase the potential for bugs.
For language designers, perhaps it should be illegal to use Unicode escapes to
represent ASCII characters. This would make the programs in Puzzles 14, 15, and
17 (this puzzle) invalid, eliminating a great deal of confusion. This restriction
would cause no great hardship to programmers.
character.fm Page 39 Thursday, April 20, 2006 10:17 PM
that follows, the program is guaranteed to print the integers from 0 to 255 in order,
regardless of the default charset:
if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0)
return "LETTER ";
/* (Operators not supported yet)
if ("+-*/&|!=".indexOf(ch) >= 0)
return "OPERATOR ";
*/
return "UNKNOWN ";
}
}
As you can see, the comment ends inside the string, which quite naturally
contains the characters */. The resulting program is syntactically invalid. Our
attempt to comment out a section of the program failed because string literals are
not treated specially within comments.
More generally, the text inside of comments is not treated specially in any way
[JLS 3.7]. Therefore, block comments do not nest. Consider the following code
snippet:
Now suppose that we try to comment out the snippet with a block comment.
Again, we highlight the entire comment in boldface:
/*
/* Add the numbers from to 1 to n */
int sum = 0;
for (int i = 1; i <= n; i++)
sum += i;
*/
As you can see, we failed to comment out the original snippet. On the bright
side, the resulting code contains a syntax error, so the compiler will tell us that we
have a problem.
puzzlers.book Page 43 Thursday, June 9, 2005 10:37 PM
You may occasionally see a section of code that is disabled with an if state-
ment whose boolean expression is the constant false:
package com.javapuzzlers;
public class Me {
public static void main(String[] args) {
System.out.println(
Me.class.getName().replaceAll(".", "/") + ".class");
}
}
puzzlers.book Page 44 Thursday, June 9, 2005 10:37 PM
package com.javapuzzlers;
public class Me {
public static void main(String[] args) {
System.out.println(
Me.class.getName().replaceAll("\\.", "/") + ".class");
}
}
To solve this kind of problem, release 5.0 provides the new static method
java.util.regex.Pattern.quote. It takes a string as a parameter and adds any
necessary escapes, returning a regular expression string that matches the input
puzzlers.book Page 45 Thursday, June 9, 2005 10:37 PM
string exactly. Here is how the program looks when modified to make use of this
method:
package com.javapuzzlers;
import java.util.regex.Pattern;
public class Me {
public static void main(String[] args) {
System.out.println(Me.class.getName().
replaceAll(Pattern.quote("."), "/") + ".class");
}
}
Another problem with this program is that the correct behavior is platform
dependent. Not all file systems use the slash character to separate hierarchical file-
name components. To get a valid filename for the platform on which you are run-
ning, you should use the correct platform-dependent separator character in place
of the slash. That is exactly what the next puzzle does.
package com.javapuzzlers;
import java.io.File;
System.out.println(MeToo.class.getName().replaceAll(
"\\.", Matcher.quoteReplacement(File.separator))
+ ".class");
The second method introduced in release 5.0 provides an even better solution.
This method, String.replace(CharSequence, CharSequence), does the same
thing as String.replaceAll, but treats both the pattern and the replacement as
literal strings. Here is how to fix the program by using this method:
System.out.println(MeToo.class.getName().
replace(".", File.separator) + ".class");
puzzlers.book Page 47 Thursday, June 9, 2005 10:37 PM
But what if you are using an earlier Java release? Unfortunately, there is no
easy way to generate the replacement string. It is easier to dispense with regular
expressions entirely and to use String.replace(char, char):
System.out.println(MeToo.class.getName().
replace(’.’, File.separatorChar) + ".class");
The main lesson of this puzzle and the previous one is: Be careful when
using unfamiliar library methods. When in doubt, consult the Javadoc. Also,
regular expressions are tricky: Problems tend to show up at run time rather than
compile time.
For API designers, it is important to use a method-naming scheme that distin-
guishes methods whose behavior differs in significant ways. Java’s String class is
not perfect in this regard. For many programmers, it is not easy to remember
which string-replacement methods use literal strings and which ones use regular
expressions or replacement strings.
http: // www.google.com;
System.out.println(":maximize");
}
}
That said, there is no earthly reason to include the label or the comment, which
has nothing to do with the program.
The lesson of this puzzle is that misleading comments and extraneous code
cause confusion. Write comments carefully and keep them up to date. Excise
dead code. Also, if something seems too strange to be true, it’s probably false.
puzzlers.book Page 49 Thursday, June 9, 2005 10:37 PM
import java.util.Random;
probably not require breaks. The lesson for language designers is to consider pro-
viding a structured switch statement.
The last and most subtle bug is that the expression new StringBuffer(’M’)
probably does not do what you think it does. You may not be familiar with the
StringBuffer(char) constructor, and with good reason: It does not exist. There
is a parameterless constructor, one that takes a String indicating the initial con-
tents of the string buffer and one that takes an int indicating its initial capacity. In
this case, the compiler selects the int constructor, applying a widening primitive
conversion to convert the char value ’M’ into the int value 77 [JLS 5.1.2]. In
other words, new StringBuffer(’M’) returns an empty string buffer with an ini-
tial capacity of 77. The remainder of the program appends the characters a, i, and
n to the empty string buffer and prints out its contents, which are always ain.
To avoid this kind of problem, use familiar idioms and APIs whenever pos-
sible. If you must use unfamiliar APIs, read the documentation carefully. In
this case, the program should have used the common StringBuffer constructor
that takes a String.
This corrected version of the program fixes all three bugs, printing Pain,
Gain, and Main with equal likelihood:
import java.util.Random;
Although this program fixes the bugs, it is overly verbose. Here is a more elegant
version:
import java.util.Random;
Better still is the following version. Although slightly longer, it is more gen-
eral. It does not depend on the fact that the possible outputs differ only in their
first characters:
import java.util.Random;
™
JavaFX
Build Rich Applications that Run on the Web, Desktops, Mobile Devices...
Anywhere!
AVAILABLE
• BOOK: 9780137012879
Using JavaFX, developers and graphic designers can work together to build robust,
• SAFARI ONLINE
immersive applications and deploy them anywhere: on the Web, on the desktop, and on
millions of Java-enabled mobile devices. Now, three of Sun’s leading JavaFX innovators • EBOOK: 0137013523
present the definitive, easy-to-use introduction to this breakthrough platform. • KINDLE: 013701354X
JavaFX™ brings together all the knowledge, techniques, and reusable code you need to
quickly deliver production-quality solutions. Writing for both developers and designers,
About the Authors
the authors explain how JavaFX simplifies and improves the Rich Internet Application Jim Clarke, principal technolo-
development process, and they show how to make the most of its ready-built components gist with Sun Microsystems, has
and frameworks. spent twelve years developing with
the Java platform. He has worked
The first book to cover the new JavaFX 1.1 release, JavaFX™ covers everything from with JavaFX for more than two
data integration to multimedia, special effects to REST. Drawing on their unsurpassed
years and served on the JavaFX
experience, the authors present a full chapter of design patterns and a complete case study
compiler team.
application. This book’s wide-ranging content includes:
Jim Connors, a long-time
• Building and running JavaFX programs
• Understanding the role of graphics designers in creating JavaFX Graphical Assets member of Sun’s system engineer-
• Writing fast, efficient JavaFX Script programs ing community, has spent a de-
• Using data binding to simplify Model-View-Controller application design cade helping customers leverage
• Creating rich user experiences with JavaFX visual components Java technologies ranging from
• Bringing user interfaces to life with lighting, reflection, and other special effects Java Card and Java ME to Java EE
• Adding motion with the JavaFX animation framework and JavaFX.
• Incorporating pictures, sound, and videos in your applications
• Creating RESTful applications with JSON and XML Eric Bruno, systems engineer
• Writing JavaFX applications that make the most of the underlying Java platform at Sun, is author of Java
Messaging (Charles River
Media, 2005) and Real-Time
Java™ Programming (Prentice
Hall, 2009) and is currently
contributing editor and blogger
for Dr. Dobb’s Journal.
informit.com/aw
3
JavaFX Primer
“I’m still at the beginning of my career. It’s all a little new,
and I’m still learning as I go.”
—Orlando Bloom
1. Torgersson, Olof. “A Note on Declarative Programming Paradigms and the Future of Defini-
tional Programming,” Chalmers University of Technology and Göteborg University, Göteborg,
Sweden. http://www.cs.chalmers.se/~oloft/Papers/wm96/wm96.html.
33
Clarke.book Page 34 Wednesday, May 6, 2009 10:00 AM
within an expression, and will produce a valid result. However, the result may
not have been what you expected. Therefore, the developer needs to be extra vig-
ilant when writing code and more thorough when testing it. At first, this may
seem alarming; however, this is offset by the ease of use and greater productivity
of JavaFX and by the fact that JavaFX tries to mitigate the user from experienc-
ing a crash.
One of the benefits of JavaFX being a declarative language is that much of the
“plumbing” to make objects interact is already provided within the language.
This allows the developer to be able to concentrate more on what needs to display,
and less on how to do it. The next sections provide an overview of the JavaFX
Script language including syntax, operators, and other features.
println("Hello World");
var a = result - 1;
while(a > 0) {
result *= a;
a--;
}
println("result = {result}");
Developer Note: If your script has exported members—that is, any external
accessible members such as public, protected, and package, functions or vari-
ables—then all loose expressions must be contained in a run function. For exam-
ple, if we change the result variable in the previous example to add public
visibility, we need to create the run function.
result = num;
var a = result - 1;
while(a > 0) {
result *= a;
a--;
}
println("{num}! = {result}");
}
Apart from the script level, a class defines instance variables and functions and
must first be instantiated into an object before being used. Class functions or
variables may access script level functions or variables within the same script
file, or from other script files if the appropriate access rights are assigned. On the
other hand, script level functions can only access class variables and functions if
the class is created into an object and then only if the class provides the appropri-
ate access rights. Access rights are defined in more detail later in this chapter.
Clarke.book Page 36 Wednesday, May 6, 2009 10:00 AM
Class Declaration
To declare a class in JavaFX, use the class keyword.
The public keyword is called an access modifier and means that this class can
be used by any other class or script, even if that class is declared in another script
file. If the class does not have a modifier, it is only accessible within the script
file where it is declared. For example, the class Point in Listing 3.2 does not
have a visibility modifier, so it is only has script visibility and can only be used
within the ArtWork script.
Developer Note: For each JavaFX script file, there is a class generated using
that script filename, even if one is not explicitly defined. For example, in the previ-
ous example for ArtWork.fx, there is a class ArtWork. This is true even if we had
not included the public class ArtWork declaration.
Also, all other classes defined within the script file have their name prepended with
the script file’s name. For example, in the previous example, class Point is fully quali-
fied as ArtWork.Point. Of course, if ArtWork belongs to a package, the package name
would also be used to qualify the name. For example, com.acme.ArtWork.Point.
To extend a class, use the extends keyword followed by the more generalized
class name. JavaFX classes can extend at most one Java or JavaFX class. If you
extend a Java class, that class must have a default (no-args) constructor.
Clarke.book Page 37 Wednesday, May 6, 2009 10:00 AM
CLASS DECLARATION 37
JavaFX may extend multiple JavaFX mixin classes or Java interfaces. Mixin
classes are discussed in the next section.
An application may contain many classes, so it is helpful to organize them in a
coherent way called packages. To declare that your class or script should belong
to a package, include a package declaration at the beginning of the script file.
The following example means that the Title class belongs to the
com.mycompany.components package. The full name of the Title class is now
com.mycompany.components.Title. Whenever the Title class is referenced, it
must be resolved to this full name.
package com.mycompany.components;
public class Title {
}
To make this resolution easier, you can include an import statement at the top of
your source file. For example:
import com.mycompany.components.Title;
Now, wherever Title is referenced within that script file, it will resolve to
com.mycompany.components.Title. You can also use a wildcard import declaration:
import com.mycompany.components.*;
With the wildcard form of import, whenever you refer to any class in the
com.mycompany.components package, it will resolve to its full name. The fol-
lowing code example shows how the class names are resolved, showing the fully
qualified class name in comments.
package com.mycompany.myapplication;
import com.mycompany.components.Title;
// com.mycompany.myapplication.MyClass
public class MyClass {
// com.mycompany.components.Title
public var title: Title;
}
A class can have package visibility by using the package keyword instead of public.
This means the class can only be accessed from classes within the same package.
Clarke.book Page 38 Wednesday, May 6, 2009 10:00 AM
A class may also be declared abstract, meaning that this class cannot be instan-
tiated directly, but can only be instantiated using one of its subclasses. Abstract
classes are not intended to stand on their own, but encapsulate a portion of
shared state and functions that several classes may use. Only a subclass of an
abstract class can be instantiated, and typically the subclass has to fill in those
unique states or behavior not addressed in the abstract class.
Mixin Classes
JavaFX supports a form of inheritance called mixin inheritance. To support this,
JavaFX includes a special type of class called a mixin. A mixin class is a class
that provides certain functionality to be inherited by subclasses. They cannot be
instantiated on their own. A mixin class is different from a Java interface in that
the mixin may provide default implementations for its functions and also may
declare and initialize its own variables.
To declare a mixin class in JavaFX, you need to include the mixin keyword in
the class declaration. The following code shows this.
A mixin class may contain any number of function declarations. If the function
declaration has a function body, then this is the default implementation for the
function. For example, the following listing shows a mixin class declaration for
a class that positions one node within another.
MIXIN CLASSES 39
(within.layoutBounds.width -
node.layoutBounds.width)/2.0 -
node.layoutBounds.minX;
}
protected bound function centerY(node: Node,
within: Node) : Number {
(within.layoutBounds.height -
node.layoutBounds.height)/2.0 -
node.layoutBounds.minY;
}
}
Subclasses that want to implement their own version of the mixin function must
use the override keyword when declaring the function. For instance, the follow-
ing code shows a subclass that implements its own version of the centerX()
function from the Positioner mixin class.
If the mixin function does not have a default implementation, it must be declared
abstract and the subclass must override this function to provide an implementa-
tion. For instance, the following code shows an abstract function added to the
Positioner mixin class.
The subclass must implement this function using the override keyword, as
shown in the following listing.
If two mixins have the same function signature or variable name, the system
resolves to the function or variable based on which mixin is declared first in the
Clarke.book Page 40 Wednesday, May 6, 2009 10:00 AM
extends clause. To specify a specific function or variable, use the mixin class
name with the function or variable name. This is shown in the following code.
Mixins may also define variables, with or without default values and triggers.
The subclass either inherits these variables or must override the variable declara-
tion. The following listing demonstrates this.
If a class extends a JavaFX class and one or more mixins, the JavaFX class takes
precedence over the mixin classes for variable initialization. If the variable is
declared in a superclass, the default value specified in the superclass is used; if
no default value is specified in the superclass, the “default value” for the type of
that variable is used. For the mixin classes, precedence is based on the order they
are defined in the extends clause. If a variable declared in a mixin has a default
value, and the variable is overridden without a default value in the main class, the
initial value specified in the mixin is used.
Mixins may also have init and postinit blocks. Mixin init and postinit blocks
are run after the super class’s init and postinit blocks and before the subclass’s
init and postinit blocks. Init and postinit blocks from the mixin classes
are run in the order they are declared in the extends clause for the subclass.
Object Literals
In JavaFX, objects are instantiated using object literals. This is a declarative syntax
using the name of the class that you want to create, followed by a list of initializ-
Clarke.book Page 41 Wednesday, May 6, 2009 10:00 AM
VARIABLES 41
ers and definitions for this specific instance. In Listing 3.3, an object of class
Title is created with the text “JavaFX is cool” at the screen position 10, 50.
When the mouse is clicked, the provided function will be called.
Variables
JavaFX supports two kinds of variables: instance and script. Script variables
hold state for the entire script, whereas instance variables hold state for specific
instantiations of a class declared within the script file.
Clarke.book Page 42 Wednesday, May 6, 2009 10:00 AM
The actual Point object assigned to centerPoint remains unchanged, but the
state of that object instance, the actual x and y values, may change. When used in
binding though, centerPoint is constant; if the state of centerPoint changes,
the bound context will be notified of the change.
Changeable instance variables are declared using the var keyword with an
optional default value. If the default value is omitted, a reasonable default is
used; basically, Numbers default to zero, Boolean defaults to false, Strings
default to the empty string, Sequences default to the Empty Sequence, and
everything else defaults to null.
Script variables are declared outside of any class declaration, whereas instance
variables are declared within a class declaration. If a script variable is declared
with one of the access modifiers—public, protected, or package—it may be
used from outside of the script file, by referring to its fully qualified name. This
fully qualified name is the combination of package name, script name, and the
variable name. The following is the fully qualified name to a public script vari-
able from the javafx.scene.Cursor class for the crosshair cursor.
javafx.scene.Cursor.CROSSHAIR;
Instance variables are declared within a class declaration and come into being
when the object is created. Listing 3.5 illustrates several examples of script and
instance variables.
Clarke.book Page 43 Wednesday, May 6, 2009 10:00 AM
VARIABLES 43
...
}
You may have noticed that some of the declarations contain a type and some
don’t. When a type is not declared, the type is inferred from the first assigned
value. String, Number, Integer, and Boolean are built-in types, everything else
is either a JavaFX or a Java class. (There is a special syntax for easily declaring
Duration and KeyFrame class instances that will be discussed in Chapter 7, Add
Motion with JavaFX Animation.)
Table 3.1 lists the access modifiers for variables and their meaning and restric-
tions. You will notice reference to initialization, which refers to object literal
declarations. Also, you will notice variables being bound. This is a key feature of
JavaFX and is discussed in depth in Chapter 4.
Clarke.book Page 44 Wednesday, May 6, 2009 10:00 AM
public var Read and writable by anyone. Also, it can be initialized, overrid-
den, read, assigned, or bound from anywhere.
public-read var Readable by anyone, but only writable within the script.
public-init var Can be initialized in object literals, but can only be updated by the
owning script. Only allowed for instance variables.
package var A variable accessible from the package. This variable can be ini-
tialized, overridden, read, assigned, or bound only from a class
within the same package.
package def Define a variable that is readable or bound only from classes
within the same package.
protected var A variable accessible from the package or subclasses. This vari-
able can be initialized, overridden, read, assigned, or bound from
only a subclass or a class within the same package.
protected def Define a variable that is readable or bound only from classes
within the same package or subclasses.
public-read protected var Readable and bound by anyone, but this variable can only be ini-
tialized, overridden, or assigned from only a subclass or a class
within the same package.
public-init protected var Can be initialized in object literals, read and bound by anyone, but
can only be overridden or assigned, from only a subclass or a
class within the same package. Only allowed for instance
variables.
Clarke.book Page 45 Wednesday, May 6, 2009 10:00 AM
SEQUENCES 45
You can also declare change triggers on a variable. Change triggers are blocks of
JavaFX script that are called whenever the value of a variable changes. To
declare a change trigger, use the on replace syntax:
Sequences
Sequences are ordered lists of objects. Because ordered lists are used so often in
programming, JavaFX supports sequence as a first class feature. There is built-in
support in the language for declaring sequences, inserting, deleting, and modify-
ing items in the sequence. There is also powerful support for retrieving items
from the sequence.
Declaring Sequences
To declare a sequence, use square brackets with each item separated by a
comma. For example:
This sequence is a sequence of Strings, because the elements within the brack-
ets are Strings. This could have also been declared as
To assign an empty sequence, just use square brackets, []. This is also the
default value for a sequence. For example, the following two statements both
equal the empty sequence.
When the sequence changes, you can assign a trigger function to process the
change. This is discussed in depth in the next chapter.
A shorthand for declaring a sequence of Integers and Numbers uses a range, a
start integer or number with an end. So, [1..9] is the sequence of the integers
from 1 thru 9, inclusive; the exclusive form is [1..<9]—that is, 1 through 8. You
can also use a step function, so if, for example, you want even positive integers,
use [2..100 step 2]. For numbers, you can use decimal fractions, [0.1..1.0
step 0.1]. Without the step, a step of 1 or 1.0 is implicit.
Ranges may also go in decreasing order. To do this, the first number must be
higher than the second. However, without a negative step function, you always
end up with an empty sequence. This is because the default step is always posi-
tive 1.
To build sequences that include the elements from other sequences, just include
the source sequences within the square brackets.
Also, you can use another sequence to create a sequence by using the Boolean
operator. Another sequence is used as the source, and a Boolean operator is
applied to each element in the source sequence, and the elements from the source
that evaluate to true are returned in the new sequence. In the following example,
n represents each item in the sequence of positive integers and n mod 2 == 0 is
the evaluation.
One can also allocate a sequence from a for loop. Each object “returned” from
the iteration of the for loop is added to the sequence:
SEQUENCES 47
You can also take slices of sequence by providing a range. Both of the next two
sequences are equal.
The following two sequences are also equal. The second example uses a syntax
for range to indicate start at an index and return all elements after that index.
Modifying Sequences
To replace an element in a sequence, just assign a new value to that indexed loca-
tion in the index.
students[3] = "john";
The assignment to position 3 would be ignored because the size of students is cur-
rently 3, and the highest valid index is 2. Similarly, assignment to the index -1 is
silently ignored for the same reason; -1 is outside of the sequence range.
Furthermore, if you access an element location outside of the existing range for the
sequence, a default value is returned. For Numbers, this is zero; for Strings, the
empty string; for Objects, this is null.
Native Array
Native array is a feature that allows you to create Java arrays. This feature is
mainly used to handle the transfer of arrays back and forth from JavaFX and
Java. An example of creating a Java int[] array is shown in the following code.
Native arrays are not the same as sequences, though they appear similar. You can-
not use the sequence operators, such as insert and delete, or slices. However, you
can do assignments to the elements of the array as shown in the following code:
ints[2] = 4;
Clarke.book Page 49 Wednesday, May 6, 2009 10:00 AM
FUNCTIONS 49
However, if you assign outside of the current bounds of the array, you will get an
ArrayIndexOutOfBounds Exception.
You can also use the for operator to iterate over the elements in the native array.
The following code shows an example of this.
for(i in ints) {
println(i);
}
for(i in ints where i mod 2 == 0) {
println(i);
}
Functions
Functions define behavior. They encapsulate statements that operate on inputs,
function arguments, and may produce a result, a returned expression. Like vari-
ables, functions are either script functions or instance functions. Script functions
operate at the script level and have access to variables and other functions
defined at the script level. Instance functions define the behavior of an object and
have access to the other instance variables and functions contained within the
function’s declaring class. Furthermore, an instance function may access any
script-level variables and functions contained within its own script file.
To declare a function, use an optional access modifier, public, protected, or
package, followed by the keyword function and the function name. If no
access modifier is provided, the function is private to the script file. Any function
arguments are contained within parentheses. You may then specify a function
return type. If the return type is omitted, the function return type is inferred from
the last expression in the function expression block. The special return type of
Void may be used to indicate that the function returns nothing.
In the following example, both function declarations are equal. The first function
infers a return type of Glow, because the last expression in the function block is
an object literal for a Glow object. The second function explicitly declares a
return type of Glow, and uses the return keyword.
The return keyword is optional when used as the last expression in a function
block. However, if you want to return immediately out of an if/else or loop,
you must use an explicit return.
In JavaFX, functions are objects in and of themselves and may be assigned to
variables. For example, to declare a function variable, assign a function to that
variable, and then invoke the function through the variable.
Functions definitions can also be anonymous. For example, for a function variable:
TextBox {
columns: 20
action: function() {
println("TextBox action");
}
}
class MyClass {
public function print() { println("MyClass"); }
}
class MySubClass extends MyClass {
override function print() { println("MySubClass"); }
}
Strings
String Literals
String literals can be specified using either double (") or single (') quotes. The
main reason to use one over the other is to avoid character escapes within the
string literal—for example, if the string literal actually contains double quotes.
Clarke.book Page 51 Wednesday, May 6, 2009 10:00 AM
STRINGS 51
By enclosing the string in single quotes, you do not have to escape the embedded
double quotes. Consider the following two examples, which are both valid:
Expressions can be embedded within the string literal by using curly braces:
The embedded expression must be a valid JavaFX or Java expression that returns
an object. This object will be converted to a string using its toString() method.
For instance:
println("The state is {
if(checkRunning()) "Running" else "Stopped"}");
In this example, the strings from both lines are concatenated into one string.
Only the string literals within the quotes are used and any white space outside of
the quotes is ignored.
Unicode characters can be entered within the string literal using \u + the four
digit unicode.
Formatting
Embedded expressions within string literals may contain a formatting code that
specifies how the embedded expression should be presented. Consider the
following:
Now if total is an integer, the resulting string will show the decimal number;
but if total is a Number, the resulting string will show the number formatted
according to the local locale.
produces:
To format an expression, you need a format code within the embedded expres-
sion. This is a percent (%) followed by the format codes. The format code is
defined in the java.util.Formatter class. Please refer to its JavaDoc page for
more details (http://java.sun.com/javase/6/docs/api/index.html).
Internationalization
To internationalize a string, you must use the “Translate Key” syntax within the
string declaration. To create a translate key, the String assignment starts with ##
(sharp, sharp) combination to indicate that the string is to be translated to the
host locale. The ## combination is before the leading double or single quote.
Optionally, a key may be specified within square brackets ([]). If a key is not
Clarke.book Page 53 Wednesday, May 6, 2009 10:00 AM
STRINGS 53
specified, the string itself becomes the key into the locale properties file. For
example:
In the preceding example, using the first form, the key is "Zip Code: ", whereas
for the second form, the key is "postal". So how does this work?
By default, the localizer searches for a property file for each unique script name.
This is the package name plus script filename with a locale and a file type of
.fxproperties. So, if your script name is com.mycompany.MyClass, the localizer
code would look for a property file named com/mycompany/MyClass_xx.
fxproperties on the classpath, where xx is the locale. For example, for English
in the United Kingdom, the properties filename would be com/mycompany/
MyClass_en_GB.fxproperties, whereas French Canadian would be com/mycom-
pany/MyClass_fr_CA.fxproperties. If your default locale is just English, the
properties file would be MyClass_en.fxproperties. The more specific file is
searched first, then the least specific file is consulted. For instance, MyClass_
en_GB.fxproperties is searched for the key and if it is not found, then
MyClass_en.fxproperties would be searched. If the key cannot be found at all,
the string itself is used as the default. Here are some examples:
Example #1:
println(##"Thank you");
French – MyClass_fr.fxproperties:
German – MyClass_de.fxproperties:
Japanese – MyClass_ja.fxproperties:
Example #2:
println(##[ThankKey] "Thank you");
French – MyClass_fr.fxproperties:
"ThankKey" = "Merci"
German – MyClass_de.fxproperties:
"ThankKey" = "Danke"
Japanese – MyClass_ja.fxproperties:
"ThankKey" = "Arigato"
When you use a string with an embedded expression, the literal key contains a
%s, where the expression is located within the string. For example:
In this case, the key is "Hello, my name is %s". Likewise, if you use more
than one expression, the key contains a "%s" for each expression:
Lastly, you can associate another Properties file to the script. This is done using
the javafx.util.StringLocalizer class. For example:
StringLocalizer.associate("com.mycompany.resources.MyResources",
"com.mycompany");
Now, all translation lookups for scripts in the com.mycompany package will look for
the properties file com/mycompany/resources/MyResources_xx.fxproperties,
instead of using the default that uses the script name. Again, xx is replaced with
the locale abbreviation codes.
Clarke.book Page 55 Wednesday, May 6, 2009 10:00 AM
Exception Handling
The throw statement is the same as Java and can only throw a class that extends
java.lang.Throwable.
The try/catch/finally expression is the same as Java, but uses the JavaFX syntax:
try {
} catch (e:SomeException) {
} finally {
}
Clarke.book Page 56 Wednesday, May 6, 2009 10:00 AM
Operators
Table 3.2 contains a list of the operators used in JavaFX. The priority column
indicates the operator evaluation precedence, with higher precedence operators
in the first rows. Operators with the same precedence level are evaluated equally.
Assignment operators are evaluated right to left, whereas all others are evaluated
left to right. Parentheses may be used to alter this default evaluation order.
- Unary minus
4 +, - Arithmetic operators
8 or Logical OR
11 = Assignment
Clarke.book Page 57 Wednesday, May 6, 2009 10:00 AM
Conditional Expressions
if/else
if is similar to if as defined in other languages. First, a condition is evaluated
and if true, the expression block is evaluated. Otherwise, if an else expression
block is provided, that expression block is evaluated.
if (date == today) {
println("Date is today");
}else {
println("Out of date!!!");
}
One important feature of if/else is that each expression block may evaluate to
an expression that may be assigned to a variable:
Also the expression blocks can be more complex than simple expressions. List-
ing 3.7 shows a complex assignment using an if/else statement to assign the
value to outOfDateMessage.
In the previous example, the last expression in the block, the error message string
literal, is the object that is assigned to the variable. This can be any JavaFX
Object, including numbers.
Because the if/else is an expression block, it can be used with another if/else
statement. For example:
Clarke.book Page 58 Wednesday, May 6, 2009 10:00 AM
Looping Expressions
For
for loops are used with sequences and allow you to iterate over the members of
a sequence.
To be similar with traditional for loops that iterate over a count, use an integer
sequence range defined within square brackets.
for( i in [0..100]} {
The for expression can also return a new sequence. For each iteration, if the
expression block executed evaluates to an Object, that Object is inserted into a
new sequence returned by the for expression. For example, in the following for
expression, a new Text node is created with each iteration of the day of the
week. The overall for expression returns a new sequence containing Text graph-
ical elements, one for each day of the week.
Another feature of the for expression is that it can do nested loops. Listing 3.8
shows an example of using nested loops.
Course {
title: "Geometry I"
students: [ "Clarke, "Connors", "Bruno" ]
},
Course {
title: "Geometry II"
students: [ "Clarke, "Connors", ]
},
Course {
title: "Algebra I"
students: [ "Connors", "Bruno" ]
},
];
There may be zero or more secondary loops and they are separated from the pre-
vious ones by a comma, and may reference any element from the previous loops.
You can also include a where clause on the sequence to limit the iteration to only
those elements where the where clause evaluates to true:
while
The while loop works similar to the while loop as seen in other languages:
var ndx = 0;
while ( ndx < 100) {
println("{ndx}");
ndx++;
}
Note that unlike the JavaFX for loop, the while loop does not return any expres-
sion, so it cannot be used to create a sequence.
Clarke.book Page 60 Wednesday, May 6, 2009 10:00 AM
Break/Continue
break and continue control loop iterations. break is used to quit the loop altogether.
It causes all the looping to stop from that point. On the other hand, continue just
causes the current iteration to stop, and the loop resumes with the next iteration.
Listing 3.9 demonstrates how these are used.
for(book in Books ) {
if(book.publisher == "Addison Wesley") {
insert book into bookList;
continue; // moves on to check next book.
}
insert book into otherBookList;
otherPrice += book.price;
}
Type Operators
The instanceof operator allows you to test the class type of an object, whereas
the as operator allows you to cast an object to another class. One way this is use-
ful is to cast a generalized object to a more specific class in order to perform a
function from that more specialized class. Of course, the object must inherently
be that kind of class, and that is where the instanceof operator is useful to test
if the object is indeed that kind of class. If you try to cast an object to a class that
that object does not inherit from, you will get an exception.
In the following listing, the printLower() function will translate a string to lower-
case, but for other types of objects, it will just print it as is. First, the generic object
is tested to see if it is a String. If it is, the object is cast to a String using the as oper-
ator, and then the String’s toLowerCase() method is used to convert the output to
all lowercase. Listing 3.10 illustrates the use of the instanceof and as operators.
}
printLower("Rich Internet Application");
printLower(3.14);
If the script contains any exported classes, variables, or functions, arguments are
obtained by defining a special run function at the script level.
You have already seen one of these, println(). Println() takes an object argu-
ment and prints it out to the console, one line at a time. It is similar to the Java
method, System.out.println(). Its companion function is print(). Print()
prints out its argument but without a new line. The argument’s toString()
method is invoked to print out a string.
commenceTest(status);
}
}
public function commenceTest(status:Number) : Void {
println("commenceTest status = {status}:);
}
}
In this example, when the class, Test, is first instantiated, the instance variable,
status, first takes on the default value of 0.0, and then the on replace expression
block is evaluated. However, this leaves the status in the uninitialized state.
Only when a value is assigned to status, will the state change to initialized.
Consider the following:
In this case when Test is created using the object literal, Test{}, status takes on
the default value of 0.0; however, it is not initialized, so commenceTest will
Clarke.book Page 63 Wednesday, May 6, 2009 10:00 AM
not be invoked during object creation. Now when we assign a value to status,
the state changes to initialized, so commenceTest is now invoked. Please note
that if we had assigned a default value to status, even if that value is 0, then
status immediately is set to initialized. The following example demon-
strates this.
println("DIR = {__DIR__}");
println("FILE = {__FILE__}");
// to locate an image
var image = Image { url: "{__DIR__}images/foo.jpeg" };
The following examples show the output from a directory based classpath versus
using a JAR-based class path.
DIR = jar:file:/export/home/jclarke/Documents/
Book/FX/code/Chapter3/Misc/dist/Misc.jar!/misc/
FILE = jar:file:/export/home/jclarke/Documents/
Book/FX/code/Chapter3/Misc/dist/Misc.jar!/misc/Test.class
continues
Clarke.book Page 64 Wednesday, May 6, 2009 10:00 AM
DIR = file:/export/home/jclarke/Documents/Book/
FX/code/Chapter3/Misc/dist/tmp/misc/
FILE = file:/export/home/jclarke/Documents/Book/
FX/code/Chapter3/Misc/dist/tmp/misc/Test.class
Notice the Trailing Slash on __DIR__: Because the tailing slash already exists
on __DIR__, do not add an extra trailing slash when using __DIR__ to build a path to
a resource like an image. Image{ url: "{__DIR__}image/foo.jpeg"} is correct.
Image{ url: "{__DIR__}/image/foo.jpeg"} is wrong. If you add the trailing slash
after __DIR__, the image will not be found and you will be scratching your head
trying to figure out why not.
Chapter Summary
This chapter covered key concepts in the JavaFX Scripting language. You were
shown what constitutes a script and what constitutes a class. You were shown
how to declare script and instance variables, how to create and modify
sequences, and how to control logic flow.
You now have a basic understanding of the JavaFX Script language syntax and
operators. Now, it is time to put this to use. In the following chapters, we will
drill down into the key features of JavaFX and show how to leverage the JavaFX
Script language to take advantage of those features. In the next chapter, we start
our exploration of JavaFX by discussing the data synchronization support in the
JavaFX runtime.
Google
Bookmarks Delicious Digg Facebook StumbleUpon Reddit Twitter
informit.com/aw
Task Execution
Most concurrent applications are organized around the execution of tasks: ab-
stract, discrete units of work. Dividing the work of an application into tasks
simplifies program organization, facilitates error recovery by providing natural
transaction boundaries, and promotes concurrency by providing a natural struc-
ture for parallelizing work.
113
114 Chapter 6. Task Execution
class SingleThreadWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
Socket connection = socket.accept();
handleRequest(connection);
}
}
}
1. In some situations, sequential processing may offer a simplicity or safety advantage; most GUI
frameworks process tasks sequentially using a single thread. We return to the sequential model in
Chapter 9.
6.1. Executing tasks in threads 115
class ThreadPerTaskWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
};
new Thread(task).start();
}
}
}
Listing 6.2. Web server that starts a new thread for each request.
• Task processing is offloaded from the main thread, enabling the main loop
to resume waiting for the next incoming connection more quickly. This
enables new connections to be accepted before previous requests complete,
improving responsiveness.
• Tasks can be processed in parallel, enabling multiple requests to be serviced
simultaneously. This may improve throughput if there are multiple process-
ors, or if tasks need to block for any reason such as I/O completion, lock
acquisition, or resource availability.
Thread lifecycle overhead. Thread creation and teardown are not free. The ac-
tual overhead varies across platforms, but thread creation takes time, intro-
ducing latency into request processing, and requires some processing activ-
ity by the JVM and OS. If requests are frequent and lightweight, as in most
server applications, creating a new thread for each request can consume
significant computing resources.
Resource consumption. Active threads consume system resources, especially
memory. When there are more runnable threads than available process-
ors, threads sit idle. Having many idle threads can tie up a lot of memory,
putting pressure on the garbage collector, and having many threads com-
peting for the CPUs can impose other performance costs as well. If you have
enough threads to keep all the CPUs busy, creating more threads won’t help
and may even hurt.
Stability. There is a limit on how many threads can be created. The limit varies
by platform and is affected by factors including JVM invocation parameters,
the requested stack size in the Thread constructor, and limits on threads
placed by the underlying operating system.2 When you hit this limit, the
most likely result is an OutOfMemoryError. Trying to recover from such an
error is very risky; it is far easier to structure your program to avoid hitting
this limit.
Up to a certain point, more threads can improve throughput, but beyond that
point creating more threads just slows down your application, and creating one
thread too many can cause your entire application to crash horribly. The way to
stay out of danger is to place some bound on how many threads your application
creates, and to test your application thoroughly to ensure that, even when this
bound is reached, it does not run out of resources.
The problem with the thread-per-task approach is that nothing places any
limit on the number of threads created except the rate at which remote users can
throw HTTP requests at it. Like other concurrency hazards, unbounded thread
creation may appear to work just fine during prototyping and development, with
problems surfacing only when the application is deployed and under heavy load.
So a malicious user, or enough ordinary users, can make your web server crash
if the traffic load ever reaches a certain threshold. For a server application that is
supposed to provide high availability and graceful degradation under load, this
is a serious failing.
2. On 32-bit machines, a major limiting factor is address space for thread stacks. Each thread main-
tains two execution stacks, one for Java code and one for native code. Typical JVM defaults yield
a combined stack size of around half a megabyte. (You can change this with the -Xss JVM flag or
through the Thread constructor.) If you divide the per-thread stack size into 232 , you get a limit of
a few thousands or tens of thousands of threads. Other factors, such as OS limitations, may impose
stricter limits.
6.2. The Executor framework 117
Executor may be a simple interface, but it forms the basis for a flexible and
powerful framework for asynchronous task execution that supports a wide vari-
ety of task execution policies. It provides a standard means of decoupling task
submission from task execution, describing tasks with Runnable. The Executor
implementations also provide lifecycle support and hooks for adding statistics
gathering, application management, and monitoring.
Executor is based on the producer-consumer pattern, where activities that
submit tasks are the producers (producing units of work to be done) and the
threads that execute tasks are the consumers (consuming those units of work).
Using an Executor is usually the easiest path to implementing a producer-consumer
design in your application.
class TaskExecutionWebServer {
private static final int NTHREADS = 100;
private static final Executor exec
= Executors.newFixedThreadPool(NTHREADS);
Listing 6.5. Executor that starts a new thread for each task.
Listing 6.6. Executor that executes tasks synchronously in the calling thread.
Execution policies are a resource management tool, and the optimal policy
depends on the available computing resources and your quality-of-service re-
quirements. By limiting the number of concurrent tasks, you can ensure that
the application does not fail due to resource exhaustion or suffer performance
problems due to contention for scarce resources.3 Separating the specification of
execution policy from task submission makes it practical to select an execution
policy at deployment time that is matched to the available hardware.
3. This is analogous to one of the roles of a transaction monitor in an enterprise application: it can
throttle the rate at which transactions are allowed to proceed so as not to exhaust or overstress limited
resources.
120 Chapter 6. Task Execution
Executing tasks in pool threads has a number of advantages over the thread-
per-task approach. Reusing an existing thread instead of creating a new one
amortizes thread creation and teardown costs over multiple requests. As an
added bonus, since the worker thread often already exists at the time the request
arrives, the latency associated with thread creation does not delay task execution,
thus improving responsiveness. By properly tuning the size of the thread pool,
you can have enough threads to keep the processors busy while not having so
many that your application runs out of memory or thrashes due to competition
among threads for resources.
The class library provides a flexible thread pool implementation along with
some useful predefined configurations. You can create a thread pool by calling
one of the static factory methods in Executors:
4. Single-threaded executors also provide sufficient internal synchronization to guarantee that any
memory writes made by tasks are visible to subsequent tasks; this means that objects can be safely
confined to the “task thread” even though that thread may be replaced with another from time to
time.
5. While the server may not fail due to the creation of too many threads, if the task arrival rate exceeds
the task service rate for long enough it is still possible (just harder) to run out of memory because
of the growing queue of Runnables awaiting execution. This can be addressed within the Executor
framework by using a bounded work queue—see Section 8.3.2.
6.2. The Executor framework 121
It also degrades more gracefully, since it does not create thousands of threads
that compete for limited CPU and memory resources. And using an Executor
opens the door to all sorts of additional opportunities for tuning, management,
monitoring, logging, error reporting, and other possibilities that would have been
far more difficult to add without a task execution framework.
card the task or might cause execute to throw the unchecked RejectedExecu-
tionException. Once all tasks have completed, the ExecutorService transitions
to the terminated state. You can wait for an ExecutorService to reach the termi-
nated state with awaitTermination, or poll for whether it has yet terminated with
isTerminated. It is common to follow shutdown immediately by awaitTermina-
tion, creating the effect of synchronously shutting down the ExecutorService.
(Executor shutdown and task cancellation are covered in more detail in Chapter
7.)
LifecycleWebServer in Listing 6.8 extends our web server with lifecycle sup-
port. It can be shut down in two ways: programmatically by calling stop, and
through a client request by sending the web server a specially formatted HTTP
request.
class LifecycleWebServer {
private final ExecutorService exec = ...;
6. Timer does have support for scheduling based on absolute, not relative time, so that tasks can be
sensitive to changes in the system clock; ScheduledThreadPoolExecutor supports only relative time.
124 Chapter 6. Task Execution
try {
List<ImageData> imageData = future.get();
for (ImageData data : imageData)
renderImage(data);
} catch (InterruptedException e) {
// Re-assert the thread’s interrupted status
Thread.currentThread().interrupt();
// We don’t need the result, so cancel the task too
future.cancel(true);
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
as is entirely possible, the resulting performance is not much different from the
sequential version, but the code is a lot more complicated. And the best we can do
with two threads is speed things up by a factor of two. Thus, trying to increase
concurrency by parallelizing heterogeneous activities can be a lot of work, and
there is a limit to how much additional concurrency you can get out of it. (See
Sections 11.4.2 and 11.4.3 for another example of the same phenomenon.)
renderText(source);
try {
for (int t = 0, n = info.size(); t < n; t++) {
Future<ImageData> f = completionService.take();
ImageData imageData = f.get();
renderImage(imageData);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
8. The timeout passed to get is computed by subtracting the current time from the deadline; this may
in fact yield a negative number, but all the timed methods in java.util.concurrent treat negative
timeouts as zero, so no extra code is needed to deal with this case.
9. The true parameter to Future.cancel means that the task thread can be interrupted if the task is
currently running; see Chapter 7.
132 Chapter 6. Task Execution
ters travel dates and requirements and the portal fetches and displays bids from
a number of airlines, hotels or car rental companies. Depending on the com-
pany, fetching a bid might involve invoking a web service, consulting a database,
performing an EDI transaction, or some other mechanism. Rather than have the
response time for the page be driven by the slowest response, it may be preferable
to present only the information available within a given time budget. For provi-
ders that do not respond in time, the page could either omit them completely or
display a placeholder such as “Did not hear from Air Java in time.”
Fetching a bid from one company is independent of fetching bids from an-
other, so fetching a single bid is a sensible task boundary that allows bid retrieval
to proceed concurrently. It would be easy enough to create n tasks, submit them
to a thread pool, retain the Futures, and use a timed get to fetch each result
sequentially via its Future, but there is an even easier way—invokeAll.
Listing 6.17 uses the timed version of invokeAll to submit multiple tasks to
an ExecutorService and retrieve the results. The invokeAll method takes a
collection of tasks and returns a collection of Futures. The two collections have
identical structures; invokeAll adds the Futures to the returned collection in the
order imposed by the task collection’s iterator, thus allowing the caller to associate
a Future with the Callable it represents. The timed version of invokeAll will
return when all the tasks have completed, the calling thread is interrupted, or
the timeout expires. Any tasks that are not complete when the timeout expires
are cancelled. On return from invokeAll, each task will have either completed
normally or been cancelled; the client code can call get or isCancelled to find
6.3. Finding exploitable parallelism 133
out which.
Summary
Structuring applications around the execution of tasks can simplify development
and facilitate concurrency. The Executor framework permits you to decouple
task submission from execution policy and supports a rich variety of execution
policies; whenever you find yourself creating threads to perform tasks, consider
using an Executor instead. To maximize the benefit of decomposing an applica-
tion into tasks, you must identify sensible task boundaries. In some applications,
the obvious task boundaries work well, whereas in others some analysis may be
required to uncover finer-grained exploitable parallelism.
134 Chapter 6. Task Execution
List<Future<TravelQuote>> futures =
exec.invokeAll(tasks, time, unit);
List<TravelQuote> quotes =
new ArrayList<TravelQuote>(tasks.size());
Iterator<QuoteTask> taskIter = tasks.iterator();
for (Future<TravelQuote> f : futures) {
QuoteTask task = taskIter.next();
try {
quotes.add(f.get());
} catch (ExecutionException e) {
quotes.add(task.getFailureQuote(e.getCause()));
} catch (CancellationException e) {
quotes.add(task.getTimeoutQuote(e));
}
}
Collections.sort(quotes, ranking);
return quotes;
}
™
Real-Time Java Programming
Sun Microsystems’ Java Real-Time System (Java RTS) is proving itself in numerous, About the Authors
wide-ranging environments, including finance, control systems, manufacturing, and
defense. Java RTS and the RTSJ standard (JSR-001) eliminate the need for complicated, Eric J. Bruno, systems engineer at
specialized, real-time languages and operating environments, saving money by leverag- Sun Microsystems, specializes in Java
ing Java’s exceptional productivity and familiarity. RTS in the financial community. He is
contributing editor for Dr. Dobb’s Jour-
In Real-Time Java™ Programming, two of Sun’s top real-time programming nal, and writes its online Java blog.
experts present the deep knowledge and realistic code examples that developers need Prior to Sun, Eric worked at Reuters
to succeed with Java RTS and its APIs. As they do so, the authors also illuminate the where he developed real-time trading
foundations of real-time programming in any RTSJ-compatible environment. systems, order-entry and routing
systems, as well as real-time news and
Key topics include: quotes feeds, in both Java and C++.
• Real-time principles and concepts, and the unique requirements of real-time Greg Bollella, Ph.D., distinguished
application design and development engineer at Sun Microsystems, leads
• How Java has been adapted to real-time environments R&D for real-time Java. He was specifi
• A complete chapter on garbage collection concepts and Java SE collectors cation lead for JSR-001, the Real-Time
• Using the Java RTS APIs to solve actual real-time system problems as efficiently Specification for Java (RTSJ), and led
as possible the Real-Time for Java Expert Group
• Utilizing today’s leading Java RTS development and debugging tools under the Java Community Process.
• Understanding real-time garbage collection, threads, scheduling, and dispatching He has written multiple books, articles,
• Programming new RTSJ memory models and professional papers about real-
• Dealing with asynchronous event handling and asynchronous transfer of control time computing. He has a Ph.D. in
computer science from the University
of North Carolina at Chapel Hill, where
he wrote a dissertation on real-time
scheduling theory and real-time
systems implementation.
informit.com/ph
THERE are many misunderstandings about what real-time is, even amongst sea-
soned enterprise Java developers. Some confuse it with high-performance, or fast,
computing; others think of dynamic applications such as instant messaging. Neither
one is necessarily an example of a real-time system. Therefore real-time does not
always equal “real fast,” although good performance is often desirable and achievable.
In fact, real-time is often orthogonal with high-throughput systems; there’s a trade-off
in throughput in many cases. The best way to avoid all of this confusion is to think of it
this way: application performance and throughput requirements can be solved with
faster, or additional, hardware; real-time requirements, in general, cannot.
This chapter will define real-time computing, and will explain why throwing hardware
at a real-time requirement will almost never do any good. We’ll discuss the qualities of
a real-time system, define key terms used in the discipline, and examine tools, lan-
guages, and environments available to real-time developers outside of the Java world.
By the end of this chapter, you’ll have a good real-time foundation to build upon.
important, but what’s also important is the system’s ability to respond at precisely
the right moment in time. Access to a high-resolution timer to perform actions on
precise time periods is often a requirement. These two qualities together best
define a real-time application’s acceptable behavior: the ability to respond to an
event before a deadline, and accurately perform periodic processing, regardless of
overall system load. Before we go any further, it’s important to examine the term
deadline a little more closely, as well as some other terms often used in the context
of real-time systems.
The term deadline can have one of two meanings. First, it can be a deadline rela-
tive to an event, such as a notification or message in some form. In this case, the
system must respond to that event within a certain amount of time of receiving
that event, or from when that event originally occurred. One example of a relative
deadline is an elevator as it passes over a sensor indicating that it’s almost at the
floor it’s meant to stop at. The real-time software within the elevator must respond
to that event within milliseconds of passing the sensor, or it won’t be able to stop
at the intended floor. The occupants of an elevator that skips stops are certain to
consider this an error.
Relative Deadline (Di ): the amount of time after a request is made that the sys-
tem needs to respond.
Absolute Deadline (di ): the precise point in time that a task must be completed,
regardless of task start time, or request arrival.
Often, with an absolute deadline, a real-time system checks for a particular sys-
tem state on a regular interval. Some examples of this are an aircraft flight control
system, or a nuclear power plant’s core temperature monitoring system. In both of
these cases, critical data is continuously polled, such as altitude, or core tempera-
ture. Failing to monitor these values at precise points in time can cause these
systems to go into a bad state with potentially catastrophic results.
Regardless of the type of deadline, relative or absolute, time is still a main compo-
nent in proper system behavior. It’s not enough that an elevator’s software knows
and responds to a floor sensor; it must do so within a deadline in order to behave
correctly. Also, a flight control system must be able to move an aircraft’s control
surfaces at the precise time, in reaction to the most recent and accurate set of data,
in order to fly the aircraft correctly (without crashing!).
For example, let’s say we have a system requirement to send a response to a
request within one millisecond. If the system responds within 500 microseconds
every time, you may think the requirement has been met. However, if the request
is delayed, outside the system under measurement, the response will not have
been sent at the right moment in time (even if it’s sent within one millisecond).
Remember, we’re talking about “real” time here; the one-millisecond require-
ment applies to when the originating system sent the original request.
Figure 1-1 illustrates the problem. Here you see that the system in question has
responded to the request within one millisecond, but it was at the wrong time
because the request was delayed in delivery. A real-time system must adhere to
the end-to-end deadline.
In a real-time system, the time delay from when a real-world event occurs (such as
an object passing over a sensor, or the arrival of a stock market data-feed tick) to
the time some code finishes processing that event should be reasonably bounded.
The ability to meet this deadline must be predictable and guaranteed, all the time,
in order to provide the determinism needed for a real-time system.
What Is “Bounded”?
When we use the term bounded in relation to a bounded amount of time, what we
really imply is a reasonable amount of time for the system to respond. In other
words, saying that the elevator responds to sensor events within a ten-year bounded
timeframe is unreasonable. It must do so according to a time requirement that
allows it to function properly. Therefore, when we use the term bounded, it’s
relative to the proper operation of the time-critical event we’re describing.
request latency
sent
Component 1
response
sent
Component 2
start .5 1
msec msec
Missed
deadline
Figure 1-1 The response time was good, but the deadline was missed. This is not a
real-time system.
Note: Jobs and Tasks in Real-Time Systems At this point in the discussion, it’s
fair to accurately define the terms job and task as used in discussions of real-time
scheduling theory. Formally speaking, a job is any unit of work that can be scheduled
and processed, while a task is a group of related jobs that work together to achieve
some function. In this classic definition, a task contains related jobs, where those jobs
have real-time constraints.
However, to keep the discussions light and simple in this book we will not distinguish
between tasks and jobs; a unit of schedulable work will simply be referred to as a task.
Therefore, in this book, a task represents a thread of execution and is synonymous
with an OS thread.
Regardless, discussions often revolve around the arrival of a system event, or the
start of task execution, which can sometimes be one and the same. To clarify, we
say that a task can be in one of the three main states:
With these task states defined, we can begin to discuss how tasks are scheduled in
a real-time system. First, the following definitions must be stated:
Release Time (ri ): sometimes called arrival time, or request time, this is the
time that a task becomes ready to execute.
Start Time (si ): the time that a task begins executing. As stated above, these
concepts may be combined for simplification in many discussions. For exam-
ple, a task may be started because of a request, or it may be started as part of a
predefined schedule. This book shall attempt to separate these concepts when
necessary to avoid confusion.
Finish Time ( fi ): the time when a task is complete.
Task Completion Time (Ci = fi − ri ): the amount of time a particular task takes
to complete its processing by subtracting the task’s arrival time from its finish
time. This is also referred to as the cost of task execution.
Lateness (Li ): the difference between the task finish time and its deadline; note
that this value is negative if a task completes before its deadline, zero if it com-
pletes at its deadline, and positive if it completes after its deadline.
These terms and their associated abbreviations will be used throughout the book.
To further clarify them, and to gain a better understanding of real-time systems,
let’s explore the factors that affect a system’s ability to meet its deadlines.
Identifying Latency
Much of the discussion so far has been about responding to an event before a
deadline. This is certainly a requirement of a real-time system. Latency is a
measure of the time between a particular event and a system’s response to that
event, and it’s quite often a focus for real-time developers. Because of this,
latency is often a key measurement in any real-time system. In particular, the
usual focus is to minimize system latency. However, in a real-time system
the true goal is simply to normalize latency, not minimize it. In other words, the
goal is to make latency a known, reasonably small, and consistent quantity that
can then be predicted. Whether the latency in question is measured in seconds,
or microseconds, the fact that it can be predicted is what truly matters to real-
time developers. Nonetheless, more often than not, real-time systems also
include the requirement that latency be minimized and bounded, often in the
sub-millisecond range.
To meet a system’s real-time requirements, all sources of latency must be
identified and measured. To do this, you need the support of your host system’s
operating system, its environment, network relationship, and programming
language.
Identifying Jitter
The definition of jitter includes the detection of irregular variations, or unsteadi-
ness, in some measured quantity. For example, in an electronic device, jitter
often refers to a fluctuation in an electrical signal. In a real-time system, jitter is
the fluctuation in latency for similar event processing. Simply measuring the
latency of message processing for one event, or averaging it over many events, is
not enough. For instance, if the average latency from request to response for a
certain web server is 250 milliseconds, we have no insight into jitter. If we look
at all of the numbers that go into the average (all of the individual request/
response round-trip times) we can begin to examine it. Instead, as a real-time
developer, you must look at the distribution and standard deviation of the
responses over time.
The chart in Figure 1-2 shows a sampling of latency data for a web server’s
request/response round-trip time. You can see that although the average of 250
milliseconds seems pleasing, a look at the individual numbers shows that some of
the responses were delivered with up to one-second latency. These “large” latency
System Throughput
3500
3000
Round-trip processing time (microseconds)
2500
2000
1500
1000
500
0
1 64 127 190 253 316 379 442 505 568 631 694 757 820 883 946
Message Number
Figure 1-2 The average response time measurement of a transaction can cover up
latency outliers.
responses stand out above most of the others, and are hence labeled outliers, since
they fall outside of the normal, or even acceptable, response time range.
However, if the system being measured is simply a web application without real-
time requirements, this chart should not be alarming; the outliers simply aren’t
important, as the average is acceptable. However, if the system were truly a real-
time system, these outliers could represent disaster. In a real-time system, every
response must be sent within a bounded amount of latency.
Isochronal Real-Time
In some cases, the requirement to respond to an event before a deadline is not
enough; it must not be sent too early either. In many control systems, responses
must be sent within a window of time after the request, and before the absolute
deadline (see Figure 1-3). Such a system has an isochronal real-time requirement.
Although clearly distinct from a hard real-time task that needs to complete any
time before its deadline, in most cases isochronal real-time tasks are simply clas-
sified as hard real-time with an additional timing constraint. This certainly makes
it easier to describe the tasks in a system design. However, this added constraint
does make a difference to the real-time task scheduler, which is something we’ll
explore later in this chapter.
Valid
response
window
Time
Task
ine
est
t
lies
adl
qu
Ear
Re
De
Figure 1-3 Isochronal real-time: the deadline must be met, but a response must not be
sent too early, either.
were some that waited the full second for their response. Because the degree of,
and the amount of, these outliers are unpredictable, high-throughput systems are
not necessarily real-time systems.
Typically, real-time systems exhibit lower average throughput than non-real-time
systems. This engineering trade-off is well known and accepted in the real-time
community. This is due to many factors that include trade-offs as to how tasks are
scheduled and how resources are allocated. We’ll explore these factors in relation
to Java RTS throughout this book.
me
a l-ti letion
e
n-r mp
Value
No k co
ta s
Time
Figure 1-4 In a non-real-time system, the perceived value of task completion is directly
proportional to the total number completed over time.
Value
Soft real-time
task completion
Time
ine
adl
De
Figure 1-5 In a soft real-time system, the value of task completion, after the deadline,
decays over time.
Value
Hard real-time
task completion
Time
ine
adl
De
Figure 1-6 The value of task completion in a hard real-time system is zero the moment
the deadline passes.
Isochronal real-time
task completion
Value
Time
ine
adl
De
Figure 1-7 The value of task completion in a firm, isochronal, real-time system is zero
if it completes early, or late.
The discussion so far assumes that task completion anytime before the deadline is
acceptable. In some cases, as with firm, or isochronal, real-time systems, the task
must complete before the deadline, but no earlier than a predefined value. In this
case, the value of task completion before and after the deadline is, or quickly goes
to, zero (see Figure 1-7).
Of course, these graphs are only general visual representations of task completion
value in non-real-time and real-time systems; actual value is derived on a case-by-
case basis. Later in this chapter, we’ll examine this in more detail as task cost
functions are used to calculate efficient real-time scheduling algorithms.
Real-Time Computing
Now that you have an understanding of what real-time is and what it means, it’s time
to expand on it. Real-time computing is the study and practice of building applica-
tions with real-world time-critical constraints. Real-time systems must respond to
external, often physical, real-world events at a certain time, or by a deadline. A real-
time system often includes both the hardware and the software in its entirety. Tradi-
tionally, real-time systems were purpose-built systems implemented for specific
use; it’s only recently that the real-time community has focused on general-purpose
computing systems (both hardware and/or software) to solve real-time problems.
Today, the need for specialized, dedicated, hardware for real-time systems has mostly
disappeared. For instance, modern chipsets include programmable interrupt control-
lers with latency resolution small enough for demanding real-time applications. As a
result, support for real-time requirements has moved to software; i.e., specialized
schedulers and resource controllers. Algorithms that were once etched into special
circuitry are now implemented in software on general-purpose computers.
This is not to say that hardware support isn’t needed in a real-time system. For exam-
ple, many real-time systems will likely require access to a programmable interrupt
controller for low-latency interrupts and scheduling, a high-resolution clock for pre-
cise timing, direct physical memory access, or a high-speed memory cache. Most
modern computer hardware, including servers, workstations, and even desktops and
laptops, support these requirements. The bottom line is whether the operating sys-
tem software running on this hardware supports access to these hardware facilities.
The operating system may, in fact, support real-time tasks directly through its
scheduling implementation, or may at least allow alternative scheduling algo-
rithms be put in place. However, many general-purpose operating systems sched-
ule tasks to achieve different goals than a real-time system. Other factors, such as
overall system throughput, foreground application performance, and GUI refresh
rates, may be favored over an individual task’s latency requirements. In fact, in a
general-purpose system, there may be no way to accurately specify or measure an
application’s latency requirements and actual results.
However, it is still possible to achieve real-time behavior, and meet real-time
tasks’ deadlines, on general-purpose operating systems. In fact, this is one of the
charter goals that Java RTS, and the RTSJ, set out to solve: real-time behavior in
Java on general-purpose hardware and real-time operating systems. In reality,
only a subset of general-purpose systems can be supported.
The remainder of this chapter provides an overview of the theory and mechanics
involved in scheduling tasks in a real-time system. To be clear, real-time scheduling
theory requires a great deal of math to describe and understand thoroughly. There is
good reason for this: when a system has requirements to meet every deadline for
actions that may have dire consequences if missed, you need to make assurances with
the utmost precision. Characterizing and guaranteeing system behavior with mathe-
matics is the only way to do it. However, we’ll attempt to discuss the subject without
overburdening you with deep mathematical concepts. Instead, analogies, descriptions,
and visuals will be used to bring the concepts down to earth, at a level where the aver-
age programmer should be comfortable. For those who are interested in the deeper
math and science of the subject, references to further reading material are provided.
t t 1 1... t 1n
Figure 1-8 As with cars on a highway, when there are more tasks executing, the system
slows down, and execution times become unpredictable.
progress over time. At moments when more tasks share the highway, the entire
system is considered to be busy, and usually all tasks will execute slower. This is
similar to the effects that high volumes of cars have on individual car speeds; they
each slow down as they share the highway. Since, in this scenario, all tasks share
the resources (the highway) equally, they are all impacted in a similar, but
unpredictable, way. It’s impossible to deterministically know when an individual
task will be able to complete.
In the real world, engineers designing road systems have come up with a solution
to this problem: a dedicated lane.
High-priority lane
very busy
t t 1 1... t 1n
Figure 1-9 Introducing a high-priority lane to a highway ensures that the cars in that
lane are less susceptible to traffic, and therefore travel more predictably towards their
destinations.
• Overall, the system loses throughput, as fewer lanes are available to execute
tasks.
• Tasks only enter the high-priority lane at certain checkpoints.
• Some system overhead is required at the checkpoints. Just as cars need to
cautiously (and slowly) enter and exit a carpool lane, tasks are slightly
impacted.
• Tasks may be denied access to the high-priority lane if their entry would
adversely affect the other tasks already running.
Additionally, metering lights are used at the on-ramps to many highways. These
lights control the flow of additional cars (analogy: new tasks) onto the highway to
ensure the cars already on the highway are impacted as little as possible. These
lights are analogous to the admission control algorithm of the scheduler in real-
time system.
Most importantly, this analogy shows that there’s no magic involved in supporting
a real-time system; it, too, has its limits. For instance, there’s a limit to the number
of high-priority tasks that can execute and meet their deadlines without causing
all tasks to miss their deadlines. Also, because of the need to dedicate resources to
real-time tasks, the added checkpoints for acceptance of real-time tasks, the need
to more tightly control access to shared resources, and the need to perform addi-
tional task monitoring; the system as a whole will assuredly lose some perfor-
mance and/or throughput. However, in a real-time system, predictability trumps
throughput, which can be recovered by other, less-complicated, means.
As we explore the details of common scheduling algorithms used in actual real-
time systems, you will also see that simply “adding more lanes” doesn’t always
resolve the problem effectively. There are practical limits to any solution. In fact,
in some cases that we’ll explore, adding processors to a computer can cause previ-
ously feasible schedules to become infeasible. Task scheduling involves many
system dynamics, where the varying combination of tasks and available resources
at different points in time represents a difficult problem to solve deterministically.
However, it can be done. Let’s begin to explore some of the common algorithms
used, and the constraints they deal with.
Real-Time Scheduling
A task by itself represents a useless body of instructions. For a task to be useful
and meaningful, it must be processed, and it therefore must have scheduled exe-
cution time on a processor. The act of scheduling processor time to a task, or
Scheduling Constraints
Many factors, or constraints, are taken into account when scheduling real-time
tasks. Because each application—and hence each task—is different, these con-
straints may vary from system to system. However, they all generally fall into a
subset of constraints that need to be considered. The first constraint is the amount
of available resources; whether they’re tasks in a computer or construction work-
ers on a job, having what you need to complete a task is important. The second
constraint is task precedence; to avoid chaos, and to ensure proper system behav-
ior, certain tasks may need to be executed before others. The third constraint is
timing; each task has its own deadline, some tasks execute longer than others,
some may execute on a steady period, and others may vary between release times.
Each constraint contains many of its own factors that need to be considered fur-
ther when scheduling tasks. Let’s examine these constraints in more detail now,
and how they may affect task scheduling.
Resources
Because we speak in terms of processors, tasks, and execution times, most people
think of only the CPU as the main resource to be scheduled. This isn’t always the
case. For example, recalling the highway analogy above, cars represented tasks,
and the road represented the processor. More realistically, in a real-time network
packet switching system, tasks represent data packets, and the processor repre-
sents an available communication line. Similarly, in a real-time file system, a task
is a file, and the processor is an individual disk platter/head combination. How-
ever, regardless of the actual physical work being done (sending a packet, writing
a file, or executing a thread), we will refer to all of them as simply a task. Further,
regardless of the actual component doing the work (a network card/communica-
tion link, a disk controller, or a CPU), we will refer to all of them as simply a
processor.
It’s easy to see how the availability of critical system resources, such as the pro-
cessor, is important to a scheduling algorithm. After all, to execute a thread, there
must be a CPU available to execute it. However, it goes beyond just the processor;
other resources, such as shared objects that require synchronization across tasks,
Task T1 blocks
on resource R1
T1
T2 R1
T3
1 2 3 4 5 Time
⫽ preempted
Figure 1-10 Resource locking can lead to priority inversion. In a real-time system, this
scenario must be controlled, or avoided.
Precedence
In many systems, real-time systems included, tasks cannot be scheduled in arbi-
trary order. There is often a precedence of events that govern which threads must
be scheduled first. Typically, the threads themselves imply the ordering through
resource sharing, or some form of communication, such as a locking mechanism.
One example is a system where a task T1 must wait for another task T2 to release
a lock on an object before it can begin execution. This act of task synchronization
must be performed at the OS kernel level so as to notify the scheduler of the task
precedence that exists.
In the example above the scheduler will block task T1, and will allow task T2 to
execute. When task T2 completes its processing and releases its lock on the syn-
chronized object that the two tasks share, the scheduler can dispatch task T1. The
task precedence dictated by the synchronization in this example must be obeyed
even if there are other processors in the system that are ready to execute waiting
tasks. Therefore, in this example, regardless of the number of available proces-
sors, task T1 will always wait for task T2 to complete and hence release task T1 to
begin.
Notation: T2 < T1, or T2 → T1 for immediate task precedence
Task precedence can get complex, as a system with multiple tasks (each with its
own dependencies) must be scheduled according to the precedence rules. Real-
time system designers must understand task precedence fully before a system
begins execution to ensure that real-time deadlines can be met. To do this, prece-
dence relationships can be predetermined and represented by a graph, such as the
one shown in Figure 1-11.
In this diagram, each node on the graph represents a task. The nodes at the top
must execute and complete first before threads below them can execute. These
rules are repeated at each level of the graph. For instance, the scheduler for the
tasks represented in Figure 1-11 will begin by dispatching task T2 to execute first.
Once T2 is complete, task T1 will execute, while tasks T3 and T4 are blocked.
T ⫽ thread T2
Thread # ⫽ order of creation
Order of creation ! ⫽ order of precedence Order
of
Precedence
T1
T3 T4
Timing
Real-time tasks are labeled as such because they have time-related constraints,
usually to do with a deadline for processing. As a result, schedulers are concerned
with many time-related parameters to ensure that a task can complete on or before
its deadline. For instance, schedulers need to consider the following, per task:
Lateness: the difference between a task’s actual completion time, and its
deadline.
Tardiness: the amount of time a task executes after its deadline. This is not
always equal to the lateness value, as a task may have been preempted by
another task, which caused it to miss its deadline.
Laxity: the amount of time after its release time that a task can be delayed with-
out missing its deadline.
Slack Time: same as laxity (above). However, this term is sometimes used to
describe the amount of time a task can be preempted during its execution and
still meet its deadline.
Scheduling Algorithms
The three constraint sets above are directly used in scheduling algorithms to deter-
mine a feasible schedule for a set of real-time tasks. Feeding into the algorithm as
parameters are the set of tasks, T, to be scheduled; the set of processors, P, available
to execute them; and the set of resources, R, available for all tasks in the system.
Scheduling algorithms differ in their criterion in determining when, and for how
long, a task can execute on a processor. Real-time scheduling algorithms exist to
ensure that regardless of system load—or the amount of threads eligible to be
dispatched—the time-critical tasks of the system get ample processing time, at
the right time, to meet their deadlines. Let’s examine some of the high-level char-
acteristics of real-time schedulers.
multi-processor system, this hybrid approach works well, since relatively long-
running non-preemptive tasks do not prevent the system from performing other
tasks, as long as there are more processors available than real-time tasks. The
advantage to the real-time programmer is increased control over system behavior,
which results in a more deterministic system overall.
Context Switching
In addition to the added determinism it provides, another reason to choose a non-
preemptive scheduler is to avoid unforeseen context switches, which occur when
one thread preempts another thread that’s already running. The operating system
must expend processor time executing code that saves the state of the already run-
ning thread, and then resets internal data structures and processor registers to
begin execution of the preempting thread so that it can begin execution. This
entire preemption process is summarized as dispatching, as mentioned earlier in
this chapter. The time it takes a particular system to perform this preemption task
is called dispatch latency, and can interfere with a system’s ability to respond to
an event by its deadline.
As a system becomes increasingly busy, with more threads competing for pro-
cessing time, a considerable percentage of time can be spent dispatching threads,
resulting in an additive effect in terms of dispatch latency. A system that spends
more time dispatching threads than actually performing real work is said to be
thrashing. Real-time systems must guarantee that even under high system load,
thrashing due to context switches will not occur, or that it at least won’t interfere
with the high-priority threads performing real-time processing in the system. OS
kernels with hybrid preemption/non-preemption support are useful in these cases,
allowing real-time processing to be guaranteed on a general-purpose system.
state of the system and its current set of tasks. For instance, a static scheduling
algorithm may assign a priority to a new task based on its period (defined below)
relative to the periods of the other tasks in the system. In this way, a static schedul-
ing algorithm can be used either offline or online (while the system is running).
Task Migration
Another important parameter to real-time task scheduling is the ability of a task to
be executed by different processors in a multiprocessor system. For instance, in
some systems, once a task begins execution on a particular processor, it cannot
migrate to another processor even if it’s preempted, and another processor is idle.
In this type of system, task migration is not allowed.
However, many systems do allow tasks to migrate between available processors
as tasks are preempted, and different processors become available. This type of
system supports dynamic task migration, as there are no restrictions placed on
tasks in relation to processors. Of course, there are systems that fall in between,
which support restricted task migration. One example is a system with many pro-
cessors, where certain tasks are restricted to a subset of total processors within the
system. The remaining processors may be dedicated to a task, or small set of tasks,
for example. Another example might be that on multiprocessor/multi-board
systems, the OS might limit task migration to CPUs on the same board to take
advantage of locality of reference.
An example of a common real-time operating system that supports restricted
task migration is Solaris. Solaris allows you to define individual processor sets,
assign physical processors (or processor cores) to the defined sets, and then
dedicate a processor set to a particular application. That processor set will then
execute only the threads (tasks) within that application, and no others. The tasks
will be able to migrate across the physical processors within the set, but not the
others.
In comparison, for a periodic task, the length of the time interval between any two
adjacent releases is always constant:
This function returns the value 1, indicating true, if the task’s finish time exceeds
its absolute deadline, or if the task’s completion time exceeds its relative dead-
line, depending upon the type of deadline the task has.
n
Number of late tasks, Nlate = ∑ late(ti )
i =1
This function calculates the total number of tasks that miss their deadline in a
given real-time system with n tasks. Note: To simplify the equations and the
discussion going forward, let’s define a task’s deadline, di, where di equals its
absolute deadline di when a task has an absolute deadline, or di = ai + Di when a
task has a relative deadline.
Maximum lateness, Lmax = max(fi − di ): this function calculates the task with
the maximum lateness (missed its deadline by the greatest amount of time)
using di as we’ve just defined it. Note that this value can be negative (when all
tasks finish before their deadlines), or positive.
1⎛ n ⎞
Average response time, Ravg = ∑ Ci
n ⎜⎝ i =1 ⎟⎠
This function calculates the average response time for a given real-time system
by dividing the sum of all task completion times by the total number of tasks in
the system.
their deadlines as a result. Often, these algorithms will err on the side of not
allowing a new task to start if there is a chance it can disrupt the system. This is
done to avoid a domino effect, where the introduction of a new task causes all
existing tasks to miss their deadlines. This pessimistic approach can some-
times mean that tasks may be blocked from starting that would not have caused
a problem in reality.
Best-Effort Based: these algorithms are dynamic, more optimistic, and less
conservative, when it comes to new task arrival. These schedulers are typically
used in systems with soft real-time constraints, such that a missed deadline due
to new task arrival is generally tolerable. They are classified a “best-effort”
because these schedulers almost always allow new tasks into the system, and
will do their best to ensure that all tasks complete their processing on or close
to their deadlines. This results in a very responsive, efficient, real-time system
that is best suited for soft real-time tasks where hard guarantees are not
needed.
Within both classifications, the different algorithms must be one of the following
to be considered as real-time:
To achieve a feasible schedule for tasks in a hard real-time system, there are three
common approaches often used. These are:
In some systems, such as those that run both real-time and general-purpose appli-
cations together, a scheduling algorithm is used in conjunction with a partitioning
scheme to achieve optimal system behavior. With this approach, tasks are parti-
tioned, or grouped, based upon certain criteria. The partitioning scheme can be
either fixed, or adaptive:
The intent of the Java Real-Time System is to hide this complexity from you.
However, to truly appreciate its implementation, and to understand why your Java
applications behave as they do within it, you should at least have a cursory under-
standing of the theory behind its implementation.
Both the RTSJ and Sun’s Java RTS are meant to provide real-time behavior to Java
applications even on general-purpose hardware with a real-time OS. However, this
is a deviation from what has been classically accepted as a real-time system. In the
past, you had to write code in a language designed for real-time applications and
use dedicated, special-purpose hardware to run them on. Again, to gain a true
appreciation of the real-time space, and the problems that Java RTS had to over-
come, let’s take a brief look at some common real-time languages and operating
systems.
Processor 1 Processor 2
C1 T1 C1 T2
C2 -- C2 --
C ⫽ core
T ⫽ thread
Figure 1-12 The Solaris kernel attempts to balance threads across CPUs to help
with heat dissipation.
continued
Processor 1 Processor 2
C1 T1 C3 T5 C1 T2 C3 T6
Processor 3 Processor 4
C1 T3 C3 T7 C1 T4 C3 T8
C ⫽ core
T ⫽ thread
Figure 1-13 Solaris kernel threads spread equally across all available cores.
From this point onward, additional threads will be scheduled on the next
available (free) core.
Many real-time operating systems target embedded systems with dedicated func-
tionality and limited hardware resources. This means that these systems are not
meant to be general-purpose systems, and using a real-time OS proves to be a
valuable tool in these cases. However, as mentioned before in this chapter, with
the continuing advances in hardware capabilities at lower cost, the need for spe-
cialized hardware and operating systems has diminished significantly. A dedicated
real-time OS comes at a high cost relative to the general-purpose operating sys-
tems available today. Many argue that the state of the industry has reached a point
that this extra cost is no longer justified. However, recall that while adding addi-
tional, more powerful, hardware can help improve raw performance and through-
put, it almost never makes an unpredictable system behave predictably.
Regardless, there are instances, such as with embedded systems, where a real-time
OS continues to be the only option. While this book does not go into detail regard-
ing real-time operating systems, it’s important to know that they do exist and serve
a specialized purpose. Real-time applications typically require the same set of ser-
vices from the OS—such as disk IO, networking support, a file system, user IO,
and so on—as general purpose applications do. However, the real-time applica-
tion requires its OS to make guarantees that are both measurable and predictable.
Preemption: true task preemption must be supported using task priority rules
that are strictly obeyed.
Priority Inversion Control: although it cannot guard well against deadlocks,
priority inheritance ensures that lower-priority threads will never be able to
block higher-priority threads due to classic priority inversion.
Periodic, Aperiodic, and Sporadic Threads: the POSIX standard requires only
processes be implemented. For real-time applications, threads of different
priority may need to run to perform a task. For this reason, the RT-POSIX stan-
dard requires the OS be able to schedule tasks down to the thread level, and that
applications be able to create and destroy those threads. Underlying OS threads
must be able to support the various types of task release events common to
real-time applications. For instance, a thread should be able to specify its period
and then rely on the OS to wake it up at precise time boundaries that match that
period. Simply performing a call to “sleep” from within the task’s code (as is
done in a non-real-time OS) is not sufficient.
High-Resolution Timers: such timers should be made available to real-time
applications, as well as the real-time OS itself so that it can dispatch threads
with as little latency and jitter as possible. For example, an OS scheduler with a
10-millisecond tick size will, at best, experience up to 10 milliseconds latency
during thread dispatch processing. In general, the larger the tick count, the
higher the max latency and jitter values will grow. Both are qualities to be
avoided in real-time systems. The RT-POSIX standard states that up to 32 timers
per process must be supported, and that timer overruns (when a timer goes
beyond its chosen duration) be recorded.
Schedulers: the kernel needs to support both deadline-monotonic and rate-
monotonic deadline scheduling in order to support the common real-time
scheduling algorithms such as weighted round-robin, fixed-priority, and
earliest-deadline-first scheduling. The type of scheduler used can be defined
down to the thread level, where two threads of the same process may be sched-
uled differently.
Scheduled Interrupt Handling: the ability to create a preemptable task that
processes low lever device interrupts. This is sometimes called a software inter-
rupt. In contrast, a general-purpose OS typically handles these events com-
pletely itself, making the data or status available through some other means.
Synchronous and Asynchronous IO: synchronous IO operations provide real-
time tasks more control over IO-related tasks, such as file and network opera-
tions. Asynchronous IO allows the system to progress task execution while IO
is occurring, or waiting to occur. For instance, a task that reads a network packet
can process the packet’s payload even while it waits for the next packet to
arrive.
Inter-Task Communication: to facilitate predictable, low-latency, communi-
cation between tasks, queues should be provided at the OS level. This also
ensures fast, consistent, and measurable performance, with support for mes-
sage prioritization. These queues are sometimes made available to tasks both
local to the system, and remote. Regardless, message delivery must be priori-
tized, and at least eight prioritized signals are supported per process.
Further Reading
Much of the information for this chapter was gathered from papers and texts on
the subject of real-time systems and scheduling theory. For readers who are inter-
ested, below is a list of reading material that will help build a complete foundation
for real-time theory and practice:
[Buttazzo05] Buttazzo, Georgia C., Hard Real-Time Computing Systems.
Springer, 2005.
[Klein93] Klein, Mark, et al., A Practitioner’s Guide to Real-Time Analysis.
Kluwer Academic Publishers, 1993.
[Layland73] Liu, C.L. and Layland, James W., Scheduling Algorithms for Multi-
programming in a Hard Real-Time Environment. Journal of the ACM, 1973
(Available at http://portal.acm.org/citation.cfm?id=321743).
[Liu00] Liu, Jane W. S., Real-Time Systems. Prentice Hall, 2000.
[McDougall07] Mauro, Jim, McDougall, Richard, Solaris Internals. Prentice
Hall, 2007.
Leading iPhone developer Erica Sadun begins by exploring the iPhone delivery
platform and SDK, helping you set up your development environment, and showing About the Author
how iPhone applications are constructed. Next, she offers single-task recipes for the
Erica Sadun has written, coauthored,
full spectrum of iPhone/iPod touch programming jobs:
and contributed to about three dozen
books about technology, particularly
• Utilize views and tables
in the areas of programming, digital
• Organize interface elements
video, and digital photography. An
• Alert and respond to users
unrepentant geek, Sadun has never met
• Access the Address Book (people), Core Location (places), and Sensors (things)
a gadget she didn’t need. Her checkered
• Connect to the Internet and Web services
past includes run-ins with NeXT,
• Display media content
Newton, iPhone, and myriad successful
• Create secure Keychain entries
and unsuccessful technologies. When
• And much more
not writing, she and her geek husband
parent three adorable geeks-in-training,
You’ll even discover how to use Cover Flow to create gorgeous visual selection
who regard their parents with restrained
experiences that put scrolling lists to shame!
bemusement.
This book is organized for fast access: related tasks are grouped together, and you can
jump directly to the right solution, even if you don’t know which class or framework to
use. All code is based on Apple’s publicly released iPhone SDK, not a beta. No matter
what iPhone projects come your way, The iPhone Developer’s Cookbook will be
your indispensable companion.
informit.com/aw
2
Views
P retty much everything that appears on the iPhone’s screen is a view.Views act like
little canvases that you can draw on with colors, pictures, and buttons.You can drag them
around the screen.You can resize them.You can layer them. In this chapter, you discover
how to design and build screen content using Cocoa Touch and UIViews.You learn
about view hierarchy, geometry, and animation, and find out how to combine event
feedback from UITouches into meaningful UIView responses.There’s so much that
UIViews can do that a single chapter has no hope of covering the entire class with the
thoroughness it deserves. Instead, this chapter introduces essential functionality and
recipes that you can use as a starting point for your own UIView exploration.
Hierarchy
A tree-based hierarchy orders what you see on your iPhone screen. Starting with the
main window, views are laid out in a specifically hierarchical way. All views may have
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 38
38 Chapter 2 Views
children, called subviews. Each view, including the root window, owns an ordered list of
these subviews.Views might own many subviews; they might own none.Your application
determines how views are laid out and who owns whom.
Subviews display onscreen in order, always from back to front. And because the iPhone
supports view transparency, this works exactly like a stack of animation cells—those trans-
parent sheets used to create cartoons. Only the parts of the sheets that have been painted
are shown.The clear parts allow any visual elements behind that sheet to be seen.
Figure 2-1 shows a little of the layering used in a typical window. Here you see the
window that owns a UINavigationController-based window.The window (repre-
sented by the clear, rightmost element) owns a Navigation subview, which in turn owns
two subview buttons (one left and one right) and a table.These items stack together to
build the GUI.
Notice how the buttons appear over the navigation bar and how the table is sized so
that it won’t obscure either the buttons or bar.The button frames are small, taking up
very little space onscreen.The table frame is large, occupying the majority of screen
space. Here are some ways you can manage subviews in your programs:
n To add a subview, use a call to [parentView addSubview:child]. Newly added
subviews are always frontmost on your screen.
n Query any view for its children by asking it for [parentView subviews].This
returns an array of views, ordered from back to front.
02_0321555457_ch02.qxd 10/23/08 1:22 PM Page 39
Note
You can tag any instance that is a child of UIView, including windows and controls. So if
you have many onscreen buttons and switches, for example, add tags so that you can tell
them apart when users trigger them.
CGRect
The CGRect structure defines an onscreen rectangle. It contains an origin (rect.origin)
and a size (rect.size).These are CGRect functions you’ll want to be aware of:
n CGRectMake(origin.x, origin.y, size.width, size.height) defines
rectangles in your code.
n NSStringFromCGRect(someCGRect) converts a CGRect structure to a formatted
string.
n CGRectFromString(aString) recovers a rectangle from its string representation.
n CGRectInset(aRect) enables you to create a smaller or larger a rectangle that’s
centered on the same point. Use a positive inset for smaller rectangles, negative for
larger ones.
n CGRectIntersectsRect(rect1, rect2) lets you know whether rectangle
structures intersect. Use this function to know when two rectangular onscreen
objects overlap.
n CGRectZero is a rectangle constant located at (0,0) whose width and height are
zero.You can use this constant when you’re required to create a frame but you’re still
unsure what that frame size or location will be at the time of creation.
40 Chapter 2 Views
sizes. Although these two structures appear to be the same (two floating-point values),
the iPhone SDK differentiates between them. Points refer to locations. Sizes refer to
extents.You cannot set myFrame.origin to a size.
As with rectangles, you can convert them to and from strings:
NSStringFromCGPoint(), NSStringFromCGSize(), CGSizeFromString(), and
CGPointFromString() perform these functions.
Defining Locations
You can define a view’s location by setting its center (which is a CGPoint) or bounds
(CGRect). Unlike the frame, a view’s bounds reflect the view’s frame in its own coordi-
nate system. In practical terms, that means the origin of the bounds is (0.0, 0.0), and its
size is its width and height.
When you want to move or resize a view, update its frame’s origin, center, or size.You
don’t need to worry about things such as rectangular sections that have been exposed or
hidden.The iPhone takes care of the redrawing.This lets you treat your views like tangi-
ble objects and delegate the rendering issues to Cocoa Touch. For example
[myView setFrame:CGRectMake(0.0f, 50.0f, mywidth, myheight)];
Transforms
Standard Core Graphics calls transform views in real time. For example, you can apply
clipping, rotation, or other 2D geometric effects. Cocoa Touch supports an entire suite of
affine transforms (translate, rotate, scale, skew, and so on).The drawRect: method for
any UIView subclass provides the entry point for drawing views through low-level Core
Graphics calls.
Note
When calling Core Graphics functions, keep in mind that Quartz lays out its coordinate
system from the bottom left, whereas UIViews have their origin at the top left.
View Layout
Figure 2-2 shows the layout of a typical iPhone application screen. For current releases of
the iPhone, the screen size is 320x480 pixels in portrait mode, 480x320 pixels in landscape.
At the top of the screen, whether in landscape or portrait mode, a standard status bar
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 41
occupies 20 pixels of height.To query the status bar frame, call [[UIApplication
sharedApplication] statusBarFrame].
If you’d rather free up those 20 pixels of screen space for other use, you can hide the
status bar entirely. Use this UIApplication call: [UIApplication sharedApplication]
setStatusBarHidden:YES animated:NO].Alternatively, set the UIStatusBarHidden
key to <true/> in your application Info.plist file.
To run your application in landscape-only mode, set the status bar orientation to
landscape. Do this even if you plan to hide the status bar (that is, [[UIApplication
sharedApplication] setStatusBarOrientation:
UIInterfaceOrientationLandscapeRight]).This forces windows to display side to
side and produces a proper landscape keyboard.
The UIScreen object acts as a stand in for the iPhone’s physical screen ([UIScreen
mainScreen]).The screen object maps view layout boundaries into pixel space. It
returns either the full screen size (bounds) or just the rectangle that applies to your
application (applicationFrame).This latter takes the size of your status bar and, if used,
any toolbars/navigation bars into account.
By default, UINavigationBar, UIToolbar, and UITabBar objects are 44 pixels in
height each. Use these numbers to calculate the available space on your iPhone screen
and lay out your application views when not using Interface Builder’s layout tools.
42 Chapter 2 Views
Gestures
Views intercept user touches.This integration between the way things look and the way
things react enables you to add a meaningful response to taps, drags, and what have you.
Adding touch handlers like touchesBegan: withEvent: to your views allows you to
intercept user touches, determine the phase of the touch (the equivalent of mouse down,
mouse dragged, mouse up), and produce feedback based on those touches.
The UITouch class tells you where the event took place (locationInView:) and the
tap count (tapCount), which is vital for distinguishing between single- and double-taps.
Several recipes in this chapter demonstrate how to use these gesture responses and how
to integrate view geometry and hierarchy into your applications for enticing, layered,
direct-manipulation interfaces.
// reset the origin point for subviews. The new origin is 0,0
appRect.origin = CGPointMake(0.0f, 0.0f);
44 Chapter 2 Views
Reorienting
Extending Recipe 2-1 to enable orientation changes takes thought.You cannot just swap
each subview’s heights and widths as you might assume.That’s because the shapes of
horizontal and vertical applications on the iPhone use different aspect ratios. Assuming a
20-pixel status bar, portrait view areas are 320 pixels wide by 460 pixels high; landscapes
are 480 pixels wide by 300 pixels high (refer to Figure 2-2).This difference throws off
interfaces that depend solely on rotation to reorient.
To rotate this example, add code that distinguishes landscape orientations from portrait
ones and adjust the frames accordingly.This is shown in Recipe 2-2.
Avoid reorientation schemes that rely on toggling (for example, “I was just in portrait
mode so, if the orientation changed, I must be in landscape mode.”) It’s entirely possible
to switch from left-landscape to right-landscape without hitting a portrait state in-
between. Orientation is all about sensors and feedback, and the iPhone is not guaranteed
to catch any middle state between two orientations. Fortunately, UIKit provides a
UIViewController callback that alerts you to new orientations and that specifies what
that orientation will be.
CGRect apprect;
apprect.origin = CGPointMake(0.0f, 0.0f);
46 Chapter 2 Views
Note
The way the example in Figure 2-4 is built, you can use multiple fingers to drag more than
one flower around the screen at once. It’s not multitouch per se because each flower
(UIView) responds to only one touch at a time. A discussion of true multitouch interaction
follows later in this chapter.
Touching an object also does one more thing in this code: It pops that object to the
front of the parent view.This means any dragged object always floats over any other
object onscreen. Do this by telling the view’s parent (its superview) to bring the view
to its front.
UITouch
The UITouch class defines how fingers move across the iPhone screen.Touches are sent
while invoking the standard began, moved, and ended handlers.You can also query user
events (of the UIEvent class) to return touches affecting a given view through
touchesForView: and touchesForWindow:.These calls return an unordered set
(NSSet) of touches.
Note
Send allObjects to any NSSet to return an array of those objects.
A touch tells you several things: where the touch took place (both the current and
most recent previous location), what stage of the touch was used (essentially mouse
down, mouse moved, mouse up), a tap count (for example, single-tap/double-tap), when
the touch took place (through a time stamp), and so forth.
For nonmultitouch interaction styles, assume that you’re dealing with a single touch
at any time.The code in Recipe 2-3 recovers the first available touch for each event by
calling anyObject on the returned touch set.
@implementation DragView
// Note the touch point and bring the touched view to the front
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 47
/*
* Hello Controller: The primary view controller
*/
@implementation HelloController
#define MAXFLOWERS 16
- (void)loadView
{
// Create the main view with a black background
CGRect apprect = [[UIScreen mainScreen] applicationFrame];
contentView = [[UIView alloc] initWithFrame:apprect];
contentView.backgroundColor = [UIColor blackColor];
self.view = contentView;
[contentView release];
48 Chapter 2 Views
-(void) dealloc
{
[contentView release];
[super dealloc];
}
@end
Adding Persistence
Persistence represents a key iPhone design touch point. After users leave a program,
Apple strongly recommends that they return to a state that matches as closely to where
they left off as possible. Adding persistence to this sample code involves several steps:
1. Storing the data
2. Resuming from a saved session
3. Providing a startup image that matches the last session
Storing State
Every view knows its position because you can query its frame.This enables you to
recover and store positions for each onscreen flower.The flower type (green, pink, or
blue) is another matter. For each view to report its current flower, the DragView class
must store that value, too. Adding a string instance variable enables the view to return
the image name used. Listing 2-1 shows the extended DragView class definition.
Listing 2-1 The Updated DragView Class Includes a String to Store the Flower Type
@interface DragView : UIImageView
{
CGPoint startLocation;
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 49
Adding this extra variable enables the HelloController class to store both a list of
colors and a list of locations to its defaults file. A simple loop collects both values from
each draggable view and then stores them. Listing 2-2 presents an updateDefaults
method, as defined in HelloController.This method saves the current state to disk. It
should be called in the application delegate’s applicationWillTerminate: method,
just before the program ends.
Notice the use here of NSStringFromCGRect(). It provides a tight way to store
frame information as a string.To recover the rectangle, issue CGRectFromString().
Each call takes one argument: a CGRect in the first case, an NSString * in the second.
The UIKit framework provides calls that translate points and sizes as well as rectangles to
and from strings.
Defaults, as you can see, work like a dictionary. Just assign an object to a key and the
iPhone “automagically” updates the preferences file associated with your application ID.
Your application ID is defined in Info.plist. Defaults are stored in Library/Preferences
inside your application’s sandbox. Calling the synchronize function updates those
defaults immediately instead of waiting for the program to terminate.
50 Chapter 2 Views
Recovering State
Persistence awareness generally resides in the view controller’s init or loadView (for
example, before the view actually appears).These methods should find any previous state
information and, for this example, match the flowers to that state.When querying user
defaults, this code checks whether state data is unavailable (for example, the value
returned is nil).When state data goes missing, the method creates random flowers at ran-
dom points. Listing 2-3 shows a state-aware version of loadView.
Note
When working with large data sources, you may want to initialize and populate your saved
object array in the UIViewController‘s init method, and then draw them in loadView.
Where possible, use threading when working with many objects to avoid blocking.
Startup Image
Apple has not yet included persistence screenshot capabilities into its official SDK
release, although the functionality is partially available in the UIKit framework as an
undocumented call.To access the _writeApplicationSnapshot feature shown in
Listing 2-4, you must add it by hand to the UIApplicationClass interface. Once
added, you can build a cached shot of your screen before ending the application.
Note
See Chapter 1, “Introducing the iPhone SDK,” for further discussion about using
undocumented calls and features in your programs.
The idea is this:When you leave the application, you snap a picture of the screen.
Then when your application starts up (presumably returning you to the same state you
left with), the cached image acts as the Default.png image, giving the illusion that you’re
jumping directly back without any startup sequence.
Apple has yet to enable this feature with the iPhone SDK, and at the time of writing,
applications cannot check in to find updated snapshots. Hopefully, Apple will provide
this functionality in a future firmware release.
52 Chapter 2 Views
you would on a Macintosh. Core Graphics enables you to build paths from sources
including points, lines, standard shapes (such as ellipses), and Bézier curves. Clipping your
views to these paths creates the illusion of nonrectangular onscreen objects. Figure 2-5
shows a number of onscreen circular clipped views, clearly overlapping with each other.
These views were created by the code shown in Listing 2-5.This code creates a path,
performs the clipping, and then draws into the clipped view.
54 Chapter 2 Views
Note
The code in this listing returns a bitmap context, and its bitmap data is based on Apple
sample code.
if (colorSpace == NULL)
{
fprintf(stderr, "Error allocating color space\n");
return NULL;
}
return data;
}
// Create an Image View that stores a copy of its image as an addressable bitmap
@interface BitMapView : UIImageView
{
unsigned char *bitmap;
CGSize size;
UIView *colorView;
}
@end
@implementation BitMapView
56 Chapter 2 Views
Note
The drawRect: routine in Recipe 2-4 clears its context each time it is called. This
removes previous circles and lines from the display. Comment out this line if you want to
see an event trail.
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 57
@implementation MultiTouchView
@synthesize loc1;
@synthesize loc2;
58 Chapter 2 Views
// circle point 1
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectInset(p1box, offset, offset));
CGContextAddPath(context, path);
CGContextFillPath(context);
CFRelease(path);
// circle point 2
path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectInset(p2box, offset, offset));
CGContextAddPath(context, path);
CGContextFillPath(context);
CFRelease(path);
}
@end
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 59
UIView Animations 59
Note
Apple provides many Core Graphics/Quartz 2D resources on its developer Web site.
Although these forums, mailing lists, and source code samples are not iPhone specific,
they offer an invaluable resource for expanding your iPhone Core Graphics knowledge.
UIView Animations
UIView animation provides one of the odd but lovely perks of working with the iPhone
as a development platform. It enables you to slow down changes when updating views,
producing smooth animated results that enhance the user experience. Best of all, this all
occurs without you having to do much work.
UIView animations are perfect for building a visual bridge between a view’s current
and changed states.With them, you emphasize visual change and create an animation
that links those changes together. Animatable changes include the following:
n Changes in location—moving a view around the screen
n Changes in size—updating the view’s frame
n Changes in transparency—altering the view’s alpha value
n Changes in rotation or any other affine transforms that you apply to a view
60 Chapter 2 Views
Sandwich your actual view change commands after setting up the animation details
and before ending the animation. Listing 2-8 shows UIView animations in action by set-
ting an animation curve and the animation duration (here, one second).The actual
change being animated is a transparency update.The alpha value of the content view
goes to zero, making it invisible. Instead of the view simply disappearing, this animation
block slows down the change and fades it out of sight.
Note
Apple often uses two animation blocks one after another to add bounce to their anima-
tions. For example, they might zoom into a view a bit more than needed and then use a
second animation to bring that enlarged view down to its final size. Use “bounces” to add
a little more life to your animation blocks. Be sure that the animations do not overlap.
Either add a delay so that the second animation does not start until the first ends
(performSelector: withObject: afterDelay:) or assign an animation delegate
callback (animationDidStop: finished:) to catch the end of the first animation and
start the second.
n Make sure to catch only mouse down events. For simple on-off-on-off toggles,
catch and respond only to presses for the most natural user feedback. Otherwise,
user taps will hide and then immediately show your image view again.
n Pick a reasonable animation time. If you lengthen the animation beyond what your
user is willing to handle, you’ll end up handling new taps before the first anima-
tion has completed.The one-second animation shown here is just about the
longest time you’ll want to use. Half- or quarter-second animations are better for
common interface changes.
@implementation ToggleView
- (id) initWithFrame: (CGRect) aFrame;
{
self = [super initWithFrame:aFrame];
isVisible = YES;
imgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen]
➥applicationFrame]];
[imgView setImage:[UIImage imageNamed:@"alphablend.png"]];
imgView.userInteractionEnabled = NO;
[self addSubview:imgView];
[imgView release];
return self;
}
isVisible = !isVisible;
62 Chapter 2 Views
@implementation ToggleView
[big setFrame:SMALLRECT];
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 64
64 Chapter 2 Views
[UIView commitAnimations];
Note
During the SDK beta period, Apple promised additional animations that were never
realized, specifically UIViewAnimationTransitionCurlUp and
UIViewAnimationTransitionCurlDown. These extra animations may appear at some
future time.
To use transitions in UIView animation blocks, you need to do two things. First, you
must add the transition as a block parameter. Use setAnimationTransition: to assign
the transition to the enclosing UIView animation block. Second, you should rearrange
the view order while inside the block.This is best done with
exchangeSubviewAtIndex: withSubviewAtIndex:. Recipe 2-7 demonstrates how
to create a simple flip view using these techniques.When tapped, the views use the ani-
mation to flip from one side to the next, as shown in Figure 2-8.
Do not confuse the UIView animation blocks with the Core Animation
CATransition class. Unfortunately, you cannot assign a CATransition to your UIView
animation.To use a CATransition, you must apply it to a UIView’s layer, which is
shown in the next recipe.
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 65
@implementation FlipView
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
// Start Animation Block
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft
➥forView:[self superview] cache:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0];
// Animations
[[self superview] exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
66 Chapter 2 Views
Note
Apple’s Core Animation features 2D and 3D routines built around Objective-C classes.
These classes provide graphics rendering and animation for your iPhone and Macintosh
applications. Core Animation avoids many low-level development details associated with,
for example, direct OpenGL while retaining the simplicity of working with hierarchical views.
68 Chapter 2 Views
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:ANIMATION_DURATION]
➥forKey:kCATransactionAnimationDuration];
// scale it down
CABasicAnimation *shrinkAnimation = [CABasicAnimation
➥animationWithKeyPath:@"transform.scale"];
shrinkAnimation.timingFunction = [CAMediaTimingFunction
➥functionWithName:kCAMediaTimingFunctionEaseIn];
shrinkAnimation.toValue = [NSNumber numberWithFloat:0.0];
[[contentView layer] addAnimation:shrinkAnimation forKey:@"shrinkAnimation"];
// fade it out
CABasicAnimation *fadeAnimation = [CABasicAnimation
➥animationWithKeyPath:@"opacity"];
fadeAnimation.toValue = [NSNumber numberWithFloat:0.0];
fadeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:
➥kCAMediaTimingFunctionEaseIn];
[[contentView layer] addAnimation:fadeAnimation forKey:@"fadeAnimation"];
[CATransaction commit];
}
70 Chapter 2 Views
Note
In early releases of the iPhone SDK, swipes didn’t work in the Simulator. In later versions,
they did. Should you encounter platform limitations while developing (for example, when
working with the Camera), you can easily add workarounds based on testing the platform.
Add compiler directives such as #if defined(TARGET_IPHONE_SIMULATOR) to your
source.
#define HORIZ_SWIPE_DRAG_MIN 12
#define VERT_SWIPE_DRAG_MAX 4
}
}
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 72
72 Chapter 2 Views
@implementation HelloController
- (id)init
{
if (self = [super init]) self.title = @"Affine Demo";
return self;
}
- (void)loadView
{
theta = 0;
self.view = contentView;
[contentView release];
74 Chapter 2 Views
-(void) dealloc
{
[contentView release];
[super dealloc];
}
@end
Summary
UIViews provide the onscreen components your users see and interact with. As this
chapter has shown, even in their most basic form, they offer incredible flexibility and
power.You’ve discovered how to use views to build up elements on a screen, create
multiple interaction objects, and introduce eye-catching animation. Here’s a collection of
thoughts about the recipes you’ve seen in this chapter that you might want to ponder
before moving on:
n When dealing with multiple onscreen views, hierarchy should always remain front-
most in your mind—no pun! Use your view hierarchy vocabulary
(bringSubviewToFront:, sendSubviewToBack:,
02_0321555457_ch02.qxd 10/2/08 8:25 AM Page 75
Summary 75
Chapter 1
A Pragmatic Philosophy
What distinguishes Pragmatic Programmers? We feel it’s an attitude, a
style, a philosophy of approaching problems and their solutions. They
think beyond the immediate problem, always trying to place it in its
larger context, always trying to be aware of the bigger picture. After all,
without this larger context, how can you be pragmatic? How can you
make intelligent compromises and informed decisions?
Another key to their success is that they take responsibility for every-
thing they do, which we discuss in The Cat Ate My Source Code. Being
responsible, Pragmatic Programmers won’t sit idly by and watch their
projects fall apart through neglect. In Software Entropy, we tell you how
to keep your projects pristine.
Most people find change difficult to accept, sometimes for good reasons,
sometimes because of plain old inertia. In Stone Soup and Boiled Frogs,
we look at a strategy for instigating change and (in the interests of
balance) present the cautionary tale of an amphibian that ignored the
dangers of gradual change.
1
The Pragmatic Programmer
2001/2/27 — Page 2 (tmp/responsibility)
Copyright 2001 Addison Wesley Longman, Inc
Take Responsibility
Responsibility is something you actively agree to. You make a commit-
ment to ensure that something is done right, but you don’t necessarily
have direct control over every aspect of it. In addition to doing your own
personal best, you must analyze the situation for risks that are beyond
your control. You have the right not to take on a responsibility for an
impossible situation, or one in which the risks are too great. You’ll have
to make the call based on your own ethics and judgment.
When you do accept the responsibility for an outcome, you should ex-
pect to be held accountable for it. When you make a mistake (as we all
do) or an error in judgment, admit it honestly and try to offer options.
The Pragmatic Programmer
2001/2/27 — Page 3 (tmp/responsibility)
Copyright 2001 Addison Wesley Longman, Inc
T HE C AT A TE M Y S OURCE C ODE 3
If there was a risk that the vendor wouldn’t come through for you, then
you should have had a contingency plan. If the disk crashes—taking
all of your source code with it—and you don’t have a backup, it’s your
fault. Telling your boss “the cat ate my source code” just won’t cut it.
TIP 3
Before you approach anyone to tell them why something can’t be done,
is late, or is broken, stop and listen to yourself. Talk to the rubber
duck on your monitor, or the cat. Does your excuse sound reasonable,
or stupid? How’s it going to sound to your boss?
Run through the conversation in your mind. What is the other person
likely to say? Will they ask, “Have you tried this: : :” or “Didn’t you con-
sider that?” How will you respond? Before you go and tell them the bad
news, is there anything else you can try? Sometimes, you just know
what they are going to say, so save them the trouble.
Try to flush out the lame excuses before voicing them aloud. If you
must, tell your cat first. After all, if little Tiddles is going to take the
blame. . . .
The Pragmatic Programmer
2001/2/27 — Page 4 (tmp/no_broken_windows)
Copyright 2001 Addison Wesley Longman, Inc
Challenges
How do you react when someone—such as a bank teller, an auto mechanic,
or a clerk—comes to you with a lame excuse? What do you think of them
and their company as a result?
2 Software Entropy
While software development is immune from almost all physical laws,
entropy hits us hard. Entropy is a term from physics that refers to the
amount of “disorder” in a system. Unfortunately, the laws of thermo-
dynamics guarantee that the entropy in the universe tends toward a
maximum. When disorder increases in software, programmers call it
“software rot.”
There are many factors that can contribute to software rot. The most
important one seems to be the psychology, or culture, at work on a
project. Even if you are a team of one, your project’s psychology can
be a very delicate thing. Despite the best laid plans and the best peo-
ple, a project can still experience ruin and decay during its lifetime. Yet
there are other projects that, despite enormous difficulties and con-
stant setbacks, successfully fight nature’s tendency toward disorder
and manage to come out pretty well.
In inner cities, some buildings are beautiful and clean, while others
are rotting hulks. Why? Researchers in the field of crime and urban
decay discovered a fascinating trigger mechanism, one that very quickly
turns a clean, intact, inhabited building into a smashed and abandoned
derelict [WK82].
The Pragmatic Programmer
2001/2/27 — Page 5 (tmp/no_broken_windows)
Copyright 2001 Addison Wesley Longman, Inc
S OFTWARE E NTROPY 5
A broken window.
One broken window, left unrepaired for any substantial length of time,
instills in the inhabitants of the building a sense of abandonment—a
sense that the powers that be don’t care about the building. So another
window gets broken. People start littering. Graffiti appears. Serious
structural damage begins. In a relatively short space of time, the build-
ing becomes damaged beyond the owner’s desire to fix it, and the sense
of abandonment becomes reality.
TIP 4
You may be thinking that no one has the time to go around cleaning
up all the broken glass of a project. If you continue to think like that,
then you’d better plan on getting a dumpster, or moving to another
neighborhood. Don’t let entropy win.
department rushed in to save the day—and his house. But before they
dragged their big, dirty hoses into the house, they stopped—with the
fire raging—to roll out a mat between the front door and the source of
the fire.
A pretty extreme case, to be sure, but that’s the way it must be with
software. One broken window—a badly designed piece of code, a poor
management decision that the team must live with for the duration
of the project—is all it takes to start the decline. If you find yourself
working on a project with quite a few broken windows, it’s all too easy
to slip into the mindset of “All the rest of this code is crap, I’ll just follow
suit.” It doesn’t matter if the project has been fine up to this point.
In the original experiment leading to the “Broken Window Theory,” an
abandoned car sat for a week untouched. But once a single window was
broken, the car was stripped and turned upside down within hours.
By the same token, if you find yourself on a team and a project where
the code is pristinely beautiful—cleanly written, well designed, and
elegant—you will likely take extra special care not to mess it up, just
like the firefighters. Even if there’s a fire raging (deadline, release date,
trade show demo, etc.), you don’t want to be the first one to make a
mess.
Challenges
Help strengthen your team by surveying your computing “neighborhood.”
Choose two or three “broken windows” and discuss with your colleagues
what the problems are and what could be done to fix them.
Can you tell when a window first gets broken? What is your reaction? If
it was the result of someone else’s decision, or a management edict, what
can you do about it?
The Pragmatic Programmer
2001/2/27 — Page 7 (tmp/stone_soup)
Copyright 2001 Addison Wesley Longman, Inc
Undeterred, the soldiers boiled a pot of water and carefully placed three stones
into it. The amazed villagers came out to watch.
“This is stone soup,” the soldiers explained. “Is that all you put in it?” asked
the villagers. “Absolutely—although some say it tastes even better with a few
carrots: : : .” A villager ran off, returning in no time with a basket of carrots from
his hoard.
A couple of minutes later, the villagers again asked “Is that it?”
“Well,” said the soldiers, “a couple of potatoes give it body.” Off ran another
villager.
Over the next hour, the soldiers listed more ingredients that would enhance the
soup: beef, leeks, salt, and herbs. Each time a different villager would run off to
raid their personal stores.
Eventually they had produced a large pot of steaming soup. The soldiers removed
the stones, and they sat down with the entire village to enjoy the first square
meal any of them had eaten in months.
There are a couple of morals in the stone soup story. The villagers are
tricked by the soldiers, who use the villagers’ curiosity to get food from
them. But more importantly, the soldiers act as a catalyst, bringing
the village together so they can jointly produce something that they
couldn’t have done by themselves—a synergistic result. Eventually ev-
eryone wins.
Every now and then, you might want to emulate the soldiers.
You may be in a situation where you know exactly what needs doing
and how to do it. The entire system just appears before your eyes—you
know it’s right. But ask permission to tackle the whole thing and you’ll
be met with delays and blank stares. People will form committees, bud-
gets will need approval, and things will get complicated. Everyone will
guard their own resources. Sometimes this is called “start-up fatigue.”
The Pragmatic Programmer
2001/2/27 — Page 8 (tmp/stone_soup)
Copyright 2001 Addison Wesley Longman, Inc
It’s time to bring out the stones. Work out what you can reasonably
ask for. Develop it well. Once you’ve got it, show people, and let them
marvel. Then say “of course, it would be better if we added: : : .” Pretend
it’s not important. Sit back and wait for them to start asking you to
add the functionality you originally wanted. People find it easier to join
an ongoing success. Show them a glimpse of the future and you’ll get
them to rally around.1
TIP 5
We’ve all seen the symptoms. Projects slowly and inexorably get totally
out of hand. Most software disasters start out too small to notice, and
most project overruns happen a day at a time. Systems drift from their
specifications feature by feature, while patch after patch gets added to
a piece of code until there’s nothing of the original left. It’s often the
accumulation of small things that breaks morale and teams.
TIP 6
We’ve never tried this—honest. But they say that if you take a frog and
drop it into boiling water, it will jump straight back out again. However,
if you place the frog in a pan of cold water, then gradually heat it, the
frog won’t notice the slow increase in temperature and will stay put
until cooked.
1. While doing this, you may be comforted by the line attributed to Rear Admiral Dr.
Grace Hopper: “It’s easier to ask forgiveness than it is to get permission.”
The Pragmatic Programmer
2001/2/27 — Page 9 (tmp/good_enough_sw)
Copyright 2001 Addison Wesley Longman, Inc
Note that the frog’s problem is different from the broken windows issue
discussed in Section 2. In the Broken Window Theory, people lose the
will to fight entropy because they perceive that no one else cares. The
frog just doesn’t notice the change.
Don’t be like the frog. Keep an eye on the big picture. Constantly review
what’s happening around you, not just what you personally are doing.
Challenges
While reviewing a draft of this book, John Lakos raised the following is-
sue: The soldiers progressively deceive the villagers, but the change they
catalyze does them all good. However, by progressively deceiving the frog,
you’re doing it harm. Can you determine whether you’re making stone
soup or frog soup when you try to catalyze change? Is the decision subjec-
tive or objective?
4 Good-Enough Software
Striving to better, oft we mar what’s well.
King Lear 1.4
There’s an old(ish) joke about a U.S. company that places an order for
100,000 integrated circuits with a Japanese manufacturer. Part of the
specification was the defect rate: one chip in 10,000. A few weeks later
the order arrived: one large box containing thousands of ICs, and a
small one containing just ten. Attached to the small box was a label
that read: “These are the faulty ones.”
The Pragmatic Programmer
2001/2/27 — Page 10 (tmp/good_enough_sw)
Copyright 2001 Addison Wesley Longman, Inc
If only we really had this kind of control over quality. But the real
world just won’t let us produce much that’s truly perfect, particularly
not bug-free software. Time, technology, and temperament all conspire
against us.
The scope and quality of the system you produce should be specified
as part of that system’s requirements.
TIP 7
But artists will tell you that all the hard work is ruined if you don’t
know when to stop. If you add layer upon layer, detail over detail, the
painting becomes lost in the paint.
Challenges
Look at the manufacturers of the software tools and operating systems that
you use. Can you find any evidence that these companies are comfortable
shipping software they know is not perfect? As a user, would you rather
(1) wait for them to get all the bugs out, (2) have complex software and
accept some bugs, or (3) opt for simpler software with fewer defects?
Ah, good old Ben Franklin—never at a loss for a pithy homily. Why, if we
could just be early to bed and early to rise, we’d be great programmers—
right? The early bird might get the worm, but what happens to the early
worm?
In this case, though, Ben really hit the nail on the head. Your knowledge
and experience are your most important professional assets.
3. An expiring asset is something whose value diminishes over time. Examples include
a warehouse full of bananas and a ticket to a ball game.
The Pragmatic Programmer
2001/2/27 — Page 13 (tmp/knowledge)
Copyright 2001 Addison Wesley Longman, Inc
4. Investors try to buy low and sell high for maximum return.
Diversify. The more different things you know, the more valuable
you are. As a baseline, you need to know the ins and outs of the
particular technology you are working with currently. But don’t
stop there. The face of computing changes rapidly—hot technology
today may well be close to useless (or at least not in demand) to-
morrow. The more technologies you are comfortable with, the better
you will be able to adjust to change.
Of all these guidelines, the most important one is the simplest to do:
TIP 8
Goals
Now that you have some guidelines on what and when to add to your
knowledge portfolio, what’s the best way to go about acquiring intellec-
tual capital with which to fund your portfolio? Here are a few sugges-
tions.
Get wired. Want to know the ins and outs of a new language or
other technology? Newsgroups are a great way to find out what
experiences other people are having with it, the particular jargon
they use, and so on. Surf the Web for papers, commercial sites,
and any other sources of information you can find.
Don’t let it stop there. Take it as a personal challenge to find the answer.
Ask a guru. (If you don’t have a guru in your office, you should be able
to find one on the Internet: see the box on on the facing page.) Search
the Web. Go to the library.4
If you can’t find the answer yourself, find out who can. Don’t let it rest.
Talking to other people will help build your personal network, and you
may surprise yourself by finding solutions to other, unrelated problems
along the way. And that old portfolio just keeps getting bigger. . . .
All of this reading and researching takes time, and time is already in
short supply. So you need to plan ahead. Always have something to
read in an otherwise dead moment. Time spent waiting for doctors and
dentists can be a great opportunity to catch up on your reading—but be
sure to bring your own magazine with you, or you might find yourself
thumbing through a dog-eared 1973 article about Papua New Guinea.
Critical Thinking
The last important point is to think critically about what you read
and hear. You need to ensure that the knowledge in your portfolio is
accurate and unswayed by either vendor or media hype. Beware of the
zealots who insist that their dogma provides the only answer—it may
or may not be applicable to you and your project.
TIP 9
Unfortunately, there are very few simple answers anymore. But with
your extensive portfolio, and by applying some critical analysis to the
4. In this era of the Web, many people seem to have forgotten about real live libraries
filled with research material and staff.
The Pragmatic Programmer
2001/2/27 — Page 17 (tmp/knowledge)
Copyright 2001 Addison Wesley Longman, Inc
torrent of technical publications you will read, you can understand the
complex answers.
Challenges
Start learning a new language this week. Always programmed in C++? Try
Smalltalk [URL 13] or Squeak [URL 14]. Doing Java? Try Eiffel [URL 10]
or TOM [URL 15]. See page 267 for sources of other free compilers and
environments.
Start reading a new book (but finish this one first!). If you are doing very
detailed implementation and coding, read a book on design and architec-
ture. If you are doing high-level design, read a book on coding techniques.
Try Safari Books Online FREE
Get online access to 7,500+ Books and Videos
A
b
IT Books eBooks
so
h
rb
tc
Ca
IT
Podcasts Short Cuts
IT
Foll
Engage
ow I T
Conferences Rough Cuts
IT
Guides Online
Cit
ch
e
ar
IT
Blogs Video Se
D Articles
is IT
cu
ss ch
IT at
W
Resea rch IT
11 ways to learn IT AT
www.InformIT.com/learn
Get Behind the Scenes
Stay Ahead of the Curve
Rough Cuts is a Safari Books Online interactive publishing service that provides you
with first access to pre-published manuscripts on cutting-edge technology topics
— enabling you to stay on the cutting-edge and remain competitive. When you
participate in the Rough Cuts program, you also own an important role in helping
to develop manuscripts into best-selling books.
Here’s how it works:
1. Select a Rough Cuts title.
2. Get access through a Safari Library account. Or if you would like,
you can order a single Rough Cuts title as well.
3. Sign up to receive Alerts by visiting the Rough Cuts catalog page.
4. Read updated versions online or in PDF at your convenience.
5. Interact with the Rough Cuts community. Post your feedback
or respond to comments from other users, editors, and authors.
6. Access the final version – online access and PDF access of the
printed version is available for up to 45 days.
• L
iveLessons provides the ability
to learn at your own pace and
avoid hours in a classroom.
Package includes:
• 1 DVD featuring 3 - 8 hours of
instructor-led classroom sessions
divided into 15-20 minute
step-by-step hands-on labs
• S ample code and printed
study guide
Part of a
User Group?
Are you a member of a group that
meets to discuss IT-related topics?
Your group may be eligible for the
Pearson User Group Program!
Benefits include :
• Member discount of 35%
• Monthly review copies
• Information materials and more!