Java is a heavily object-oriented programming language. When you do work in Java, you use objects to get the job done. You create objects, modify them, move them around, change their variables, call their methods, and combine them with other objects. You develop classes, create objects out of those classes, and use them with other classes and objects.
Today, you work extensively with objects. The following topics are covered:
Creating objects (also called instances)
Testing and modifying class and instance variables in those objects
Calling an object’s methods
Converting objects and other types of data from one class to another
When you write a Java program, you define a set of classes. As you learned during Day 1, “Getting Started with Java,” classes are templates used to create objects. These objects, which are also called instances, are self-contained elements of a program with related features and data. For the most part, you use the class merely to create instances and then work with those instances. In this section, therefore, you learn how to create a new object from any given class.
When using strings on Day 2, “The ABCs of Programming,” you learned that using a string literal (a series of characters enclosed in double quotation marks) creates a new instance of the class String
with the value of that string.
The String
class is unusual in that respect. Although it’s a class, the use of a string literal serves as a shortcut to create instances of that class. To create instances of other classes, the new
operator is used.
What about the literals for numbers and characters—don’t they create objects, too? Actually, they don’t. The primitive data types for numbers and characters create numbers and characters, but for efficiency they actually aren’t objects. On Day 5, “Creating Classes and Methods,” you’ll learn how to use objects to represent primitive values.
To create a new object, you use the new
operator with the name of the class that should be used as a template. The name of the class is followed by parentheses, as in these three examples:
String name = new String(); URL address = new URL("http://www.java21days.com"); VolcanoRobot robbie = new VolcanoRobot();
The parentheses are important; don’t leave them off. The parentheses can be empty, in which case the most simple, basic object is created, or the parentheses can contain arguments that determine the values of instance variables or other initial qualities of that object.
The following examples show objects being created with arguments:
Random seed = new Random(6068430714); Point pt = new Point(0, 0);
The number and type of arguments you can use inside the parentheses with new are defined by the class itself using a special method called a constructor. (You’ll learn more about constructors later today.) If you try to create a new instance of a class with the wrong number or type of arguments (or if you give it no arguments and it needs some), you’ll receive an error when you try to compile your Java program.
Here’s an example of creating different types of objects with different numbers and types of arguments: the StringTokenizer
class, part of the java.util
package, divides a string into a series of shorter strings called tokens.
A string is divided into tokens by applying some kind of character or characters as a delimiter. For example, the text "02/20/67"
could be divided into three tokens—02
, 20
, and 67
—using the slash character (“/”) as a delimiter.
Listing 3.1 is a Java program that creates StringTokenizer
objects by using new
in two different ways and then displays each token the objects contain.
Example 3.1. The Full Text of TokenTester.java
1: import java.util.StringTokenizer; 2: 3: class TokenTester { 4: 5: public static void main(String[] arguments) { 6: StringTokenizer st1, st2; 7: 8: String quote1 = "VIZY 3 -1/16"; 9: st1 = new StringTokenizer(quote1); 10: System.out.println("Token 1: " + st1.nextToken()); 11: System.out.println("Token 2: " + st1.nextToken()); 12: System.out.println("Token 3: " + st1.nextToken()); 13: 14: String quote2 = "NPLI@9 27/32@3/32"; 15: st2 = new StringTokenizer(quote2, "@"); 16: System.out.println(" Token 1: " + st2.nextToken()); 17: System.out.println("Token 2: " + st2.nextToken()); 18: System.out.println("Token 3: " + st2.nextToken()); 19: } 20: }
When you compile and run the program, the output should resemble the following:
Token 1: VIZY Token 2: 3 Token 3: -1/16 Token 1: NPLI Token 2: 9 27/32 Token 3: 3/32
In this example, two different StringTokenizer
objects are created using different arguments to the constructor listed after new
.
The first instance uses new StringTokenizer()
with one argument, a String
object named quote1
(line 9). This creates a StringTokenizer
object that uses the default delimiters: blank spaces, tab, newline, carriage return, or formfeed characters.
If any of these characters is contained in the string, it is used to divide the tokens. Because the quote1
string contains spaces, these are used as delimiters dividing each token. Lines 10–12 display the values of all three tokens: VIZY
, 3
, and -1/16
.
The second StringTokenizer
object in this example has two arguments when it is constructed in line 14—a String
object named quote2
and an at-sign character ("@"
). This second argument indicates that the "@"
character should be used as the delimiter between tokens. The StringTokenizer
object created in line 15 contains three tokens: NPLI
, 9 27/32
, and 3/32
.
You get a chance to work with live stock data from Yahoo Finance in the QuoteData project during Day 18, “Accessing Databases with JDBC.”
The application uses StringTokenizer
to split up stock quote fields separated by commas.
Several things happen when you use the new
operator—the new instance of the given class is created, memory is allocated for it, and a special method defined in the given class is called. This special method is called a constructor.
A constructor is a special method for creating and initializing a new instance of a class. A constructor initializes the new object and its variables, creates any other objects that the object needs, and performs any other operations that the object needs to initialize itself.
Multiple constructor definitions in a class can each have a different number, or type, of arguments. When you use new
, you can specify different arguments in the argument list, and the correct constructor for those arguments is called. Multiple constructor definitions enabled the TokenTester
class in the previous example to accomplish different things with the different uses of the new
operator. When you create your own classes, you can define as many constructors as you need to implement the behavior of the class.
If you are familiar with other object-oriented programming languages, you might wonder whether the new
statement has an opposite that destroys an object when it is no longer needed. Memory management in Java is dynamic and automatic. When you create a new object, Java automatically allocates the correct amount of memory for that object. You don’t have to allocate any memory for objects explicitly. Java does it for you.
Because Java memory management is automatic, you do not need to deallocate the memory an object uses when you’re finished using the object. Under most circumstances, when you are finished with an object you have created, Java can determine that the object no longer has any live references to it. (In other words, the object won’t be assigned to any variables still in use or stored in any arrays.)
As a program runs, Java periodically looks for unused objects and reclaims the memory that those objects are using. This process is called garbage collection and occurs without requiring any programming on your part. You don’t have to explicitly free the memory taken up by an object; you just have to make sure that you’re not still holding onto an object you want to get rid of.
At this point, you could create your own object with class and instance variables defined in it, but how do you work with those variables? Easy! Class and instance variables are used in largely the same manner as the local variables you learned about yesterday. You can use them in expressions, assign values to them in statements, and so on. You just refer to them slightly differently from how you refer to regular variables in your code.
To get to the value of an instance variable, you use dot notation, a form of addressing in which an instance or class variable name has two parts: a reference to an object or class on the left side of the dot and a variable on the right side of the dot.
Dot notation is a way to refer to an object’s instance variables and methods using a dot (.
) operator.
For example, if you have an object named myCustomer
, and that object has a variable called orderTotal
, you refer to that variable’s value as myCustomer.orderTotal
, as in this statement:
float total = myCustomer.orderTotal;
This form of accessing variables is an expression (that is, it returns a value), and both sides of the dot are also expressions. That means you can nest instance variable access. If the orderTotal
instance variable itself holds an object, and that object has its own instance variable called layaway
, you could refer to it as in this statement:
boolean onLayaway = myCustomer.orderTotal.layaway;
Dot expressions are evaluated from left to right, so you start with myCustomer
’s variable orderTotal
, which points to another object with the variable layaway
. You end up with the value of that layaway
variable.
Assigning a value to that variable is equally easy; just tack on an assignment operator to the right side of the expression:
myCustomer.orderTotal.layaway = true;
This example sets the value of the layaway
variable to true
.
Listing 3.2 is an example of a program that tests and modifies the instance variables in a Point
object. Point
, a class in the java.awt
package, represents points in a coordinate system with x
and y
values.
Example 3.2. The Full Text of PointSetter.java
1: import java.awt.Point; 2: 3: class PointSetter { 4: 5: public static void main(String[] arguments) { 6: Point location = new Point(4, 13); 7: 8: System.out.println("Starting location:"); 9: System.out.println("X equals " + location.x); 10: System.out.println("Y equals " + location.y); 11: 12: System.out.println(" Moving to (7, 6)"); 13: location.x = 7; 14: location.y = 6; 15: 16: System.out.println(" Ending location:"); 17: System.out.println("X equals " + location.x); 18: System.out.println("Y equals " + location.y); 19: } 20: }
When you run this application, the output should be the following:
Starting location: X equals 4 Y equals 13 Moving to (7, 6) Ending location: X equals 7 Y equals 6
In this example, you first create an instance of Point where x
equals 4
, and y
equals 13
(line 6). Lines 9 and 10 display these individual values using dot notation. Lines 13 and 14 change the values of x
to 7
and y
to 6
, respectively. Finally, lines 17 and 18 display the values of x
and y
again to show how they have changed.
Class variables, as you have learned, are variables defined and stored in the class itself. Their values apply to the class and all its instances.
With instance variables, each new instance of the class gets a new copy of the instance variables that the class defines. Each instance then can change the values of those instance variables without affecting any other instances. With class variables, only one copy of that variable exists. Changing the value of that variable changes it for all instances of that class.
You define class variables by including the static
keyword before the variable itself. For example, consider the following partial class definition:
class FamilyMember { static String surname = "Mendoza"; String name; int age; }
Each instance of the class FamilyMember
has its own values for name
and age
. The class variable surname
, however, has only one value for all family members: "Mendoza"
. Change the value of surname
, and all instances of FamilyMember
are affected.
Calling these static
variables refers to one of the meanings of the word static: fixed in one place. If a class has a static
variable, every object of that class has the same value for that variable.
To access class variables, you use the same dot notation as with instance variables. To retrieve or change the value of the class variable, you can use either the instance or the name of the class on the left side of the dot. Both lines of output in this example display the same value:
FamilyMember dad = new FamilyMember(); System.out.println("Family's surname is: " + dad.surname); System.out.println("Family's surname is: " + FamilyMember.surname);
Because you can use an instance to change the value of a class variable, it’s easy to become confused about class variables and where their values are coming from. Remember that the value of a class variable affects all its instances. For this reason, it’s a good idea to use the name of the class when you refer to a class variable. It makes your code easier to read and makes strange results easier to debug.
Calling a method in an object is similar to referring to its instance variables: Dot notation is used. The object whose method you’re calling is on the left side of the dot, and the name of the method and its arguments are on the right side of the dot:
myCustomer.addToOrder(itemNumber, price, quantity);
Note that all methods must have parentheses after them, even if the method takes no arguments:
myCustomer.cancelAllOrders();
Listing 3.3 shows an example of calling some methods defined in the String
class. Strings include methods for string tests and modification, similar to what you would expect in a string library in other languages.
Example 3.3. The Full Text of StringChecker.java
1: class StringChecker { 2: 3: public static void main(String[] arguments) { 4: String str = "Nobody ever went broke by buying IBM"; 5: System.out.println("The string is: " + str); 6: System.out.println("Length of this string: " 7: + str.length()); 8: System.out.println("The character at position 5: " 9: + str.charAt(5)); 10: System.out.println("The substring from 26 to 32: " 11: + str.substring(26, 32)); 12: System.out.println("The index of the character v: " 13: + str.indexOf('v')); 14: System.out.println("The index of the beginning of the " 15: + "substring "IBM": " + str.indexOf("IBM")); 16: System.out.println("The string in upper case: " 17: + str.toUpperCase()); 18: } 19: }
The following is displayed on your system’s standard output device when you run the program:
The string is: Nobody ever went broke by buying IBM Length of this string: 36 The character at position 5: y The substring from 26 to 32: buying The index of the character v: 8 The index of the beginning of the substring "IBM": 33 The string in upper case: NOBODY EVER WENT BROKE BY BUYING IBM
In line 4, you create a new instance of String
by using a string literal. The remainder of the program simply calls different string methods to do different operations on that string:
Line 5 prints the value of the string you created in line 4: "Nobody ever went broke by buying IBM"
.
Line 7 calls the length()
method in the new String
object. This string has 36 characters.
Line 9 calls the charAt()
method, which returns the character at the given position in the string. Note that string positions start at position 0
rather than 1
, so the character at position 5
is y
.
Line 11 calls the substring()
method, which takes two integers indicating a range and returns the substring with those starting and ending points. The substring()
method also can be called with only one argument, which returns the substring from that position to the end of the string.
Line 13 calls the indexOf()
method, which returns the position of the first instance of the given character (here, 'v'
). Character literals are surrounded by single quotation marks; if double quotation marks had surrounded the v
in line 13, the literal would be considered a String
.
Line 15 shows a different use of the indexOf()
method, which takes a string argument and returns the index of the beginning of that string.
Line 17 uses the toUpperCase()
method to return a copy of the string in all uppercase.
If you are familiar with printf-style formatting from other programming languages, you can employ the System.out.format()
method to apply this formatting when displaying strings.
The method takes two arguments: the output format and the string to display. Here’s an example that adds a dollar sign and commas to the display of an integer:
int accountBalance = 5005; System.out.format("Balance: $%,d%n", accountBalance);
This code produces the following output:
Balance: $5,005
The formatting string begins with a percent sign (“%”) followed by one or more flags. The “%,d” code displays a decimal with commas dividing each three digits. The “%n” code displays a newline character.
The next example displays the value of pi to 11 decimal places:
double pi = Math.PI; System.out.format("%.11f%n", pi);
The output:
3.14159265359
Sun’s Java site includes an introductory tutorial for printf-style output that describes some of the most useful formatting codes at the following address:
http://java.sun.com/docs/books/tutorial/java/data/number format.html
A method can return a reference to an object, a primitive data type, or no value at all. In the StringChecker
application, all the methods called on the String
object str
returned values that were displayed; for example, the charAt()
method returned a character at a specified position in the string.
The value returned by a method also can be stored in a variable:
String label = "From"; String upper = label.toUpperCase();
In the preceding example, the String
object upper
contains the value returned by calling label.toUpperCase()
—the text “FROM”, an uppercase version of “From”.
If the method returns an object, you can call the methods of that object in the same statement. This makes it possible for you to nest methods as you would variables.
Earlier today, you saw an example of a method called with no arguments:
myCustomer.cancelAllOrders();
If the cancelAllOrders()
method returns an object, you can call methods of that object in the same statement:
myCustomer.cancelAllOrders().talkToManager();
This statement calls the talkToManager()
method, which is defined in the object returned by the cancelAllOrders()
method of the myCustomer
object.
You can combine nested method calls and instance variable references, as well. In the next example, the putOnLayaway()
method is defined in the object stored by the orderTotal
instance variable, which itself is part of the myCustomer
object:
myCustomer.orderTotal.putOnLayaway(itemNumber, price, quantity);
This manner of nesting variables and methods is demonstrated in System.out.println()
, the method you’ve been using in all program examples to display information.
The System
class, part of the java.lang
package, describes behavior specific to the computer system on which Java is running. System.out
is a class variable that contains an instance of the class PrintStream
representing the standard output of the system, which is normally the screen but can be redirected to a printer or file. PrintStream
objects have a println()
method that sends a string to that output stream.
Class methods, like class variables, apply to the class as a whole and not to its instances. Class methods are commonly used for general utility methods that might not operate directly on an instance of that class but do fit with that class conceptually.
For example, the String
class contains a class method called valueOf()
, which can take one of many different types of arguments (integers, Booleans, objects, and so on). The valueOf()
method then returns a new instance of String
containing the string value of the argument. This method doesn’t operate directly on an existing instance of String
, but getting a string from another object or data type is behavior that makes sense to define in the String
class.
Class methods also can be useful for gathering general methods together in one place. For example, the Math
class, defined in the java.lang
package, contains a large set of mathematical operations as class methods; there are no instances of the class Math
, but you still can use its methods with numeric or Boolean arguments.
For example, the class method Math.max()
takes two arguments and returns the larger of the two. You don’t need to create a new instance of Math
; it can be called anywhere you need it, as in the following:
int higherPrice = Math.max(firstPrice, secondPrice);
Dot notation is used to call a class method. As with class variables, you can use either an instance of the class or the class itself on the left side of the dot. For the same reasons noted in the discussion on class variables, however, using the name of the class makes your code easier to read. The last two lines in this example produce the same result—the string “550”:
String s, s2; s = "item"; s2 = s.valueOf(550); s2 = String.valueOf(550);
As you work with objects, it’s important to understand references.
A reference is an address that indicates where an object’s variables and methods are stored.
You aren’t actually using objects when you assign an object to a variable or pass an object to a method as an argument. You aren’t even using copies of the objects. Instead, you’re using references to those objects.
To better illustrate the difference, Listing 3.4 shows how references work.
Example 3.4. The Full Text of RefTester.java
1: import java.awt.Point; 2: 3: class RefTester { 4: public static void main(String[] arguments) { 5: Point pt1, pt2; 6: pt1 = new Point(100, 100); 7: pt2 = pt1; 8: 9: pt1.x = 200; 10: pt1.y = 200; 11: System.out.println("Point1: " + pt1.x + ", " + pt1.y); 12: System.out.println("Point2: " + pt2.x + ", " + pt2.y); 13: } 14: }
Here is this program’s output:
Point1: 200, 200 Point2: 200, 200
The following takes place in the first part of this program:
Line 5—. Two Point
variables are created.
Line 6—. A new Point
object is assigned to pt1
.
Line 7—. The value of pt1
is assigned to pt2
.
Lines 9–12 are the tricky part. The x
and y
variables of pt1
are both set to 200
, and then all variables of pt1
and pt2
are displayed onscreen.
You might expect pt1
and pt2
to have different values. However, the output shows this is not the case. As you can see, the x
and y
variables of pt2
also were changed, even though nothing in the program explicitly changes them. This happens because line 7 creates a reference from pt2
to pt1
, instead of creating pt2
as a new object copied from pt1
.
pt2
is a reference to the same object as pt1
; this is shown in Figure 3.1. Either variable can be used to refer to the object or to change its variables.
If you wanted pt1
and pt2
to refer to separate objects, separate new Point()
statements could be used on lines 6 and 7 to create separate objects, as shown in the following:
pt1 = new Point(100, 100); pt2 = new Point(100, 100);
References in Java become particularly important when arguments are passed to methods. You learn more about this later today.
One thing you discover quickly about Java is how finicky it is about the information it will handle. Like Morris, the perpetually hard-to-please cat in the old 9Lives cat food commercials, Java methods and constructors require things to take a specific form and won’t accept alternatives.
When you are sending arguments to methods or using variables in expressions, you must use variables of the correct data types. If a method requires an int
, the Java compiler responds with an error if you try to send a float
value to the method. Likewise, if you’re setting up one variable with the value of another, they must be of the same type.
There is one area where Java’s compiler is decidedly flexible: String
s. String handling in println()
methods, assignment statements, and method arguments is simplified by the concatenation operator (+
). If any variable in a group of concatenated variables is a string, Java treats the whole thing as a String
. This makes the following possible:
float gpa = 2.25F; System.out.println("Honest, dad, my GPA is a " + (gpa+1.5));
Using the concatenation operator, a single string can hold the text representation of multiple objects and primitive data in Java.
Sometimes you’ll have a value in your Java class that isn’t the right type for what you need. It might be the wrong class or the wrong data type, such as a float
when you need an int
.
You use casting to convert a value from one type to another.
Casting is the process of producing a new value that has a different type than its source.
Although the concept of casting is reasonably simple, the usage is complicated by the fact that Java has both primitive types (such as int
, float
, and boolean
) and object types (String
, Point
, ZipFile
, and the like). This section discusses three forms of casts and conversions:
Casting between primitive types, such as int
to float
or float
to double
Casting from an instance of a class to an instance of another class, such as Object
to String
Casting primitive types to objects and then extracting primitive values from those objects
When discussing casting, it can be easier to think in terms of sources and destinations. The source is the variable being cast into another type. The destination is the result.
Casting between primitive types enables you to convert the value of one type to another primitive type. It most commonly occurs with the numeric types, and there’s one primitive type that can never be used in a cast. Boolean values must be either true
or false
and cannot be used in a casting operation.
In many casts between primitive types, the destination can hold larger values than the source, so the value is converted easily. An example would be casting a byte
into an int
. Because a byte
holds values from –128 to 127 and an int
holds from -2.1 billion to 2.1 billion, there’s more than enough room to cast a byte
into an int
.
You often can automatically use a byte
or a char
as an int
; you can use an int
as a long
, an int
as a float
, or anything as a double
. In most cases, because the larger type provides more precision than the smaller, no loss of information occurs as a result. The exception is casting integers to floating-point values; casting an int
or a long
to a float
, or a long
to a double
, can cause some loss of precision.
A character can be used as an int
because each character has a corresponding numeric code that represents its position in the character set. If the variable i
has the value 65
, the cast (char)i
produces the character value A
. The numeric code associated with a capital A is 65, according to the ASCII character set, and Java adopted this as part of its character support.
You must use an explicit cast to convert a value in a large type to a smaller type, or else converting that value might result in a loss of precision. Explicit casts take the following form:
(typename)value
In the preceding example, typename
is the name of the data type to which you’re converting, such as short
, int
, or float
. value
is an expression that results in the value of the source type. For example, in the following statement, the value of x
is divided by the value of y
, and the result is cast into an int
in the following expression:
int result = (int)(x / y);
Note that because the precedence of casting is higher than that of arithmetic, you have to use parentheses here; otherwise, the value of x
would be cast into an int
first and then divided by y
, which could easily produce a different result.
Instances of classes also can be cast into instances of other classes, with one restriction: The source and destination classes must be related by inheritance; one class must be a subclass of the other.
Some objects might not need to be cast explicitly. In particular, because a subclass contains all the same information as its superclass, you can use an instance of a subclass anywhere a superclass is expected.
For example, consider a method that takes two arguments, one of type Object
and another of type Component
. You can pass an instance of any class for the Object
argument because all Java classes are subclasses of Object
. For the Component
argument, you can pass in its subclasses, such as Button
, Container
, and Label
.
This is true anywhere in a program, not just inside method calls. If you had a variable defined as class Component
, you could assign objects of that class or any of its subclasses to that variable without casting.
This is true in the reverse, so you can use a superclass when a subclass is expected. There is a catch, however: Because subclasses contain more behavior than their superclasses, there’s a loss in precision involved. Those superclass objects might not have all the behavior needed to act in place of a subclass object. For example, if you have an operation that calls methods in objects of the class Integer
, using an object of class Number
won’t include many methods specified in Integer
. Errors occur if you try to call methods that the destination object doesn’t have.
To use superclass objects where subclass objects are expected, you must cast them explicitly. You won’t lose any information in the cast, but you gain all the methods and variables that the subclass defines. To cast an object to another class, you use the same operation as for primitive types:
(classname)object
In this case, classname
is the name of the destination class, and object
is a reference to the source object. Note that casting creates a reference to the old object of the type classname
; the old object continues to exist as it did before.
The following example casts an instance of the class VicePresident
to an instance of the class Employee
; VicePresident
is a subclass of Employee
with more information:
Employee emp = new Employee(); VicePresident veep = new VicePresident(); emp = veep; // no cast needed for upward use veep = (VicePresident)emp; // must cast explicitly
As you’ll see when you begin working with graphical user interfaces during Week 2, “The Java Class Library,” casting one object is necessary whenever you use Java2D graphics operations. You must cast a Graphics
object to a Graphics2D
object before you can draw onscreen. The following example uses a Graphics
object called screen
to create a new Graphics2D
object called screen2D
:
Graphics2D screen2D = (Graphics2D)screen;
Graphics2D
is a subclass of Graphics
, and both are in the java.awt
package. You explore the subject fully during Day 13, “Using Color, Fonts, and Graphics”.
In addition to casting objects to classes, you also can cast objects to interfaces, but only if an object’s class or one of its superclasses actually implements the interface. Casting an object to an interface means that you can call one of that interface’s methods even if that object’s class does not actually implement that interface.
One thing you can’t do under any circumstance is cast from an object to a primitive data type, or vice versa.
Primitive types and objects are very different things in Java, and you can’t automatically cast between the two.
As an alternative, the java.lang
package includes classes that correspond to each primitive data type: Float
, Boolean
, Byte
, and so on. Most of these classes have the same names as the data types, except that the class names begin with a capital letter (Short
instead of short
, Double
instead of double
, and the like). Also two classes have names that differ from the corresponding data type: Character
is used for char
variables, and Integer
is used for int
variables.
Using the classes that correspond to each primitive type, you can create an object that holds the same value. The following statement creates an instance of the Integer
class with the integer value 7801
:
Integer dataCount = new Integer(7801);
After you have an object created in this manner, you can use it as you would any object (although you cannot change its value). When you want to use that value again as a primitive value, there are methods for that, as well. For example, if you wanted to get an int
value from a dataCount
object, the following statement would be apt:
int newCount = dataCount.intValue(); // returns 7801
A common translation you need in programs is converting a String
to a numeric type, such as an integer. When you need an int
as the result, this can be done by using the parseInt()
class method of the Integer
class. The String
to convert is the only argument sent to the method, as in the following example:
String pennsylvania = "65000"; int penn = Integer.parseInt(pennsylvania);
The following classes can be used to work with objects instead of primitive data types: Boolean
, Byte
, Character
, Double
, Float
, Integer
, Long
, Short
, and Void
. These classes are commonly referred to as object wrappers because they provide an object representation that contains a primitive value.
If you try to use the preceding example in a program, your program won’t compile. The parseInt()
method is designed to fail with a NumberFormatException
error if the argument to the method is not a valid numeric value. To deal with errors of this kind, you must use special error-handling statements, which are introduced during Day 7, “Exceptions, Assertions, and Threads”.
Working with primitive types and objects that represent the same values is made easier through autoboxing and unboxing, an automatic conversion process.
Autoboxing automatically converts a primitive type to an object, and unboxing converts in the other direction.
If you write a statement that uses an object where a primitive type is expected, or vice versa, the value will be converted so that the statement executes successfully.
This is a marked departure from most earlier versions of the language.
As a demonstration, the following statements can’t be compiled in Java 2 version 1.4:
Float f1 = new Float(12.5F); Float f2 = new Float(27.2F); System.out.println("Lower number: " + Math.min(f1, f2));
When you attempt to compile a class containing these statements, the compiler stops with an error message stating that the Math.min()
method requires two float
primitive values as arguments, rather than Float
objects.
The statements compile successfully in Java 6. The Float
objects are unboxed into primitive values automatically when the Math.min()
method is called.
In addition to casting, you will often perform three other common tasks that involve objects:
Finding out the class of any given object
Testing to see whether an object is an instance of a given class
Yesterday, you learned about operators for comparing values—equal, not equal, less than, and so on. Most of these operators work only on primitive types, not on objects. If you try to use other values as operands, the Java compiler produces errors.
The exceptions to this rule are the operators for equality—==
(equal) and !=
(not equal). When applied to objects, these operators don’t do what you might first expect. Instead of checking whether one object has the same value as the other object, they determine whether both sides of the operator refer to the same object.
To compare instances of a class and have meaningful results, you must implement special methods in your class and call those methods.
A good example of this is the String
class. It is possible to have two different String
objects that represent the same text. If you were to employ the ==
operator to compare these objects, however, they would be considered unequal. Although their contents match, they are not the same object.
To see whether two String
objects have matching values, a method of the class called equals()
is used. The method tests each character in the string and returns true
if the two strings have the same values. Listing 3.5 illustrates this.
Example 3.5. The Full Text of EqualsTester.java
1: class EqualsTester { 2: public static void main(String[] arguments) { 3: String str1, str2; 4: str1 = "Free the bound periodicals."; 5: str2 = str1; 6: 7: System.out.println("String1: " + str1); 8: System.out.println("String2: " + str2); 9: System.out.println("Same object? " + (str1 == str2)); 10: 11: str2 = new String(str1); 12: 13: System.out.println("String1: " + str1); 14: System.out.println("String2: " + str2); 15: System.out.println("Same object? " + (str1 == str2)); 16: System.out.println("Same value? " + str1.equals(str2)); 17: } 18: }
This program’s output is as follows:
String1: Free the bound periodicals. String2: Free the bound periodicals. Same object? true String1: Free the bound periodicals. String2: Free the bound periodicals. Same object? false Same value? true
The first part of this program declares two variables (str1
and str2
), assigns the literal “Free the bound periodicals.” to str1
, and then assigns that value to str2
(lines 3–5). As you learned earlier, str1
and str2
now point to the same object, and the equality test at line 9 proves that.
In the second part of this program, you create a new String
object with the same value as str1
and assign str2
to that new String
object. Now you have two different string objects in str1
and str2
, both with the same value. Testing them to see whether they’re the same object by using the ==
operator (line 15) returns the expected answer: false
—they are not the same object in memory. Testing them using the equals()
method in line 16 also returns the expected answer: true
—they have the same values.
Why can’t you just use another literal when you change str2
, instead of using new
? String literals are optimized in Java; if you create a string using a literal and then use another literal with the same characters, Java knows enough to give you the first String
object back. Both strings are the same objects; you have to go out of your way to create two separate objects.
Want to find out what an object’s class is? Here’s the way to do it for an object assigned to the variable key
:
String name = key.getClass().getName();
What does this do? The getClass()
method is defined in the Object
class and is, therefore, available for all objects. It returns a Class
object that represents that object’s class. That object’s getName()
method returns a string holding the name of the class.
Another useful test is the instanceof
operator, which has two operands: a reference to an object on the left and a class name on the right. The expression produces a Boolean value: true
if the object is an instance of the named class or any of that class’s subclasses or false
otherwise, as in these examples:
boolean check1 = "Texas" instanceof String // true Point pt = new Point(10, 10); boolean check2 = pt instanceof String // false
The instanceof
operator also can be used for interfaces. If an object implements an interface, the instanceof
operator returns true
when this is tested.
Now that you have spent three days exploring how object-oriented programming is implemented in Java, you’re in a better position to decide how useful it can be in your own programming.
If you are a “glass is half empty” person, object-oriented programming is a level of abstraction that gets in the way of what you’re trying to use a programming language for. You learn more about why OOP is thoroughly ingrained in Java in the coming days.
If you are a “glass is half full” person, object-oriented programming is worth using because of the benefits it offers: improved reliability, reusability, and maintenance.
Today, you learned how to deal with objects: creating them, reading and changing their values, and calling their methods. You also learned how to cast objects from one class to another, cast to and from primitive data types and classes, and take advantage of automatic conversions through autoboxing and unboxing.
I’m confused about the differences between objects and the primitive data types, such as | |
The primitive types ( Objects are instances of classes and, as such, are usually much more complex data types than simple numbers and characters, often containing numbers and characters as instance or class variables. | |
The | |
The two methods look at strings a little differently. The | |
If Java lacks pointers, how can I do something like linked lists, where there’s a pointer from one node to another so that they can be traversed? | |
It’s untrue to say that Java has no pointers at all; it just has no explicit pointers. Object references are, effectively, pointers. To create something like a linked list, you could create a class called |
Review today’s material by taking this three-question quiz.
The following question is the kind of thing you could expect to be asked on a Java programming certification test. Answer it without looking at today’s material or using the Java compiler to test the code.
Given:
public class AyeAye { int i = 40; int j; public AyeAye() { setValue(i++); } void setValue(int inputValue) { int i = 20; j = i + 1; System.out.println("j = " + j); } }
What is the value of the
|
The answer is available on the book’s website at http://www.java21days.com. Visit the Day 3 page and click the Certification Practice link.
To extend your knowledge of the subjects covered today, try the following exercises:
Create a program that turns a birthday in MM/DD/YYYY format (such as 12/04/2007) into three individual strings.
Create a class with instance variables for height
, weight
, and depth
, making each an integer. Create a Java application that uses your new class, sets each of these values in an object, and displays the values.
Where applicable, exercise solutions are offered on the book’s website at http://www.java21days.com.