A:
Passing & Returning Objects
By now you should be reasonably comfortable with the idea that when you’re “passing” an object, you’re actually passing a reference.
In many programming languages you can use that language’s “regular” way to pass objects around, and most of the time everything works fine. But it always seems that there comes a point at which you must do something irregular, and suddenly things get a bit more complicated (or in the case of C++, quite complicated). Java is no exception, and it’s important that you understand exactly what’s happening as you pass objects around and manipulate them. This appendix will provide that insight.
Another way to pose the question of this appendix, if you’re coming from a programming language so equipped, is “Does Java have pointers?” Some have claimed that pointers are hard and dangerous and therefore bad, and since Java is all goodness and light and will lift your earthly programming burdens, it cannot possibly contain such things. However, it’s more accurate to say that Java has pointers; indeed, every object identifier in Java (except for primitives) is one of these pointers, but their use is restricted and guarded not only by the compiler but by the run-time system. Or to put it another way, Java has pointers, but no pointer arithmetic. These are what I’ve been calling “references,” and you can think of them as “safety pointers,” not unlike the safety scissors of elementary school—they aren’t sharp, so you cannot hurt yourself without great effort, but they can sometimes be slow and tedious.
Passing references around
When you pass a reference into a method, you’re still pointing to the same object. A simple experiment demonstrates this:
//: appendixa:PassReferences.java
// Passing references around.
import com.bruceeckel.simpletest.*;
public class PassReferences {
private static Test monitor = new Test();
public static void f(PassReferences h) {
System.out.println("h inside f(): " + h);
}
public static void main(String[] args) {
PassReferences p = new PassReferences();
System.out.println("p inside main(): " + p);
f(p);
monitor.expect(new String[] {
"%% p inside main\\(\\): PassReferences@[a-z0-9]+",
"%% h inside f\\(\\): PassReferences@[a-z0-9]+"
});
}
} ///:~
The method toString( ) is automatically invoked in the print statements, and PassReferences inherits directly from Object with no redefinition of toString( ). Thus, Object’s version of toString( ) is used, which prints out the class of the object followed by the address where that object is located (not the reference, but the actual object storage). The output looks like this:
p inside main(): PassReferences@ad3ba4
h inside f(): PassReferences@ad3ba4
You can see that both p and h refer to the same object. This is far more efficient than duplicating a new PassReferences object just so that you can send an argument to a method. But it brings up an important issue.