What Is an Object?

The previous section focused on classes and ended with a small but complete user-defined class called Timestamp. We've already let the cat out of the bag that an object is a variable of a class type, not a primitive type. There are several other ways to say that:

  • An object is an instance of a class

  • An object belongs to a class

  • An object belongs to a reference type

However, they all mean the same thing. This section fills in some more of the details on objects: how you declare them and how you create them (two different things), and how you use them.

What “reference type” really means

An object declaration looks exactly the same as a primitive variable declaration. You first write the type name (which is the class name), then follow it by the variable name (which is the object name). So it's just like a declaration of a primitive variable. Here is a declaration of a variable of class type:

Timestamp  tick;

The following example shows a qualified name that lets you declare an object of a type defined in another package:

java.util.Calendar  now;

The name Calendar is qualified by the package it is in, java.util.

The Java Language Specification talks about classes being reference types. It means memory references, as in a pointer or address. The variables in the previous examples, tick and now, look like they can hold an object. Don't be fooled! They really only hold something that refers to (is the address of) an object. You have to create the object itself as a separate step!

The big difference between primitive and object

That is a very big difference between primitive variables and objects. When you declare a variable of primitive type, the declaration allocates the memory, and you can start assigning into the variable right away. When you declare a variable of class type, you get somewhere to store the address of an object, but you do not get some storage for the object itself. You have to make the variable point to an existing or newly created object before you can assign to the fields or call the methods of an object.

The “null” reference

When you declare a reference type, it is automatically initialized with the null value, typically represented as zero on the underlying hardware. The null value says “I don't refer to anything” and if you try to get to an object through a null reference, it causes an error condition. You can explicitly assign null or check for it using the literal value “null” as shown in this example:

Timestamp t;        // t has the value null at this point

t = new Timestamp(); // this line creates a Timestamp object,
                     // and assigns its address into t.

if (t= =null)         // t won't be null here, because it points to the
                      // Timestamp we created in the previous statement

We can now use t to access the fields of the object t points to.

t.hrs = 12;

It's no big deal to create an object. Every class has one or more special methods, known as constructors, that create and initialize an object belonging to that class. Someone who wants to create an object will call one of these constructors. A constructor is written with statements like a regular method and has the same name as the class it belongs to. If you didn't explicitly provide any constructors, a simple implicit one is provided on your behalf. Our Timestamp class does not have any explicit constructors, so it gets the implicit one. The next two lines show an object declaration, then a constructor call. The return value from the constructor is a pointer to the freshly created object. Variable cv is assigned that pointer.

ClockView cv;
cv = new ClockView();

The keyword new is a tipoff that the programmer intends a constructor call, not a regular method. Typically, the two lines are combined into one as below:

ClockView cv = new ClockView();

Why Java uses reference types

You might ask, why the two-step process of declaring a reference variable, and filling it with a value? That's really two questions: why are constructors used, and why are reference variables used? Chapter 5 explores constructors more thoroughly, and addresses the “why constructors?” question. Reference variables are used for one overriding reason: they greatly simplify automatic memory management at run-time.

Managing memory resources by hand is very error-prone.It is all too easy to introduce leaks, dangling references, and memory corruption. By lifting this burden from the programmer, Java greatly improves software reliability of larger and longer running systems. Reference variables are a central part of Java so we can have automatic memory management, and that's an enabler for more reliable software.

In particular, when the JLS (Java Language Specification) lays down that all non-primitives are accessed through a reference, it becomes easy for the run-time system to tell if an area of memory is potentially still in use. If nothing points to an object, then it cannot be accessed, so it is not in use. References make it easy to reclaim the storage for an object that's no longer in use, and make it easy to move things about to compact memory. Reference variables bring other simplifications to the compiler and JVM, but automatic memory management is the main reason. As an implementation detail, there might be more than one level of indirection to reach an object, but that is invisible to the programmer. Reference variables also make it easy to build dynamic self-referencing data structures like trees.

class Tree {
    int data;
    Tree leftBranch;
    Tree rightBranch;
}

There's a cost, of course (there's always a cost). The big cost of reference variables is the “Huh -whuzzat?” factor they cause in people learning the language. The Java design team worked hard to reduce the places where you have to be aware of references. When you access a field in an object, the compiler automatically does the dereference for you, with no special syntax needed. In everyday use, most Java programmers usually say “object” when they mean “reference to an object,” and it's perfectly understandable. We'll follow that practice here, and only make a distinction in places where it makes a difference. But it's really important to understand the difference.

Choosing which Object will be operated on

OOP stresses the importance of objects rather than procedural statements. Consider it analogous to the expression “-5”, indicating “take the object known as '5' and do the '-' operation on it.” In OOP we have “take the object pointed at by t and do the fillTimes() method on it.”

As we already mentioned, operations on an object are expressed with method calls rather than operators[1] . To get to any member of an object you say what reference variable you are using, follow that by a “dot,” and then say what member you want. This “dot selection” is a common notation in several languages.

Here's an example. Assume we have some class with this declaration and initialization:

Timestamp t = new Timestamp();

Now we can access the fields of the object t points to.

t.hrs = 12;

This says “Go to the object that t is pointing to and assign to its hrs field". You invoke a method on an object variable in a similar way. It looks like this

t.fillTimes();

You specify which object you intend, here it is t, and then which method you want to call. That line of code calls the fillTimes() method on the fields of the object that t points to. If t is not currently pointing at an object a run-time error occurs, so silly mistakes are caught. Reference variables always point at a valid object or contain the value null. They can never hold a pointer to random memory as their contents. (Unless you link some non-Java code with your program; then the non-Java code could forge an invalid pointer.)

In this example, the object t has to belong to a class type that has a fillTimes() method, or else the compiler will flag it as an error. You can't call one of Timestamp's methods on a Calendar object. So if you want to “spiflicate” an object, you'd better make sure that its class contains a “spiflicate” method to do that. You don't call methods on individual fields of an object. You invoke them on the object as a whole.

Names can be arbitrarily deep. You might have two or three levels of package name, followed by a class nested inside a class, followed by a method call that returns an object that you select a field of. So the naming could look like this:

a.b.c.d.e.f().g

It's never a problem because you simply read it from left to right, to find out what it means.

Accessing a member from inside a class versus outside a class

Refresh your memory of the Timestamp class, which starts like this:

class Timestamp {
        int hrs;
        int mins;
        int secs;

        public fillTimes() {
              /* more code */
            hrs = now.get(java.util.Calendar.HOUR_OF_DAY);

Look at the part shown in bold. You can see that the get() method is being invoked on the Calendar object called now. Note also that we can access the field hrs without specifying which object it belongs to! We don't have any reference variable qualifier to the name, like myObj.hrs.

The reason this works just fine is because hrs is a field in the same class as the fillTimes() method. If you do not explicitly provide a reference variable when accessing fields from a method, the compiler makes the reasonable assumption that you are talking about the members belonging to the same object that you invoked the method on.

Under the covers, there is an extra hidden argument, argument_0, passed to all methods at run-time. Argument_0 is a copy of the reference variable that was used to invoke the method at run-time, so it points to the object containing the data for the method to use. Argument_0 would be assigned the value of t here. Argument_0 has a name that shows up in Java: this. You don't declare it; it's provided to you if you want to use it. You can qualify any member name with this:

this.hrs = now.get( ...

One reason for qualifying a field or method name with this is to emphasize you're talking about a member belonging to this specific object, as opposed to a local declaration inside a method or a member of some other object.

It's time to get back to our digital clock example.


[1] There is ongoing discussion in the Java community about whether Java should be changed to make it possible to use arithmetic operators, not just methods, to express operations on user-defined types. This feature is known as operator overloading, and it is not in JDK 1.5, though it may come in future. Operator overloading was left out of Java originally because it is easy to misuse, resulting in confusing and hard-to-maintain code. When used correctly, however, operator overloading can be a cool feature.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.47.218