Student();
Another way to initialize a reference variable is to hand it a preexisting object: that is, an object ("helium balloon") that is already being referenced ("held onto") by a different reference variable ("hand"). Let's look at an example:
// We declare a reference variable, and instantiate our first Student object. Student x = new Student(); // We declare a second reference variable, but do not instantiate a // second object. Student y; // We pass y a "handle" on the same object that x is holding onto // (x continues to hold onto it, too). We now, in essence, // have two "strings" tied to the same "balloon". y = x;
The conceptual outcome of the preceding code is illustrated in Figure 3-4: two "strings", being held by two different "hands", tied to the same "balloon"—that is, two different reference variables referring to the same physical object in memory.
We therefore see that the same object can have many reference variables simultaneously referring to it; but, as it turns out, any one reference variable can only hold onto/refer to one object at a time. To grab onto a new object handle means that a reference variable must let go of the object handle that it was previously holding onto, if any.
If there comes a time when all handles for a particular object have been let go of, then as we discussed earlier the object is no longer accessible to our program, like a helium balloon that has been let loose. Continuing with our previous example (note highlighted code below and Figures 3-5, 3-6, and 3-7):
// We instantiate our first Student object. Student x = new Student(); // We declare a second reference variable, but do not instantiate a // second object. Student y; // We pass y a "handle" on the same object that x is holding onto // (x continues to hold onto it, too). We now, in essence, // have TWO "strings" tied to the same "balloon". y = x; // We now declare a third reference variable and instantiate a second // Student object. Student z = new Student(); // y now lets go of the first Student object and grabs onto the second. y = z; // Finally, x lets go of the first Student object, and grabs onto // the second, as well; the first Student object is now lost to // the program because we no longer have any reference variables // maintaining a "handle" on it! x = z;
As it turns out, if all of an object's handles are lost, it might seem as though the memory that the object occupies is permanently wasted. (In a language like C++, this is indeed the case, and programmers have to explicitly take care to "reclaim" the memory of an object that is no longer needed before all of its handles are dropped. Failure to do so is a chronic source of problems in C++ programs.) In C# (and all other .NET languages) the common language runtime (CLR) periodically performs garbage collection, a process that automatically reclaims the memory of "lost" objects for us. We'll revisit this topic in Chapter 13.