Aliasing
Objective #1: Explain the differences between an object, an object variable, and an object reference and understand the concept of aliasing.
- Objects are used
in Java to store data. In fact, one object can store multiple pieces of
data which
are called instance variables. For example,
a Rectangle object
might store its width and height as instance variables. A Circle object might store
its
radius
as
an instance variable.
A Person object might store its name, age, and height as instance variables.
- In the old computer programming languages such as FORTRAN, COBOL,
C, and BASIC, objects were not used to store data. Instead, variables and
inefficient combinations of variables were used to store data.
- An object is associated with a specific class. The data type (or type) of an object is simply the name of the class it comes from. For
example, an object could be a Rectangle object because the Rectangle class is used as the definition for creating that object behind-the-scenes. Or, an object could be an Circle object but it cannot be both a Rectangle and a Circle at the same time just like the primitive variable num cannot be both an int and a double at the same time.
Objects
of different types require different amounts of memory to store their data
since they have different sets of instance variables. In other words, different classes have different sets of instance variables and therefore objects that are created from those classes use up different amounts of memory. A Rectangle object might use 124 bytes of memory because it has a length and a width while a Circle object may only use 116 bytes because it only has a radius property.
- If you want to use an object to store data, it is usually necessary
to first declare an object variable as in
Bug nemo; // 8 bytes of memory is used to store the memory address of null (i.e. memory address zero)
Instead of saying that we are declaring a variable we can say more specifically that we are declaring an object variable. It is even better to say that we are declaring a reference to an object. A reference is really just another name for a memory address. In this case the memory address 0 (called null) is probably assigned to the object variable nemo. In fact, we could type it out as Bug nemo = null;
Next, we need to create the actual object. So in this line of code
nemo = new Bug(); // a memory address where a whole Bug object can be stored is saved to the variable nemo
we are creating the actual object itself using the Java keyword new.
At this time, the computer's operating system finds an available, unused chunk of memory where a Bug object can be stored. If, according to the Bug class, a Bug object needs 124 bytes of memory then a chunk of 124 bytes of memory is found. The memory address number of the first memory cell in this contiguous chunk of memory is returned by the operating system and stored in the object variable named nemo.
Perhaps the memory address is 132. Or, maybe the memory address is 2456. It is not important for the Java programmer to know the actual numeric memory address. (Often the memory address is written as a hexadecimal number such as A32.)
So an object variable is really just a variable that stores the
object reference (i.e. memory address) of an actual object.
- The Bug object itself
might consume a lot of space, maybe 124 bytes of memory because it may contain many instance variables like myAge, myX, myY, myName, etc. But
memory addresses like A32 are simple numbers that only take up 8 bytes of memory. So the variable nemo is 8 bytes of memory being used to store a number that tells you where the actual object is being stored in the memory of the computer.
- The two separate statements explained above
Bug nemo;
nemo = new Bug();
are usually combined into the single statement
Bug nemo = new Bug();
- You can re-instantiate an object after it has already been instantiated by using the new operator again.
Bug nemo = new Bug(); // nemo's age is 0
System.out.println(nemo.getAge()); // 0
nemo.setAge(2);
System.out.println(nemo.getAge()); // 2
nemo = new Bug(); // re-instantiating nemo wipes out previous age & reset age to 0
// because a whole new underlying memory address (e.g. B52 instead of A32)
//
may now be used to store nemo
System.out.println(nemo.getAge()); // 0
It would cause an error though to re-declare nemo as a Bug when you are re-instantiating it a second time with the new operator as in:
Bug nemo = new Bug();
nemo.setAge(2);
System.out.println(nemo.getAge()); // 2
Bug nemo = new Bug(); // it is illegal to re-declare nemo as a Bug
- Here is an analogy
that may help you understand the relationship between an object,
an object
variable,
and
an
object reference.
Think
of
the house
that you
live in as an object. It is a rather large chunk of memory that stores
multiple pieces of data (i.e. your family members). Think of your mailing
address
(e.g. 123 Reading Boulevard) as an object reference. Your local post office
(like the operating system of the computer) needs to know where your house
is located (i.e. stored) in order to provide mail service. Pretend that
your parents put up a nicely painted sign in your front lawn with a cute
name
for you house, say The Smith Plantation. In this case, the name "The
Smith Plantation" is like an object variable that is used to store
your mailing address of
123 Reading Boulevard, which in turn helps other people find your actual
house.
Objective #2: Understand the concept of aliasing.
- Two object variables may contain the same object reference and therefore
refer to the same actual object in the memory of the computer. This concept is called aliasing.
In the following
example both object variables nemo and flik contain
the object reference.
Bug nemo = new Bug(); // nemo stores the memory address A32
Bug flik = nemo; // the value A32 that's found in nemo is now assigned and stored in flik as well
// so both object variables store the memory address A32
nemo.setAge(2); // setting nemo's age to 2 is also setting flik's age to 2 since they are really the same Bug object
System.out.println(nemo.getAge()); // 2
System.out.println(flik.getAge()); // 2 since flik is really a reference to the same underlying object as nemo
- Because aliasing can be confusing to follow when tracing code, it is generally
discouraged especially for beginner programmers. In more sophisticated programming, professionals may intentionally use aliasing though. To do well on the exam though, you must understand and be able to trace code with the aliasing but you will not likely need to write any code that makes use of aliasing.
- You can "break" aliasing between 2 object variables by setting one equal to null as in
Bug nemo = new Bug();
Bug flik = nemo;
nemo.setAge(2);
System.out.println(nemo.getAge()); // 2
System.out.println(flik.getAge()); // 2 since flik and nemo are aliased
flik = null;
System.out.println(nemo.getAge()); // 2
System.out.println(flik.getAge()); // error occurs since flik are nemo are no longer aliased
// In fact,
the object variable flik now stores the special memory address null and is no longer tied to an actual Bug object with a myAge instance variable
// This is called a "null exception error"
- You can also "break" aliasing between 2 object variables by re-instantiating one of them as in
Bug nemo = new Bug();
nemo.setAge(2);
Bug flik = nemo; // nemo & flik are aliased & both have myAge of 2
nemo = new Bug(); // they are no longer aliased
System.out.println(nemo.getAge()); // 0 since that is the default value of myAge
System.out.println(flik.getAge()); // 2 since flik still stores the original memory address where myAge was 2
- In the following example however, the two object
variables are NOT the same object since they do not contain the same object
reference
Bug nemo = new Bug(); // nemo stores the memory address A32
Bug flik = new Bug(); // a different memory address for another "new" object is found such as B52
nemo.setAge(2);
flik.setAge(3);
System.out.println(nemo.getAge()); // 2
System.out.println(flik.getAge()); // 3
Objective #3: Explain how object references are copied & be able to draw a supporting diagram that traces their values.
- Here is a diagram that illustrates the relationship between objects and
object variables:
RAM (memory) |
nemo
A32
willie
B52
flounder
A10 |
public static void main(String[]
args)
{
Bug nemo = new Bug();
nemo.setAge(2);
Bug willie = new Bug();
willie.setAge(3);
Bug flounder = new Bug(willie.getAge());
System.out.println(nemo.getAge()); // 2
System.out.println(willie.getAge()); // 3
System.out.println(flounder.getAge()); // 3
} |
0 (null) |
|
... |
|
A10 |
myAge
3 |
... |
|
A32 |
myAge
2 |
... |
|
B52 |
myAge
3 |
... |
|
FFFFFF |
|
- Consider the following code segment
Bug nemo = new Bug();
nemo.setAge(1); // nemo's age is 1
Bug willie = nemo; // aliasing
System.out.println(willie.getAge()); // 1
willie.setAge(2); // setting willie's age also sets nemo's age due to aliasing
System.out.println(nemo.getAge()); // 2
willie = null; // willie is no longer aliased to nemo
System.out.println(nemo.getAge()); // 2
System.out.println(willie.getAge()); // Null Pointer Exception error since willie stores null rather than the memory address of an actual object
willie = nemo; // aliasing
System.out.println(willie.getAge()); // 2
willie = new Bug(3); // willie refers to a new Bug object that has an age of 3
System.out.println(willie.getAge()); // 3
System.out.println(nemo.getAge()); // 2 since they are not aliased