Certification Objective Using Wrapper Classes and Boxing (Exam Objective 3.1)


Certification Objective —Using Wrapper Classes and Boxing (Exam Objective 3.1)

3.1 Develop code that uses the primitive wrapper classes (such as Boolean, Character, Double, Integer, etc.), and/or autoboxing & unboxing. Discuss the differences between the String, StringBuilder, and StringBuffer 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. Table 3-2 lists the wrapper classes in the Java API.

Table 3-2: Wrapper Classes and Their Constructor Arguments

Primitive

Wrapper Class

Constructor Arguments

 boolean 

 Boolean 

 boolean or String 

 byte 

 Byte 

 byte or String 

 char 

 Character 

 char 

 double 

 Double 

 double or String 

 float 

 Float 

 float, double, or String 

 int 

 Integer 

 int or String 

 long 

 Long 

 long or String 

 short 

 Short 

 short or String 

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 no-arg 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,

Table 3-3: Common Wrapper Conversion Methods

Method

s = static

n = NFE exception

Boolean

Byte

Character

Double

Float

Integer

Long

Short

byteValue

  

x

 

x

x

x

x

x

doubleValue

  

x

 

x

x

x

x

x

floatValue

  

x

 

x

x

x

x

x

intValue

  

x

 

x

x

x

x

x

longValue

  

x

 

x

x

x

x

x

shortValue

  

x

 

x

x

x

x

x

          

parseXxx

s,n

 

x

 

x

x

x

x

x

parseXxx (with radix)

s,n

 

x

   

x

x

x

          

valueOf

s,n

x

x

 

x

x

x

x

x

valueOf (with radix)

s,n

 

x

   

x

x

x

          

toString

 

x

x

x

x

x

x

x

x

toString (primitive)

s

x

x

x

x

x

x

x

x

toString (primitive, radix)

s

     

x

x

 

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

 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, statictoString() 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" 

Studying Table 3-3 is the single best way to prepare for this section of the test. If you can keep the differences between xxxValue(), parseXxx(), and valueOf() straight, you should do well on this part of the exam.

Autoboxing

New to Java 5 is a feature known variously as: autoboxing, auto-unboxing, boxing, and unboxing. We'll stick with the terms boxing and unboxing. Boxing and unboxing make using wrapper classes more convenient. In the old, pre-Java 5 days, if you wanted to make a wrapper, unwrap it, use it, and then rewrap it, you might do something like this:

 Integer y = new Integer(567);     // make it int x = y.intValue();             // unwrap it x++;                              // use it y = new Integer(x);               // re-wrap it System.out.println("y = " + i);   // print it 

Now, with new and improved Java 5 you can say

 Integer y = new Integer(567);     // make it y++;                              // unwrap it, increment it,                                   // rewrap it System.out.println("y = " + i);   // print it 

Both examples produce the output:

 y = 568 

And yes, you read that correctly. The code appears to be using the post-increment operator on an object reference variable! But it's simply a convenience. Behind the scenes, the compiler does the unboxing and reassignment for you. Earlier we mentioned that wrapper objects are immutable this example appears to contradict that statement. It sure looks like y's value changed from 567 to 568. What actually happened, is that a second wrapper object was created and its value was set to 568. If only we could access that first wrapper object, we could prove it

Let's try this:

 Integer y = 567;                   // make a wrapper Integer x = y;                     // assign a second ref                                    // var to THE wrapper System.out.println(y==x);          // verify that they refer                                    // to the same object y++;                               // unwrap, use, "rewrap" System.out.println(x + " " + y);   // print values System.out.println(y==x);          // verify that they refer                                    // to different objects 

Which produces the output:

 true 567 568 false 

So, under the covers, when the compiler got to the line i++; it had to substitute something like this:

 int x2 = y.intValue();         // unwrap it x2++;                          // use it y = new Integer(x2);           // re-wrap it 

Just as we suspected, there's gotta be a call to new in there somewhere.

Boxing, ==, and Equals()

We just used = = to do a little exploration of wrappers. Let's take a more thorough look at how wrappers work with = =, !=, and equals (). We'll talk a lot more about the equals() method in later chapters. For now all we have to know is that the intention of the equals() method is to determine whether two instances of a given class are "meaningfully equivalent." This definition is intentionally subjective; it's up to the creator of the class to determine what "equivalent" means for objects of the class in question. The API developers decided that for all the wrapper classes, two objects are equal if they are of the same type and have the same value. It shouldn't be surprising that

 Integer i1 = 1000; Integer i2 = 1000; if(il != i2) System.out.println("different objects"); if(i1.equals(i2)) System.out.println("meaningfully equal"); 

Produces the output:

 different objects meaningfully equal 

It's just two wrapper objects that happen to have the same value. Because they have the same int value, the equals() method considers them to be "meaningfully equivalent", and therefore returns true. How about this one:

 Integer i3 = 10; Integer i4 = 10; if(i3 == i4) System.out.println("same object"); if(i3.equals(i4)) System.out.println("meaningfully equal"); 

This example produces the output:

 same object meaningfully equal 

Yikes! The equals() method seems to be working, but what happened with = = and != ? Why is != telling us that i1 and i2 are different objects, when = = is saying that i3 and i4 are the same object? In order to save memory, two instances of the following wrapper objects will always be = = when their primitive values are the same:

  • Boolean

  • Byte

  • Character from \u0000 to \u007f (7f is 127 in decimal)

  • Short and Integer from -128 to 127

Where Boxing Can be Used

As we discussed earlier, it's very common to use wrappers in conjunction with collections. Any time you want your collection to hold objects and primitives, you'll want to use wrappers to make those primitives collection-compatible. The general rule is that boxing and unboxing work wherever you can normally use a primitive or a wrapped object. The following code demonstrates some legal ways to use boxing:

 class UseBoxing {   public static void main(String [] args) {     UseBoxing u = new UseBoxing();     u.go(5) ;   }   boolean go(Integer i) {        // boxes the int it was passed     Boolean ifSo = true;         // boxes the literal     Short s = 300;               // boxes the primitive     if(ifSo) {                   // unboxing       System.out.println(++s);   // unboxes, increments, reboxes     }     return !ifSo;                // unboxes, returns the inverse   } } 

image from book
Exam Watch

Remember, wrapper reference variables can be null.That means that you have to watch out for code that appears to be doing safe primitive operations, but that could throw a NullPointerException:

 class Boxing2 {   static Integer x;   public static void main(String [] args) {     doStuff(x);   }   static void doStuff(int z) {     int z2 = 5;     System.out.println(z2 + z); } } 

This code compiles fine, but the JVM throws a NullpointerException when it attempts to invoke doStuff(x), because x doesn't refer to an Integer object, so there's no value to unbox.

image from book



SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net