Day 4
Working with Objects
by Laura Lemay
CONTENTS
Let's start today's lesson with an obvious statement: Because
Java is an object-oriented language, you're going to be dealing
with a lot of objects. You'll create them, modify them, move them
around, change their variables, call their methods, combine them
with other objects-and, of course, develop classes and use your
own objects in the mix.
Today, therefore, you'll learn all about the Java object in its
natural habitat. Today's topics include
- Creating instances of classes
- Testing and modifying class and instance variables in your
new instance
- Calling methods in that object
- Casting (converting) objects and other data types from one
class to another
- Other odds and ends about working with objects
- An overview of the Java class libraries
When you write a Java program, you define a set of classes. As
you learned on Day 2, "Object-Oriented Programming and Java,"
classes are templates for objects; for the most part, you merely
use the class to create instances and then work with those instances.
In this section, therefore, you'll learn how to create a new object
from any given class.
Remember strings from yesterday? You learned that using a string
literal-a series of characters enclosed in double-quotes-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, there's an easy way to
create instances of that class using a literal. The other classes
don't have that shortcut; to create instances of those classes
you have to do so explicitly by using the new
operator.
Note |
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 aren't actually objects. You can put
object wrappers around them if you need to treat them like objects (you'll learn how to do this in "Casting and `Converting Objects and Primitive Types").
|
To create a new object, you use the new
operator with the name of the class you want to create an instance
of, then parentheses after that. The following examples create
new instances of the classes String,
Random, and Motorcycle,
and store those new instances in variables of the appropriate
types:
String str = new String();
Random r = new Random();
Motorcycle m2 = new Motorcycle();
The parentheses are important; don't leave them off. The parentheses
can be empty (as in these examples), in which case the most simple,
basic object is created; or the parentheses can contain arguments
that determine the initial values of instance variables or other
initial qualities of that object:
Date dt = new Date(90, 4, 1, 4, 30);
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 and 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), then you'll
get an error when you try to compile your Java program.
Here's an example of creating several different types of objects
using different numbers and types of arguments. The Date
class, part of the java.util
package, creates objects that represent the current date. Listing
4.1 is a Java program that shows three different ways of creating
a Date object using new.
Listing 4.1. Laura's Date
program.
1: import java.util.Date;
2:
3: class CreateDates {
4:
5: public static void main(String args[]) {
6: Date d1, d2, d3;
7:
8: d1 = new Date();
9: System.out.println("Date 1: " + d1);
10:
11: d2 = new Date(71, 7, 1, 7, 30);
12: System.out.println("Date 2: " + d2);
13:
14: d3 = new Date("April 3 1993 3:24 PM");
15: System.out.println("Date 3: " + d3);
16: }
17: }
Date 1: Tue Feb 13 09:36:56 PST 1996
Date 2: Sun Aug 01 07:30:00 PDT 1971
Date 3: Sat Apr 03 15:24:00 PST 1993
Analysis |
In this example, three different date objects are created using different arguments to the class listed after new. The first instance (line 8) uses new Date() with no arguments, which creates a Date object for today's date (the
first line of the output shows a sample; your output will, of course, read the current date and time for you).
|
The second Date object you
create in this example has five integer arguments. The arguments
represent a date: year, month, day, hours, and minutes. And, as
the output shows, this creates a Date
object for that particular date: Sunday, August 1, 1971, at 7:30
a.m.
Note |
Java numbers months starting from 0. So although you might expect the seventh month to be July, month 7 in Java is indeed August.
|
The third version of Date
takes one argument, a string, representing the date as a text
string. When the Date object
is created, that string is parsed, and a Date
object with that date and time is created (see the third line
of output). The date string can take many different formats; see
the API documentation for the Date
class (part of the java.util
package) for information about what strings you can use.
When you use the new operator,
the new instance of the given class is created, and memory is
allocated for it. In addition (and most importantly), a special
method defined in the given class is called to initialize the
object and set up any initial values it needs. This special method
is called a constructor. Constructors
are special methods, defined in classes, that create and initialize
new instances of classes.
New Term
|
Constructors are special methods that initialize a new object, set its variables, create any other objects that object needs, and generally perform any other operations the object needs to initialize itself.
|
Multiple constructor definitions in a class can each have a different
number or type of arguments-then, when you use new,
you can specify different arguments in the argument list, and
the right constructor for those arguments will be called. That's
how each of those different versions of new
that you used in the CreateDates
class can create different Date
objects.
When you create your own classes, you can define as many constructors
as you need to implement that class's behavior. You'll learn how
to create constructors on Day 7, "More About Methods."
Memory management in Java is dynamic and automatic. When you create
a new object in Java, Java automatically allocates the right amount
of memory for that object in the heap. You don't have to allocate
any memory for any objects explicitly; Java does it for you.
What happens when you're finished with that object? How do you
de-allocate the memory that object uses? The answer, again, is
that memory management is automatic. Once you're done with an
object, you reassign all the variables that might hold that object
and remove it from any arrays, thereby making the object unusable.
Java has a "garbage collector" that looks for unused
objects and reclaims the memory that those objects are using.
You don't have to do any explicit freeing of memory; you just
have to make sure you're not still holding onto an object you
want to get rid of. You'll learn more specific details about the
Java garbage collector and how it works on Day 21, "Under
the Hood."
New Term |
A garbage collector is a special thing built into the Java environment that looks for unused objects. If it finds any, it automatically removes those objects and frees the memory those objects were using.
|
Now you have your very own object, and that object may have class
or instance variables defined in it. How do you work with those
variables? Easy! Class and instance variables behave in exactly
the same ways as the local variables you learned about yesterday;
you just refer to them slightly differently than you do regular
variables in your code.
To get to the value of an instance variable, you use an expression
in what's called dot notation.
With dot notation, the reference to an instance or class variable
has two parts: the object on the left side of the dot and the
variable on the right side of the dot.
New Term |
Dot notation is an expression used to get at instance variables and methods inside a given object.
|
For example, if you have an object assigned to the variable myObject,
and that object has a variable called var,
you refer to that variable's value like this:
myObject.var;
This form for accessing variables is an expression (it returns
a value), and both sides of the dot can also be expressions. This
means that you can nest instance variable access. If that var
instance variable itself holds an object and that object has its
own instance variable called state,
you could refer to it like this:
myObject.var.state;
Dot expressions are evaluated left to right, so you start with
myObject's variable var,
which points to another object with the variable state.
You end up with the value of that state
variable after the entire expression is done evaluating.
Assigning a value to that variable is equally easy-just tack an
assignment operator on the right side of the expression:
myObject.var.state = true;
Listing 4.2 is an example of a program that tests and modifies
the instance variables in a Point
object. Point is part of
the java.awt package and
refers to a coordinate point with an x
and a y value.
Listing 4.2. The TestPoint
Class.
1: import java.awt.Point;
2:
3: class TestPoint {
4: public static void main(String args[]) {
5: Point thePoint = new Point(10,10);
6:
7: System.out.println("X is " + thePoint.x);
8: System.out.println("Y is " + thePoint.y);
9:
10: System.out.println("Setting X to 5.");
11: thePoint.x = 5;
12: System.out.println("Setting Y to 15.");
13: thePoint.y = 15;
14:
15: System.out.println("X is " + thePoint.x);
16: System.out.println("Y is " + thePoint.y);
17:
18: }
19:}
X is 10
Y is 10
Setting X to 5.
Setting Y to 15.
X is 5
Y is 15
Analysis |
In this example, you first create an instance of Point where X and Y are both 10 (line 6). Lines 8 and 9 print out those individual values, and you can see dot notation at work there. Lines 11 through 14 change the
values of those variables to 5 and 15, respectively. Finally, lines 16 and 17 print out the values of X and Y again to show how they've changed.
|
Class variables, as you've already learned, are variables that
are defined and stored in the class itself. Their values, therefore,
apply to the class and to all its instances.
With instance variables, each new instance of the class gets a
new copy of the instance variables that class defines. Each instance
can then change the values of those instance variables without
affecting any other instances. With class variables, there is
only one copy of that variable. Every instance of the class has
access to that variable, but there is only one value. Changing
the value of that variable changes it for all the instances of
that class.
You define class variables by including the static
keyword before the variable itself. You'll learn more about this
on Day 6, "Creating Classes and Applications in Java."
For example, take the following partial class definition:
class FamilyMember {
static String surname = "Johnson";
String name;
int age;
...
}
Instances of the class FamilyMember
each have their own values for name and age. But the class variable
surname has only one value
for all family members. Change surname,
and all the instances of FamilyMember
are affected.
To access class variables, you use the same dot notation as you
do with instance variables. To get 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 of the lines of output
in this example print 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 the 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 strange
results easier to debug.
Calling a method is similar to referring to an object's instance
variables: Method calls to objects also use dot notation. The
object itself whose method you're calling is on the left side
of the dot; the name of the method and its arguments are on the
right side of the dot:
myObject.methodOne(arg1, arg2, arg3);
Note that all calls to methods must have parentheses after them,
even if that method takes no arguments:
myObject.methodNoArgs();
If the method you've called returns an object that itself has
methods, you can nest methods as you would variables. This next
example calls the getName()
method, which is defined in the object returned by the getClass()
method, which was defined in myObject.
Got it?
myObject.getClass().getName();
You can combine nested method calls and instance variable references
as well (in this case you're calling the methodTwo() method, which
is defined in the object stored by the var
instance variable, which in turn is part of the myObject
object):
myObject.var.methodTwo(arg1, arg2);
System.out.println(), the
method you've been using through the book this far to print out
bits of text, is a great example of nesting variables and methods.
The System class (part of
the java.lang package) describes
system-specific behavior. System.out
is a class variable that contains an instance of the class PrintStream
that points to the standard output of the system. PrintStream
instances have a println()
method that prints a string to that output stream.
Listing 4.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.
Listing 4.3. Several uses of String
methods.
1: class TestString {
2:
3: public static void main(String args[]) {
4: String str = "Now is the winter of our discontent";
5:
6: System.out.println("The string is: " + str);
7: System.out.println("Length of this string: "
8: + str.length());
9: System.out.println("The character at position 5: "
10: + str.charAt(5));
11: System.out.println("The substring from 11 to 17: "
12: + str.substring(11, 17));
13: System.out.println("The index of the character d: "
14: + str.indexOf('d'));
15: System.out.print("The index of the beginning of the ");
16: System.out.println("substring \"winter\": "
17: + str.indexOf("winter"));
18: System.out.println("The string in upper case: "
19: + str.toUpperCase());
20: }
21: }
The string is: Now is the winter of our discontent
Length of this string: 35
The character at position 5: s
The substring from positions 11 to 17: winter
The index of the character d: 25
The index of the beginning of the substring "winter": 11
The string in upper case: NOW IS THE WINTER OF OUR DISCONTENT
Analysis |
In line 4, you create a new instance of String by using a string literal (it's easier that way than using new and then putting the characters in individually). The remainder of the program simply calls different string methods to do
different operations on that string:
|
- Line 6 prints the value of the string we created in line 4:
"Now is the winter of our discontent".
- Line 7 calls the length()
method in the new String
object. This string has 35 characters.
- Line 9 calls the charAt()
method, which returns the character at the given position in the
string. Note that string positions start at 0,
so the character at position 5
is s.
- Line 11 calls the substring()
method, which takes two integers indicating a range and returns
the substring at those starting and ending points. The substring()
method can also 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, 'd').
- 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.
- Finally, line 19 uses the toUpperCase()
method to return a copy of the string in all uppercase.
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 may not operate directly on an instance
of that class, but 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, other objects, and so on). The valueOf()
method then returns a new instance of String
containing the string value of the argument it was given. This
method doesn't operate directly on an existing instance of String,
but getting a string from another object or data type is definitely
a String-like operation,
and it makes sense to define it in the String
class.
Class methods can also be useful for gathering general methods
together in one place (the class). 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 can still 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;
just call the method anywhere you need it, like this:
in biggerOne = Math.max(x, y);
To call a class method, you use dot notation as you do with instance
methods. As with class variables, you can use either an instance
of the class or the class itself on the left site of the dot.
However, for the same reasons noted in the discussion on class
variables, using the name of the class for class methods makes
your code easier to read. The last two lines in this example produce
the same result (the string "5"):
String s, s2;
s = "foo";
s2 = s.valueOf(5);
s2 = String.valueOf(5);
As you work with objects, one important thing going on behind
the scenes is the use of references to those objects. When you
assign objects to variables, or pass objects as arguments to methods,
you are passing references to those objects, not the objects themselves
or copies of those objects.
An example should make this clearer. Examine Listing 4.4, which
shows a simple example of how references work.
Listing 4.4. A references example.
1: import java.awt.Point;
2:
3: class ReferencesTest {
4: public static void main (String args[]) {
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: }
Point1: 200, 200
Point2: 200, 200
Analysis |
In the first part of this program, you declare two variables of type Point (line 5), create a new Point object to pt1 (line 6), and finally, assign the value of pt1 to pt2 (line 7).
|
Now, here's the challenge. After changing pt1's
x and y
instance variables in lines 9 and 10, what will pt2
look like?
As you can see, pt2's x and
y instance variables were also changed, even though you never
explicitly changed them. When you assign the value of pt1
to pt2, you actually create
a reference from pt2 to the
same object to which pt1
refers (see Figure 4.1). Change the object that pt2
refers to, and you also change the object that pt1
points to, because both are references to the same object.
Figure 4.1 : References to objects.
Note |
If you actually do want pt1 and pt2 to point to separate objects, you should use new Point() for both lines to create separate objects.
|
The fact that Java uses references becomes particularly important
when you pass arguments to methods. You'll learn more about this
later today, but keep these references in mind.
Technical Note |
There are no explicit pointers or pointer arithmetic in Java as there are in C-like languages-just references. However, with these references, and with Java arrays, you have most of the capabilities that you have with pointers without the confusion and
lurking bugs that explicit pointers can create.
|
Sometimes in your Java programs you may have a value stored somewhere
that is the wrong type for what you want to do with it. Maybe
it's an instance of the wrong class, or perhaps it's a float
and you want it to be an int.
To convert the value of one type to another, you use casting.
Casting is a programming term that means, effectively, converting
a value or an object from one type to another. The result of a
cast is a new value or object; casting does not change the original
object or value.
New Time |
Casting converts the value of an object or primitive type into another type.
|
Although the concept of casting is a simple one, the rules for
what types in Java can be converted to what other types are complicated
by the fact that Java has both primitive types (int,
float, boolean),
and object types (String,
Point, Window,
and so on). There are three forms of casts and conversions to
talk about in this section:
- Casting between primitive types: int
to float or float
to double
- Casting between object types: an instance of a class to an
instance of another class
- Converting primitive types to objects and then extracting
primitive values back out of those objects
Casting between primitive types allows you to "convert"
the value of one type to another primitive type-for example, to
assign a number of one type to a variable of another type. Casting
between primitive types most commonly occurs with the numeric
types; boolean values cannot be cast to any other primitive type.
Often, if the type you are casting to is "larger" than
the type of the value you're converting, you may not have to use
an explicit cast. You can often automatically treat a byte or
a character as an int, for
example, or an int as a long,
an int as a float,
or anything as a double automatically.
In most cases, because the larger type provides more precision
than the smaller, no loss of information occurs when the value
is cast. The exception is casting integers to floating-point values;
casting an int or a long
to a float or a long
to a double may cause some
loss of precision.
To convert a large value to smaller type, you must use an explicit
cast, because converting that value may result in a loss of precision.
Explicit casts look like this:
(typename)value
In this form, typename
is the name of the type you're converting to (for example: short,
int, float,
boolean), and value
is an expression that results in the value you want to convert.
So, for example, in this expression the value of x
is divided by the value of y
and the result is cast to an int:
(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
first and then divided by y
(which might very well be a very different result).
Instances of classes can also be cast to instances of other classes,
with one restriction: The class of the object you're casting and
the class you're casting it to must be related by inheritance;
that is, you can cast an object only to an instance of its class's
sub- or superclass-not to any random class.
Analogous to converting a primitive value to a larger type, some
objects may not need to be cast explicitly. In particular, because
subclasses contain all the same information as their superclass,
you can use an instance of a subclass anywhere a superclass is
expected. (Did you just have to read that sentence four times
before you understood it? I had to rewrite it a whole lot of times
before it became even that simple. Bear with me, its not that
bad. Let's try an example.) Suppose you have a method that takes
two arguments: one of type Object,
and one of type Number. You
don't have to pass instances of those particular classes to that
method. For the Object argument,
you can pass any subclass of Object
(any object, in other words), and for the Number
argument you can pass in any instance of any subclass of Number
(Integer, Boolean,
Float, and so on); you don't
have to explicitly convert them first.
Casting downward in the class hierarchy is automatic, but casting
upward is not. Converting an instance of a subclass to an instance
of a superclass loses the information the original subclass provided
and requires an explicit cast. To cast an object to another class,
you use the same casting operation that you used for base types:
(classname)object
In this case, classname
is the name of the class you want to cast the object to, and object
is a reference to the object you're casting. Note that casting
creates a reference to the old object of the type classname;
the old object still continues to exist as it did before.
Here's a (fictitious) example of a cast of an instance of the
class GreenApple to an instance
of the class Apple (where
GreenApple is theoretically
a subclass of Apple with
more information to define the apple as green):
GreenApple a;
Apple a2;
a = new GreenApple();
a2 = (Apple) a;
In addition to casting objects to classes, you can also cast objects
to interfaces-but only if that object's class or one of its superclasses
actually implements that 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.
You'll learn more about interfaces in Week 3.
Now you know how to cast a primitive type to another primitive
type and how to cast between classes. How can you cast one to
the other?
You can't! Primitive types and objects are very different things
in Java and you can't automatically cast or convert between the
two. However, the java.lang
package includes several special classes that correspond to each
primitive data type: Integer
for ints, Float
for floats, Boolean
for booleans, and so on.
Note that the class names have an initial capital letter, and
the primitive types are lowercase. Java treats these names very
differently, so don't confuse them, or your methods and variables
won't behave the way you expect.
Using class methods defined in these classes, you can create an
object-equivalent for all the primitive types using new.
The following line of code creates an instance of the Integer
class with the value 35:
Integer intObject = new Integer(35);
Once you have actual objects, you can treat those values as objects.
Then, when you want the primitive values back again, there are
methods for that as well-for example, the intValue()
method extracts an int primitive
value from an Integer object:
int theInt = intObject.intValue(); // returns 35
See the Java API documentation for these special classes for specifics
on the methods for converting primitives to and from objects.
Note |
In Java 1.0 there are special type classes for Boolean, Character, Double, Float, Integer, and Long. Java 1.1 adds classes for Byte and Short, as well as a special wrapper class for
Void. The latter classes are used primarily for object reflection.
|
This section is a catchall for other information about working
with objects, particularly the following:
- Comparing 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: equals,
not equals, 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 exception to this rule is with the operators for equality:
== (equal) and !=
(not equal). These operators, when used with objects,
test whether the two operands refer to exactly the same object
in memory.
What should you do if you want to be able to compare instances
of your class and have meaningful results? You have to implement
special methods in your class, and you have to call those methods
using those method names.
Technical Note |
Java does not have the concept of operator overloading-that is, the ability to redefine the behavior of the built-in operators using methods in your own classes. The built-in operators remain defined only for numbers.
|
A good example of this is the String
class. It is possible to have two strings, two independent objects
in memory with the same values-that is, the same characters in
the same order. According to the ==
operator, however, those two String
objects will not be equal, because, although their contents are
the same, they are not the same object.
The String class, therefore,
defines a method called equals()
that tests each character in the string and returns true
if the two strings have the same values. Listing 4.5 illustrates
this.
Listing 4.5. A test of string equality.
1: class EqualsTest {
2: public static void main(String args[]) {
3: String str1, str2;
4: str1 = "she sells sea shells by the sea shore.";
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: }
String1: she sells sea shells by the sea shore.
String2: she sells sea shells by the sea shore.
Same object? true
String1: she sells sea shells by the sea shore.
String2: she sells sea shells by the sea shore.
Same object? false
Same value? true
Analysis |
The first part of this program (lines 4 through 6) declares two variables (str1 and str2) assigns the literal she sells sea shells by the sea shore. to str1, and then assigns that value to str2. As you learned
earlier when we talked about object references, now str1 and str2 point to the same object, and the equality test at line 10 proves that.
|
In the second part, 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
16) returns the expected answer (false-they
are not the same object in memory), as does testing them using
the equals() method (line
17) (true-they have the same
values).
Technical Note |
Why can't you just use another literal when you change str2, rather than 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-to create two separate objects you have to go out of your way.
|
Want to find out the class of an object? Here's the way to do
it for an object assigned to the variable obj:
String name = obj.getClass().getName();
What does this do? The getClass()
method is defined in the Object
class, and as such is available for all objects. The result of
that method is a Class object
(where Class is itself a
class), which has a method called getName().
getName() returns a string
representing the name of the class.
Another test that might be useful to you is the instanceof
operator. instanceof has
two operands: an object on the left and the name of a class on
the right. The expression returns true
or false based on whether
the object is an instance of the named class or any of that class's
subclasses:
"foo" instanceof String // true
Point pt = new Point(10, 10);
pt instanceof String // false
The instanceof operator can
also be used for interfaces; if an object implements an interface,
the instanceof operator with
that interface name on the right side returns true.
You'll learn all about interfaces in Week 3.
Reflection, also known as introspection, is a somewhat lofty term
to describe the ability to "look inside" a class or
an object and get information about that object's variables and
methods as well as actually set and get the values of those variables
and to call methods. Object reflection is useful for tools such
as class browsers or debuggers, where getting at the information
of an object on-the-fly allows you to explore what that object
can do, or for component-based programs such as Java Beans, where
the ability for one object to query another object about what
it can do (and then ask it to do something) is useful to building
larger applications.
The classes that support reflection of Java classes and objects
will be part of the core Java 1.1 API (they are not available
in the 1.0.2 version of the JDK). A new package, java.lang.reflect,
will contain new classes to support reflection, which include
the following:
- Field, for managing and
finding out information about class and instance variables
- Method, for managing
class and instance methods
- Constructor, for managing
the special methods for creating new instances of classes (you'll
learn more about constructors on Day 7)
- Array, for managing arrays
- Modifier, for decoding
modifier information about classes, variables and methods (more
about modifiers on Day 15, "Modifiers, Access Control, and
Class Design")
In addition, there will be a number of new methods available in
the Class class to help tie
together the various reflection classes.
You can find out more about the new reflection classes and methods
from http://java.sun.com/products/JDK/1.1/designspecs/reflection/.
To finish up today, let's look at the Java class library. Actually,
you've had some experience with some of the Java classes already,
so they shouldn't seem that strange.
The Java class library provides the set of classes that are guaranteed
to be available in any commercial Java environment (for example,
in any Java development environment or in browsers such as Netscape).
Those classes are in the java
package and include all the classes you've seen so far in this
book, plus a whole lot more classes you'll learn about later on
in this book (and more you may not learn about at all).
The Java Developer's Kit comes with documentation for all of the
Java class library, which includes descriptions of each class's
instance variables, methods, constructors, interfaces, and so
on. You can get to this documentation (called the Java Application
Programmer's Interface, or API) via the Web at http://java.sun.com:80/products/JDK/CurrentRelease/api/packages.html.
A shorter summary of the Java API is in appendix C as well. Exploring
the Java class library and its methods and instance variables
is a great way to figure out what Java can and cannot do, as well
as how it can become a starting point for your own development.
Here are the class packages that are part of the Java class library:
- java.lang-Classes that
apply to the language itself, including the Object
class, the String class,
and the System class. It
also contains the special classes for the primitive types (Integer,
Character, Float,
and so on). You'll get at least a glance at most of the classes
in this package in this first week.
- java.util-Utility classes,
such as Date, as well as
simple collection classes, such as Vector
and Hashtable. You'll learn
more about these classes in the Bonus Week.
- java.io-Input and output
classes for writing to and reading from streams (such as standard
input and output) and for handling files. Day 19, "Streams
and I/O," describes the classes in this package.
- java.net-Classes for
networking support, including Socket
and URL (a class to represent
references to documents on the World Wide Web). You'll learn a
little about networking on Day 14, "Windows, Networking,
and Other Tidbits," and then on Day 26, "Client/Server
Networking in Java."
- java.awt-This is the
Abstract Windowing Toolkit. It contains classes to implement graphical
user interface features, including classes for Window,
Menu, Button,
Font, CheckBox,
and so on. It also includes mechanisms for managing system events
and for processing images (in the java.awt.Image
package). You'll learn all about the awt in Week 2.
- java.applet-Classes to
implement Java applets.
In addition to the Java classes, your development environment
may also include additional classes that provide other utilities
or functionality. Although these classes may be useful, because
they are not part of the standard Java library, they may not be
available to other people trying to run your Java program unless
you explicitly include those classes with your program. This is
particularly important for applets, because applets are expected
to be able to run on any platform, using any Java-enabled browser.
Only classes inside the java
package are guaranteed to be available on all browsers and Java
environments.
Objects, objects everywhere. Today, you've learned all about how
to deal with objects: how to create them, how to find out and
change the values of their variables, and how to call their methods.
You have also learned how to copy and compare them and how to
convert them into other objects. Finally, you have learned a bit
about the Java class libraries-which give you a whole slew of
classes to play with in your own programs.
You now have the fundamentals of how to deal with most simple
things in the Java language. All you have left are arrays, conditionals,
and loops, which you'll learn about tomorrow. Then you'll learn
how to define and use classes in Java applications on Day 6, and
launch directly into applets next week. With just about everything
you do in your Java programs, you'll always come back to objects.
Q: | I'm confused about the differences between objects and the primitive data types, such as int and boolean.
|
A: | The primitive types in the language (byte, short, int, long, float, double, boolean, and char) represent the smallest
things in the language. They are not objects, although in many ways they can be handled like objects-they can be assigned to variables and passed in and out of methods. Most of the operations that work exclusively on objects, however, will not work with
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.
|
Q:
| No pointers in Java? If you don't have pointers, how are you supposed to do something like linked lists, where you have a pointer from one nose to another so you can traverse them?
|
A:
| Java doesn't have no pointers at all; it has no explicit pointers. Object references are, effectively, pointers. So to create something like a linked list, you would create a class called Node, which would have an instance variable
also of type Node. Then to link together node objects all you need to do is assign a node object to the instance variable of the object just before it in the list. Because object references are pointers, linked lists set up this way will behave as
you would expect them to.
|
Q: | In the section on calling methods, you had examples of calling a method with a different number of arguments each time-and it gave a different kind of result. How is that possible?
|
A: | That's called method overloading. Overloading means that the same method can have different behavior based on the arguments it's called with-and the number and type of arguments can
vary. When you define methods in your own classes, you define separate method signatures with different sets of arguments and different definitions. When a method is called, Java figures out which definition to execute based on the number and type of
arguments with which you called it.
You'll learn all about this on Day 6.
|
Q: | No operator overloading in Java? Why not? I thought Java was based on C++, and C++ has operator overloading.
|
A: | Java was indeed based on C++, but it was also designed to be simple, so many of C++'s features have been removed. The argument against operator overloading is that because the operator can be
defined to mean anything; it makes it very difficult to figure out what any given operator is doing at any one time. This can result in entirely unreadable code. When you use a method, you know it can mean many things to many classes, but when you use an
operator you would like to know that it always means the same thing. Given the potential for abuse, the designers of Java felt it was one of the C++ features that was best left out.
|
Gin