Using Wrapper Classes:
The wrapper
classes in the Java API serve two primary purposes:
·
To provide a mechanism to
"wrap" primitive values in an object so that
the
primitives can be included in activities reserved for objects, like
being added
to Collections, or returned from a method with an object
return
value. Note: With Java 5's addition of autoboxing (and unboxing),
which we'll
get to in a few pages, many of the wrapping operations that
programmers
used to do manually are now handled automatically.
·
To provide an
assortment of utility functions for primitives. Most of
these
functions are related to various conversions: converting primitives
to and from
String objects, and converting primitives and String
objects to
and from different bases (or radix), such as binary, octal,
and
hexadecimal.
An Overview of the Wrapper Classes
There is a
wrapper class for every primitive in Java. For instance, the wrapper
class for int is Integer, the class for float is Float, and so on. Remember that
the
primitive name is simply the lowercase name of the wrapper except for char,
which maps
to Character, and int, which maps
to Integer.

Creating Wrapper Objects:
For the exam
you need to understand the three most common approaches for
creating
wrapper objects. Some approaches take a String representation of a
primitive as
an argument. Those that take a String throw NumberFormatException
if the
String provided cannot be parsed into the appropriate primitive. For example
"two"
can't be parsed into "2". Wrapper
objects are immutable. Once they have
been given a
value, that value cannot be changed. We'll talk more about wrapper
immutability
when we discuss boxing in a few pages.
The Wrapper Constructors:
All of the
wrapper classes except Character provide two constructors: one that takes
a primitive
of the type being constructed, and one that takes a String representation
of the type
being constructed—for example,
Integer i1
= new Integer(42);
Integer i2
= new Integer("42");
or
Float f1 =
new Float(3.14f);
Float f2 =
new Float("3.14f");
The
Character class provides only one constructor, which takes a char as an
argument—for
example,
Character
c1 = new Character('c');
The
constructors for the Boolean wrapper take either a boolean value true or
false, or a case-insensitive String with the value "true" or
"false". Until Java 5, a
Boolean
object couldn't be used as an expression in a boolean test—for instance,
Boolean b =
new Boolean("false");
if (b) //
won't compile, using Java 1.4 or earlier
As of Java
5, a Boolean object can be used in a boolean test, because the compiler
will
automatically "un-box" the Boolean to a boolean. We'll be focusing on Java 5's
autoboxing
capabilities in the very next section—so stay tuned!
The valueOf() Methods:
The two
(well, usually two) static valueOf() methods provided in most of the
wrapper
classes give you another approach to creating wrapper objects. Both
methods take
a String representation of the appropriate type of primitive as
their first
argument, the second method (when provided) takes an additional
argument, int radix, which indicates in what base (for
example binary, octal, or
hexadecimal)
the first argument is represented—for example,
Integer i2
= Integer.valueOf("101011", 2); // converts 101011
// to 43
and
// assigns
the value
// 43 to
the
// Integer
object i2
or
Float f2 =
Float.valueOf("3.14f"); // assigns 3.14 to the
// Float
object f2
Using Wrapper Conversion Utilities:
As we said
earlier, a wrapper's second big function is converting stuff. The following
methods are
the most commonly used, and are the ones you're most likely to see on
the test.
xxxValue():
When you
need to convert the value of a wrapped numeric to a primitive, use
one of the
many xxxValue() methods. All of the
methods in this family are noarg
methods. As
you can see by referring to Table 3-3, there are 36 xxxValue()
methods.
Each of the six numeric wrapper classes has six methods, so that any
numeric
wrapper can be converted to any primitive numeric type—for example,
Integer i2
= new Integer(42); // make a new wrapper object
byte b =
i2.byteValue(); // convert i2's value to a byte
//
primitive
short s =
i2.shortValue(); // another of Integer's xxxValue
// methods
double d =
i2.doubleValue(); // yet another of Integer's
// xxxValue
methods
or
Float f2 =
new Float(3.14f); // make a new wrapper object
short s =
f2.shortValue(); // convert f2's value to a short
//
primitive
System.out.println(s);
// result is 3 (truncated, not
// rounded)
parseXxx() and valueOf()
The six parseXxx() methods (one for each numeric wrapper
type) are closely
related to
the valueOf() method that exists in all of
the numeric wrapper
classes.
Both parseXxx() and valueOf() take a String as an argument, throw a
NumberFormatException
(a.k.a. NFE) if the String argument is not properly formed,
and can
convert String objects from different bases (radix), when the underlying
primitive
type is any of the four integer types. (See Table 3-3.) The difference
between the
two methods is
·
parseXxx() returns the named primitive.
·
valueOf() returns a newly created wrapped object of the type that invoked
the method.
Here are
some examples of these methods in action:
double d4 =
Double.parseDouble("3.14"); // convert a String
// to a
primitive
System.out.println("d4
= " + d4); // result is d4 = 3.14
Double d5 =
Double.valueOf("3.14"); // create a Double obj
System.out.println(d5
instanceof Double); // result is "true"
The next
examples involve using the radix argument (in this case binary):
long L2 =
Long.parseLong("101010", 2); // binary String to a
//
primitive
System.out.println("L2
= " + L2); // result is: L2 = 42
Long L3 =
Long.valueOf("101010", 2); // binary String to
// Long
object
System.out.println("L3
value = " + L3); // result is:
// L3 value
= 42
toString()
Class
Object, the alpha class, has a toString() method. Since we know that all
other Java classes
inherit from class Object, we also know that all other Java classes
have a toString() method. The idea of the toString() method is to allow you
to get some
meaningful representation of a given object. For instance, if you have a
Collection
of various types of objects, you can loop through the Collection and print
out some
sort of meaningful representation of each object using the toString()
method,
which is guaranteed to be in every class. We'll talk more about toString()
in the
Collections chapter, but for now let's focus on how toString() relates to
the wrapper
classes which, as we know, are marked final. All of the wrapper classes
have a
no-arg, nonstatic, instance version of toString(). This method returns a
String with
the value of the primitive wrapped in the object—for instance,
Double d =
new Double("3.14");
System.out.println("d
= "+ d.toString() ); // result is d = 3.14
All of the
numeric wrapper classes provide an overloaded, static
toString()
method that
takes a primitive numeric of the appropriate type (Double.
toString() takes a double, Long.toString() takes a long, and so on) and, of
course,
returns a String:
String d =
Double.toString(3.14); // d = "3.14"
Finally,
Integer and Long provide a third toString() method. It's static, its first
argument is
the primitive, and its second argument is a radix. The radix tells the
method to
take the first argument, which is radix 10 (base 10) by default, and
convert it
to the radix provided, then return the result as a String—for instance,
String s =
"hex = "+ Long.toString(254,16); // s = "hex = fe"
toXxxString() (Binary, Hexadecimal, Octal)
The Integer
and Long wrapper classes let you convert numbers in base 10 to other
bases. These
conversion methods, toXxxString(), take an int or long, and return
a String
representation of the converted number, for example,
String s3 =
Integer.toHexString(254); // convert 254 to hex
System.out.println("254
is " + s3); // result: "254 is fe"
String s4 =
Long.toOctalString(254); // convert 254 to octal
System.out.print("254(oct)
="+ s4); // result: "254(oct) =376"
In summary,
the essential method signatures for Wrapper conversion methods are
primitive
xxxValue() - to convert a Wrapper to a primitive
primitive
parseXxx(String) - to convert a String
to a primitive
Wrapper
valueOf(String) - to convert a String
to a Wrapper



Garbage
Collection:
Overview
of Java's Garbage Collector
Let's look at what we
mean when we talk about garbage collection in the land of
Java. From the 30,000
ft. level, garbage collection is the phrase used to describe
automatic memory
management in Java. Whenever a software program executes (in
Java, C, C++, Lisp,
Ruby, and so on), it uses memory in several different ways. We're
not going to get into
Computer Science 101 here, but it's typical for memory to be
used to create a stack,
a heap, in Java's case constant pools, and method areas. The
heap is that part of
memory where Java objects live, and it's the one and only part of
memory that is in any
way involved in the garbage collection process.
A heap is a heap is a
heap. For the exam it's important to know that you can call
it the heap, you can
call it the garbage collectible heap, you can call it Johnson,
but there is one and
only one heap.
So, all of garbage
collection revolves around making sure that the heap has as
much free space as
possible. For the purpose of the exam, what this boils down to
is deleting any objects
that are no longer reachable by the Java program running.
We'll talk more about
what reachable means, but let's drill this point in. When
the garbage collector
runs, its purpose is to find and delete objects that cannot be
reached. If you think of
a Java program as being in a constant cycle of creating the
objects it needs (which
occupy space on the heap), and then discarding them when
they're no longer
needed, creating new objects, discarding them, and so on, the
missing piece of the
puzzle is the garbage collector. When it runs, it looks for those
discarded objects and
deletes them from memory so that the cycle of using memory
and releasing it can
continue. Ah, the great circle of life.
When
Does the Garbage Collector Run?
The garbage collector is
under the control of the JVM. The JVM decides when to
run the garbage
collector. From within your Java program you can ask the JVM to
run the garbage
collector, but there are no guarantees, under any circumstances, that
the JVM will comply.
Left to its own devices, the JVM will typically run the garbage
collector when it senses
that memory is running low. Experience indicates that when
your Java program makes
a request for garbage collection, the JVM will usually grant
your request in short
order, but there are no guarantees. Just when you think you can
count on it, the JVM
will decide to ignore your request.
How
Does the Garbage Collector Work?
You just can't be sure.
You might hear that the garbage collector uses a mark and
sweep algorithm, and for
any given Java implementation that might be true, but the
Java specification
doesn't guarantee any particular implementation. You might hear
that the garbage
collector uses reference counting; once again maybe yes maybe no.
The important concept to
understand for the exam is when does an object become
eligible for garbage
collection? To answer this question fully, we have to jump ahead
a little bit and talk
about threads. (See Chapter 9 for the real scoop on threads.) In a
nutshell, every Java
program has from one to many threads. Each thread has its own
little execution stack.
Normally, you (the programmer) cause at least one thread to
run in a Java program,
the one with the main() method at the bottom of the
stack.
However, as you'll learn
in excruciating detail in Chapter 9, there are many really
cool reasons to launch
additional threads from your initial thread. In addition to
having its own little
execution stack, each thread has its own lifecycle. For now,
all we need to know is
that threads can be alive or dead. With this background
information, we can now
say with stunning clarity and resolve that an object is
eligible
for
garbage collection when no live thread can access it.
(Note: Due to the vagaries of
the String constant
pool, the exam focuses its garbage collection questions on non-
String objects, and so
our garbage collection discussions apply to only non-String
objects too.)
Based on that
definition, the garbage collector does some magical, unknown
operations, and when it
discovers an object that can't be reached by any live thread,
it will consider that
object as eligible for deletion, and it might even delete it at
some point. (You guessed
it; it also might not ever delete it.) When we talk about
reaching an object,
we're really talking about having a reachable reference variable
that refers to the object
in question. If our Java program has a reference variable
that refers to an
object, and that reference variable is available to a live thread, then
that object is
considered reachable. We'll talk more about how objects can become
unreachable in the following
section.
Can a Java application
run out of memory? Yes. The garbage collection system
attempts to remove
objects from memory when they are not used. However, if
you maintain too many
live objects (objects referenced from other live objects),
the system can run out
of memory. Garbage collection cannot ensure that there
is enough memory, only
that the memory that is available will be managed as
efficiently as possible.
Writing
Code That Explicitly Makes Objects Eligible for Collection
In the preceding
section, we learned the theories behind Java garbage collection.
In this section, we show
how to make objects eligible for garbage collection using
actual code. We also
discuss how to attempt to force garbage collection if it is
necessary, and how you
can perform additional cleanup on objects before they are
removed from memory.
Nulling
a Reference
As we discussed earlier,
an object becomes eligible for garbage collection when
there are no more
reachable references to it. Obviously, if there are no reachable
references, it doesn't
matter what happens to the object. For our purposes it is just
floating in space,
unused, inaccessible, and no longer needed.
The first way to remove
a reference to an object is to set the reference variable
that refers to the
object to null. Examine the following code:
1. public class
GarbageTruck {
2. public static void
main(String [] args) {
3. StringBuffer sb =
new StringBuffer("hello");
4.
System.out.println(sb);
5. // The StringBuffer
object is not eligible for collection
6. sb = null;
7. // Now the
StringBuffer object is eligible for collection
8. }
9. }
The StringBuffer object
with the value hello is assigned to the reference
variable sb in the
third line. To make the object eligible (for GC), we set the
reference variable sb to null, which
removes the single reference that existed to the
StringBuffer object.
Once line 6 has run, our happy little hello StringBuffer
object
is doomed, eligible for
garbage collection.
Forcing Garbage Collection
The first thing
that should be mentioned here is that, contrary to this section's title,
garbage
collection cannot be forced. However, Java provides some methods that
allow you to
request that the JVM perform garbage collection. For example, if you
are about to
perform some time-sensitive operations, you probably want to minimize
the chances
of a delay caused by garbage collection. But you must remember that the
methods that
Java provides are requests, and not demands; the virtual machine will
do its best
to do what you ask, but there is no guarantee that it will comply.
Figure 3-

In reality, it is
possible only to suggest to the JVM that it perform garbage
collection. However,
there are no guarantees the JVM will actually remove all of the
unused objects from
memory (even if garbage collection is run). It is essential that
you understand this
concept for the exam.
The garbage collection
routines that Java provides are members of the Runtime
class. The Runtime class
is a special class that has a single object (a Singleton) for
each main program. The
Runtime object provides a mechanism for communicating
directly with the
virtual machine. To get the Runtime instance, you can use the
method
Runtime.getRuntime(), which returns the Singleton. Once you have
the Singleton you can
invoke the garbage collector using the gc() method.
Alternatively, you can
call the same method on the System class, which has static
methods that can do the
work of obtaining the Singleton for you. The simplest way
to ask for garbage
collection (remember—just a request) is
System.gc();
Theoretically, after
calling System.gc(), you will have as much free memory as
possible. We say
theoretically because this routine does not always work that way.
First, your JVM may not
have implemented this routine; the language specification
allows this routine to
do nothing at all. Second, another thread (again, see the
Chapter 9) might grab
lots of memory right after you run the garbage collector.
This is not to say that System.gc()
is a useless method—it's much better than
nothing. You just can't
rely on System.gc() to free up enough memory so that
you don't have to worry
about running out of memory. The Certification Exam is
interested in guaranteed
behavior, not probable behavior.
Now that we are somewhat
familiar with how this works, let's do a little
experiment to see if we
can see the effects of garbage collection. The following
program lets us know how
much total memory the JVM has available to it and how
much free memory it has.
It then creates 10,000 Date objects. After this, it tells us
how much memory is left
and then calls the garbage collector (which, if it decides
to run, should halt the
program until all unused objects are removed). The final free
memory result should
indicate whether it has run.
Cleaning Up Before Garbage Collection—the finalize() Method
Java
provides you a mechanism to run some code just before your object is deleted
by the
garbage collector. This code is located in a method named finalize() that
all classes
inherit from class Object. On the surface this sounds like a great idea;
maybe your
object opened up some resources, and you'd like to close them before
your object
is deleted. The problem is that, as you may have gathered by now, you
can't count
on the garbage collector to ever delete an object. So, any code that you
put into
your class's overridden finalize() method is not guaranteed to run. The
finalize() method for any given object might run, but you can't count on it,
so
don't put
any essential code into your finalize() method. In fact, we recommend
that in
general you don't override finalize() at all.
Tricky Little finalize() Gotcha's
There are a
couple of concepts concerning finalize() that you need to remember.
n For any given object, finalize() will be called only once (at most) by the
garbage
collector.
n Calling finalize() can actually result in saving an object from deletion.
Let's look
into these statements a little further. First of all, remember that any
code that
you can put into a normal method you can put into finalize(). For
example, in
the finalize() method you could
write code that passes a reference
to the
object in question back to another object, effectively uneligiblizing the object
for garbage
collection. If at some point later on this same object becomes eligible for
garbage
collection again, the garbage collector can still process this object and
delete
it. The
garbage collector, however, will remember that, for this object, finalize()
already ran,
and it will not run finalize() again.