References

advertisement
Java 211, Lecture 5
Mendelsohn
References
References
An object reference and an object are two separate things.
See Powerpoint presentation, slides 3-9
Recall that when we declare an object with the syntax:
ObjectType objectName; we have only created a reference to an object. It is
only when we use the ‘new’ operator and call the constructor that we actually have
created a valid object to work with. Well, what exactly do we mean by a ‘reference to an
object’?
The answer is that a reference variable holds the address of an object. In other words, the
reference variable holds a location in memory where the information about the object is
stored.
When you use the dot operator to refer to a field or method, what you are doing is using
the reference to determine the location of the object in memory. Once the location of the
object is determined, we can refer to any field or call any method available to that object.
Null Reference
When a reference is created, but does not contain the address to a valid object, we say
that it is a null reference. If you try to access some information of a null reference, a
NullPointerException is thrown. ( “Exceptions” will be discussed in CSC-212).
String name;
System.out.println(name.length());
//throws a NullPointerException
‘null’ is a reserved word in Java. You can test for it:
if (name != null)
System.out.println(name.length());
//avoids possibility of NullPointerException
//being thrown
Aliases
Two or more references that refer to the same object are called ‘aliases’ of each
other. If an object has multiple references to it, you can change the state of that object
via any of its references.
Assignment statements assign slightly different things if they are operating on primitive
data types as opposed to objects.
For primitive data types, the assignment statement places the value of the right-side
variable into the left hand side.
int n1 = 5;
int n2 = 12;
n2 = n1; //puts a ‘5’ into n2
For objects, the assignment statement puts the address of the right-side object into the left
hand side.
ChessPiece cp1 = new ChessPiece();
ChessPiece cp2 = new ChessPiece();
cp2 = cp1; //assigns the address of cp1 to cp2
cp1 and cp2 are now aliases to the same object. Making a change to the object using
the cp1 reference will also result in changes to the object being pointed to by cp2.
Why? Because cp1 and cp2 are both aliases to the same object.
When you think about it, the assignment statement is not technically doing anything
different between the above two situations. In the first, n1 and n2 are storing integers,
and in the second situation, cp1 and cp2 are storing addresses. In both cases, the type on
the right-side of the assignment statement matches the type on the left-side of the
assignment statement.
Comparing Objects/References
ChessPiece cp1 = new ChessPiece();
ChessPiece cp2 = new ChessPiece();
// cp1==cp2 would return false
cp2 = cp1;
// cp1==cp2 would return true since both
// cp1 and cp2 hold the same value
// (that is, the same address)
‘equals(Object o)’ method v.s. ‘==’
Unless we have overridden one of these methods, they typically do the same thing.
Specifically, they compare the addresses (for objects) or the values (for primitive data
types) and return true if they are the same, and false if they are different.
- ‘==’ for references always returns its value based on the addresses stored inside
those references. In other words, it will only return true if the references are
aliases of each other.
- ‘equals()’ can be altered by the programmer to have a different definition. By
default, however, it compares the addresses stored inside the references. For the
String class, the equals() method has been “overridden” to return true based on the
sequence of characters stored inside the object.
Question: Why do we compare two strings to see if they are the same using the
‘equals(String s)’ method as opposed to using ‘==’?
Question: How might we override ‘equals()’ method to compare two ChessPiece objects?
Garbage Collection
Question: Consider the assignment cp2 = cp1; above. What happens to the old object
being referenced by cp2? Prior to the statement, cp2 was pointing at some object in
memory, but after the assignment, cp2 is now pointing to a different object. (The same
object that cp1 is pointing to).
- Is there any way to get cp2 to point back to its original object if we wanted it to?
- What about all the memory being used by the object that was previously being
referenced by cp2?
Answer: Unless we create a new reference that can point to cp2’s original object, that
object is lost to us forever. This also means that all the memory being used by that object
is just sitting there hogging up space. In C++, numerous objects “lost” in memory could
eventually present a problem. However, the Java runtime has a tool called automatic
garbage collection whereby any objects that do not have references pointing to them have
their memory reclaimed by the system.
-
The programmer can NOT control when the Java runtime will carry out garbage
collection.
Every object can have a method called ‘finalize()’ which the programmer can
define if they wish. This method allows the programmer to carry out some final
tasks prior to garbage collection taking place (e.g. closing files).
Representing real-life scenarios as objects
In object-oriented (“OO”) programming, part of the trick is learning to represent real-live
objects by programming objects. This is not necessarily as difficult as it may sound.
- For example, to categorize a dice object, you really only need one field: to
represent the number of dots (i.e., the side). You can then use your
Math.random() method to simulate rolling of the dice. See Die.java
- To simulate a chess piece, you need only two fields: One to represent the color
(black or white), and another to represent which piece (e.g. bishop, v.s. rook, v.s.
pawn, etc). How you choose to store this information is up to the programmer.
For example, you may store the color as a string, or a char. Alternatively, you
could store the color as an int with 0 = white and 1 = black. It’s up to you.
- To take our chess analogy further, you could have a field representing
the current location on the board. In chess, each square is represented
by a letter and a number. These range from A1 to H8. So you could
store the position of each chess piece as a string.
- You could have a method called void move(String
location) which moves the piece to the location you specify. The
move method could examine the object that called the method and
validate that the piece is allowed to be moved to that location. For
example, a pawn could not move halfway across the board. The
method could either refuse to change the position, or it could require
the user to keep reentering positions until a valid one is provided.
Passing Objects as Arguments (Parameters)
When you pass an argument to a method, we say that the argument is passed by value.
This means that the value of the argument (e.g. an integer, a double, a char, an address) is
passed down to the method. The corresponding formal parameter of the method is then
initialized to that value.
public void doStuff(int number) { … }
//a method with one formal parameter
int x=5;
doStuff(x);
//inside doStuff, the formal parameter ‘number’ is
//initialized to the value of ‘x’
//It is as if there is an invisible ‘number=5’
//statement inside the method.
//What would be output by the following?
System.out.println(x);
It is also possible for a method to accept an object as a parameter instead of a plain old
primitive data type. In this case, it is not entirely accurate to say that you are passing an
actual object to the method, since what you are really passing is a reference to the object.
public void doStuff(ChessPiece cp) { … }
//a method with one formal parameter that is
//a reference to an object
ChessPiece c = new ChessPiece();
doStuff(c);
//inside doStuff, the formal parameter ‘cp’ is
//initialized to the value of ‘c’
//Since ‘c’ is a reference, it stores an address
//and that is what the formal parameter ‘cp’ is
//initialized to.
The key here is that when you pass an object (or more accurately, a reference to an
object) to a method, you are creating an alias to that object. The formal parameter
becomes an alias to the object which was passed to the method. In the above example,
the formal parameter ‘cp’ is an alias to the object ‘c’ that was passed to the method.
Carefully study ParameterPassing.java and be sure that you understand what is taking
place. In this example, we make use of a class called ‘Num’. Objects of type Num have
only one field, an integer. This makes ‘Num’ a pretty useless class, but it is useful in that
we can use it to observe what happens when you pass objects (references) to methods.
Incidentally, notice the use of the ‘toString()’ method in the Num class. The toString()
method is invoked by the println() method when we try to output a Num object in line 24
of ParameterPassing.java .
‘this’ Reference
‘this’ is a reserved word in Java. It is used when an object needs to refer to itself. For
example, if you are inside a method and you need to refer to the calling object (the object
that invoked the method), you can refer to that object by using the keyword ‘this’. We
will discuss ‘this’ further as we progress.
public boolean move(ChessPiece cp) {
if (this.position == cp.position)
// ‘this’ refers to the calling object
return false;
… more code…
}
More on the ‘static’ modifier
Recall that if a method is declared static, this means that we can invoke the method
without even having to have an instantiation of the class. Ordinarily to invoke a method,
we must first specify an object. However, with a static method, we do not need to
instantiate, and can invoke by simply qualifying the method name with the class (not
object) name.
System.out.println(“The square root of 27 is: “ +
Math.sqrt(27) );
Here, the sqrt() method of the class Math is a static method. Therefore we can invoke it
by simply providing the class name. In fact, all methods of the Math class are static.
Note how we have always created main() with a static modifier. This is so that main()can
be instantiated by the interpreter without having to instantiate an object from whatever
class contains main().
Static Variables
We’ve already discussed the ‘scope’ of variables. For example, a variable defined inside
a method is “local” to that method. That is, the variable does not exist outside of that
method. If you attempt to refer to variable outside of its scope, the compiler will give
you an error.
We’ve also seen variables that are tied to a class. For example, the StudentRecord class
we discussed earlier in the quarter has a number of “instance variables” (fields)
including: lastName, firstName, totalHwGrade, etc. These are called instance variables
since the only way you can refer to them is via an instance (object) of the class. This
makes sense, since, for example, the ‘lastName’ field only makes sense in the context of
a StudentRecord object. For instance variables, each object of the class reserves memory
for its own copy of that variable.
However, there are times when you might want a variable to belong to all objects of the
class. That is, you might want the variable to have the same value for all objects of the
class. In this case, each object of the class shares the same memory space for the
variable. If any one object changes the value of the variable, the value is changed for all
objects of that class. These are called static variables. In the StudentRecord class, there
is one such variable, ‘section’ that stores the section number for that class. All objects of
type StudentRecord will have the same value for ‘section’.
Question: Can you remember what the ‘final’ modifier stands for?
Memory space for a static variable is reserved the first time the class is referenced by the
program. (E.g. The first time the class is instantiated).
Note that static methods can not refer to non-static variables. This is because non-static
variables only exist in the context of an object. However, static methods do not exist in
the context of an object, and therefore, know nothing about non-static (“instance”)
variables. Static methods can, of course, access static variables.
See CountInstances.java , and MyClass.java .
Download