Java Programming, Lecture Notes # 42, Revised 10/03/99.
Preface
Introduction
Instance Variables
Class Variables
Instance Methods
Class Methods
Constructors
Finalization
Review
Students in Prof. Baldwin's Introductory Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson (except that they are not responsible for detailed information that is specific to C++).
The detailed material on C++ is provided as supplementary material for the benefit of persons already familiar with C++ who are making the transition into Java.
Instance variables, class variables, instance methods, and class methods were introduced in earlier lessons. This lesson builds on the material provided earlier
This lesson also provides some new insights into constructors which were not provided in previous discussions on the use of constructors.
Finally, this lesson takes another pass at the topic of finalization, the process of doing cleanup before an object is reclaimed by the garbage collector.
In both Java and C++, the declaration of a member variable (data member) inside a class definition without use of the static keyword results in an instance variable in objects of that class. (A declaration which uses the static keyword signifies a class variable.)
The significance of an instance variable is that every object instantiated from that class contains its own copy of all instance variables. If you were to examine the memory allocated for each object of that class, you would find that memory has been set aside for each object to contain its own copy of all instance variables of the class.
In other words, since an object is an instance of a class, and since every object has its own copy of a particular data member of the class, then that data member can be thought of as an instance variable. That is the source of the name.
In Java, you access the instance variables associated with a particular object by joining the name of the object with the name of the variable using a period as shown below (note that some authors refer to the period as the dot operator):
myObject.myInstanceVariable |
In C++, you access the instance variables associated with a particular object in either of two ways. One way is to use the name of the object, the dot operator, and the name of the variable just the same as in Java. The other way is to use the name of a pointer variable which points to the object, the pointer operator, and the name of the variable. Both of these ways are shown below: |
myObject.myInstanceVariable myPointerVariable -> myInstanceVariable |
In both Java and C++, the declaration of a data member inside the class using the static keyword results in a class variable.
One of the significant things about a class variable is that all instances of the class (all objects instantiated from the class) share the class variables.
In C++, it is necessary to "re-declare" all class variables outside the class definition, producing a "pseudo-global" variable; that is, a variable that is global insofar as objects of that class are concerned. Such "re-declaration" is not required in Java. |
If you were to find a way to examine the memory allocated for each object, you would find that every objects shares the same memory for storage of each class variable. Although this author isn't certain exactly how this is implemented in Java, it probably means that the memory allocated for each individual object contains some sort of pointer or reference to the memory location where the actual class variable is stored.
Another significant thing about class variables is that in both Java and C++, you can access class variables using the name of the class and the name of the variable. It is not necessary to instantiate an object of the class to access class variables.
In Java, class variables are accessed using the name of the class and the name of the variable joined by the dot operator. The following familiar code fragment accesses the class variable named out of the class named System. In the process, it accesses the println method of the class variable to display a string on the standard output device.
System.out.println("Display my string"); |
In C++, class variables are accessed using the scope resolution operator along with the name of the class and the name of the variable as shown below: |
MyClass::myClassVariable |
Also in both Java and C++, you can access class variables through an instance of the class (object) using the same syntax that you would use to access an instance variable.
Remember that since all objects of the class share the class variable, if any object modifies it, it is modified for all objects of the class. This can be used as a form of inter-object communication.
In Java, class variables are often used with the final keyword to create variables that act like constants. This is a memory efficient approach because you don't need multiple copies of constants.
When you include a member function in a C++ class definition, or include a method in a Java class definition without use of static keyword, this will result in objects of that class containing an instance method.
Although each object of the class does not contain its own copy of the instance method (multiple copies of the method do not exist in memory), the end result is the same as if each object contained its own copy. (If memory were not a concern, each instance might be given its own copy of the method for improved speed.)
When you invoke an instance method using a specific object, if that method refers to instance variables of the class, that method is caused to refer to the specific instance variables of the specific object for which it was invoked. This is the behavior that causes us to refer to the method as an instance method.
Instance methods are invoked in Java by joining the name of the object and the name of the method using the dot operator as shown below:
myObject.myInstanceMethod( ) |
Instance methods are invoked in C++ in several different ways. One way is identical to Java using the name of the object, the dot operator, and the name of the method. Another way is to use the name of a pointer variable which points to the object, the pointer operator, and the name of the method as shown below. |
myObject.myInstanceMethod( ) myObjectPointer -> myInstanceMethod( ) |
Note that instance methods have access to both instance variables and class variables in both Java and C++.
When a member function is included in a C++ class definition or a method is included in a Java class definition, and the static keyword is used, this results in a class method.
One of the significant things about class methods is that they can be invoked without a requirement to instantiate an object of the class.
In Java, a class method can be invoked by joining the name of the class to the name of the method using the dot operator as shown below:
MyClass.myClassMethod() |
In C++, a class method can be invoked using the name of the class, the scope resolution operator, and the name of the method as shown below: |
MyClass::myClassMethod() |
In Java, class methods can only access other class members: class variables and other class methods. They do not have access to the instance variables of the class unless they create a new object and access the instance variables through that object.
Similar considerations hold for C++. |
Constructor Overloading
Default Constructor
Instantiating Objects on the Heap
OutOfMemoryError
Instantiating Objects in Method Calls
Constructors and Inheritance
Constructors and Access Control
Both Java and C++ support the notion of overloaded methods. This means that two or more methods may have the same name so long as they have different argument lists in their signatures.
If you overload a method name, the compiler determines at compile time, on the basis of the arguments provided to the invocation of the method, which version of the method to call in that instance.
Also, both Java and C++ support the notion of a constructor. A constructor is a special method which is used to construct an object. A constructor always has the same name as the class in which it is defined, has no return type specified, and must not include a return statement.
Constructors may be overloaded, so a single class may have more than one constructor, all of which have the same name, but different argument lists.
The primary purpose of a constructor is to initialize the instance variables of an object when the object is instantiated.
The same set of instance variables can often be initialized in more than one way using overloaded constructors.
For example, an object which stores date information might be initialized using a "mm/dd/yy" string format, or might be initialized using three integer values, one for month, one for day, and one for year.
In this case, two constructors could be defined, each of which would accept the initialization data in a different format, and convert that data to the format required to initialize the instance variables in an object of that type.
It is not necessary to define a constructor in either Java or C++. In both cases, if you don't define a constructor, a default constructor is provided automatically on your behalf.
In Java, the default constructor automatically initializes all instance variables to zero or the equivalent of zero.
In C++, the default constructor doesn't perform any initialization. Rather, it simply causes the object to be instantiated in memory and available for use. |
You can think of the default constructor as a constructor which doesn't take any parameters. For that reason, some authors refer to the default constructor as the Noarg constructor.
Note however that you can also define a constructor that doesn't take any parameters, and cause its behavior to differ from the behavior of the default constructor. In this case, your new constructor will replace the default constructor.
In both Java and C++, if you provide any constructors, the default constructor is no longer provided automatically. In the event that you need a parameterized constructor and a constructor which doesn't take parameters, you must provide them both. |
In Java, objects can only be instantiated on the heap.
C++ is less restrictive than Java in this regard, so that in C++, objects can be instantiated in a variety of ways. |
In other words, in order to instantiate an object in Java, you must
Note that there are ways to instantiate objects that don't make explicit use of the new operator, but they also instantiate the object on the heap. They are discussed in the Intermediate and Advanced courses. |
The syntax for instantiating objects is different in Java and C++. |
The following statements show how the constructor is typically used in Java to declare, instantiate, and (optionally) initialize an object:
MyClass myObject = new MyClass(); MyClass myObject = new MyClass(1,2,3); |
These statements perform three actions in one.
In the first case, the default constructor is called which will initialize the instance variables to zero or equivalent.
In the second case, a parameterized constructor is called and the initialization parameters 1, 2, and 3 are passed to that constructor.
In Java, if you attempt to instantiate an object and the Java Virtual Machine cannot allocate the requisite memory (following a run of the garbage collector, if necessary), the system will throw an OutOfMemoryError. You could catch and attempt to process that error if you believe that some sort of recovery is possible.
In C++, the result of running out of memory will depend on the version of compiler that you are using. This is a complex topic in C++, so you need to make certain that you review the documentation received with your compiler to determine how programs produced using that compiler will behave. |
The two statements above return a reference to the new object. The reference is stored in the variable named MyObject.
In both Java and C++, it is also possible to make a call to the constructor without obviously assigning the reference to a variable. This is commonly referred to as an anonymous object.
For example, if a method requires an object of a particular type as an argument, it is allowable to include a call to the constructor of that type of object in the call to the method as illustrated below.
obj.myFunction( new myClassConstructor(1,2,3) );//Java version |
This statement causes an object to be instantiated and initialized and passed to the method where it is used to initialize a method parameter inside the method.
In this case, in order for the program to compile properly, a version of the method must exist which expects to receive an object of this type as a parameter. The argument list for the method will include a reference parameter of the correct type.
In both Java and C++, when a function or method begins execution, all of the parameters are created as local automatic variables. (Automatic variables cease to exist then the method terminates.) In Java, all variables are automatic.
In addition to automatic variables, C++ supports static variables that do not cease to exist when the function terminates. |
The scope of these variables, that come into existence as method parameters, is the full extent of the method.
In this case, the object that is instantiated in conjunction with the method call will be used to initialize the method parameter variable when the method begins execution. That is how it is saved.
Since it will be automatic, when the method terminates, it will become eligible for garbage collection in Java.
Because it is be automatic, when the function terminates, it will destruct in C++. |
In both Java and C++, you declare and implement a constructor just like you would implement any other method in your class, except that you do not specify a return type (note that there are some other differences if the superclass contains a parameterized constructor and you elect to invoke it).
As mentioned earlier, the name of the constructor must be the same as the name of the class. Your constructor may be overloaded, meaning that you may define more than one constructor having the same name but a different argument list in the signature.
The following Java application illustrates some of the constructor concepts discussed above.
/*File cnstrct1.java Copyright 1997, R.G.Baldwin This is a Java application which illustrates: 1. Instantiating an object by calling the default constructor. 2. Instantiating an object by calling a parameterized constructor as a parameter to a function call. The program was tested using JDK 1.1.3 under Win95. The program displays the following output: Starting Program Object contains 100 **********************************************************/ class NewClass{ int instanceVar; //-----------------------------------------------------// NewClass(int inData){//parameterized constructor instanceVar = inData; //save inData }//end parameterized constructor //-----------------------------------------------------// void showInstanceVar(){ System.out.println("Object contains " + instanceVar); }//end showInstanceVar }//end NewClass //=======================================================// class cnstrct1 { //define the controlling class //The following method receives an object and calls one // of the methods of the object to display the data // contained in the object. void myFunction(NewClass objIn){ objIn.showInstanceVar(); }//end myFunction //-----------------------------------------------------// public static void main(String[] args){ //main method System.out.println("Starting Program"); //Instantiate an object of this type by calling // default constructor for the controlling class cnstrct1 obj = new cnstrct1(); //Call instance method with call to parameterized // constructor as a parameter obj.myFunction(new NewClass(100)); }//end main }//End cnstrct1 class. |
Constructors take on a special significance in cases where inheritance is involved.
In cases of inheritance, you will want the subclass to cause the constructor for the superclass to execute first to initialize those instance variables which derive from the superclass.
In C++, this sequence of events is accomplished using a fairly complex syntax in the definition of the constructor. In Java, the syntax is less complex. |
In Java, to cause the constructor of the superclass to be invoked prior to the execution of the body of the constructor of the subclass, you simply include the following statement as the first statement in the constructor for the subclass:
super(optional parameters); |
This will cause the constructor for the superclass to execute, using the optional parameters for initialization.
Although it isn't obvious, in all cases that you don't provide a super() statement ahead of the first executable statement in your constructor, the system inserts and executes such a statement on your behalf. This causes the noarg method of the superclass to be invoked before any of the code in your constructor is invoked.
Therefore, the construction of all objects in Java begins with the construction of that part of the object attributed to the class named Object which is the superclass of all classes. Construction then works its way down the inheritance tree (from the root to the branches) until the code in your constructor is finally executed.
The following Java application illustrates use of the super keyword to call a superclass constructor from a subclass.
/*File cnstrct2.java Copyright 1997, R.G.Baldwin This is a Java application which extends the application named cnstrct1.java. In addition to the concepts illustrated in that application, this application illustrates the use of super() in a subclass constructor to call the constructor in the superclass. Note that this program will not compile unless the replacement for the default constructor is provided. Note also that the program will not compile if an output statement is placed ahead of the call to super() in the subclass constructor. The program displays the following output: Starting Program Default constructor invoked Entering SuperClass constructor Back in SubClass constructor Object contains 100 **********************************************************/ class SuperClass{ int instanceVar; SuperClass(){//replacement for default constructor System.out.println("Default constructor invoked"); }//end default constructor //-----------------------------------------------------// SuperClass(int inData){//parameterized constructor System.out.println("Entering SuperClass constructor"); instanceVar = inData;//put inData in instance variable }//end parameterized constructor //-----------------------------------------------------// void showInstanceVar(){ System.out.println("Object contains " + instanceVar); }//end showInstanceVar }//end SuperClass //=======================================================// class SubClass extends SuperClass{ SubClass(int incomingData){// constructor //The following statement will not compile. The call // to super() must be the first thing in a method if // it appears at all. //System.out.println("Entering SubClass constructor"); super(incomingData);//call the SuperClass constructor System.out.println("Back in SubClass constructor"); }//end SubClass constructor }//end SubClass //=======================================================// class cnstrct2 { //define the controlling class //The following method receives an object and calls one // of the methods of the object to display the data // contained in the object. In this case, both the // method and the instance variable are inherited from // the SuperClass. void myFunction(SubClass objIn){ objIn.showInstanceVar(); }//end myFunction //-----------------------------------------------------// public static void main(String[] args){ //main method System.out.println("Starting Program"); //Instantiate an object of the SuperClass type to // illustrate the requirement for the default // constructor in the SuperClass class new SuperClass(); //Instantiate an object of this type by calling // default constructor cnstrct2 obj = new cnstrct2(); //Call function with call to parameterized SubClass // constructor as a parameter. The constructor in the // subclass passes the parameter along to the // constructor in the superclass. obj.myFunction(new SubClass(100)); }//end main }//End cnstrct2 class. |
Access control also has a special meaning with regard to constructing objects.
The specified access control determines what other objects can instantiate objects of the class.
The following is a non-exhaustive description of what access control means with regards to constructing new objects.
Any code in any object can access and instantiate an object of any class in any package that has a public Only code in the same package can access and instantiate an object of a class that does not have a public modifier (if the class has an abstract modifier, it can't be instantiated). |
At this point, you may want to review earlier lesson on access control to refresh your memory as to what access control means to object construction.
The above results are obtained when you apply an access modifier to the class. According to The Java Tutorial, you can also apply access modifiers directly to the constructor with the following results:
private |
Finalize is Not a Destructor
Finalization and Inheritance
To begin with, finalization is a much more complex topic than some authors might lead you to believe. This will be apparent to C++ programmers who are familiar with the notion of destructors as they are used in C++.
It is strongly recommended that you avail yourself of the opportunity (if it still exists) to review the "book in progress" named Thinking in Java http://www.eckelobjects.com being written by Bruce Eckel. Pay particular attention to Eckel's discussion of C++ destructors and the Java finalize method.
The following treatment is based primarily on The Java Tutorial by Campione and Walrath which is somewhat light in the area of the finalize method as supplemented by Bruce Eckel's comments.
Every object has a finalize method which is inherited from the class named Object. Before an object is reclaimed by the garbage collector, the finalize method for the object is called.
If you need to do any cleanup associated with memory management, you can override the finalize method and place the cleanup code in that method.
If you need to do cleanup not associated with memory management before the object is reclaimed, and you are not in a hurry to have that cleanup done, you can invoke runFinalizersOnExit(true) early in your program, override the finalize method, and be confident that your objects will be finalized before the program terminates.
Note that the following two methods were added to JDK 1.1 to deal with some of finalization uncertainty that existed in JDK 1.0.2.
|
In Java, if you need cleanup code to be executed on a timely basis, it is the responsibility of the programmer to write special methods for non-memory cleanup, and to purposely execute those methods at the appropriate points in time.
Note that The Java Tutorial points out that if your class inherits from a superclass that also has a finalize method, you should use the following syntax to call that method after your finalize method has performed all of its duties.
super.finalize(); |
For construction, we usually want to construct from the root of the inheritance tree to the branches.
For finalization, we usually want to finalize in the reverse order, from the branches back to the root.
Q - In Java, class variables are often used with the __________ keyword to create variables that act like constants.
A - The final keyword.
Q - In Java, the ___________ keyword is used to declare a class variable.
A - The static keyword.
Q - In Java, the ___________ keyword is used to declare a class method.
A - The static keyword.
Q - When you include a method in a Java class definition without use of static keyword, this will result in objects of that class containing an instance method: True or False? If false, explain why.
A - True.
Q - Normally each object contains its own copy of each instance method: True or False?
A - False, multiple copies of the method do not normally exist in memory.
Q - When you invoke an instance method using a specific object, if that method refers to instance variables of the class, that method is caused to refer to the specific instance variables of the specific object for which it was invoked: True or False? If false, explain why.
A - True.
Q - Instance methods are invoked in Java using the name of the object, the colon, and the name of the method as shown below: True or False? If false, explain why.
A - False. Use the period or dot operator, not the colon.
Q - Instance methods have access to both instance variables and class variables in Java: True or False. If false, explain why.
A - True.
Q - Class methods have access to both instance variables and class variables in Java: True or False. If false, explain why.
A - False. Class method can only access other class members.
Q - What are the two most significant characteristics of class methods?
A - 1. Class methods can only access other class members. 2. Class methods can be accessed using only the name of the class. An object of the class is not required to access class methods.
Q - In Java, a class method can be invoked using the name of the class, the colon, and the name of the method as shown below: True or False? If false, explain why.
A - False. You must use the period or dot operator, not the colon.
Q - What is meant by overloaded methods?
A - The term overloaded methods means that two or more methods may have the same name so long as they have different argument lists.
Q - If you overload a method name, the compiler determines at run time, on the basis of the arguments provided to the invocation of the method, which version of the method to call in that instance: True or False? If false, explain why.
A - False. The determination is made at compile time.
Q - A constructor is a special method which is used to construct an object. A constructor always has the same name as the class in which it is defined, and has no return type specified. True or False? If false, explain why.
A - True.
Q - Constructors may be overloaded, so a single class may have more than one constructor, all of which have the same name, but different argument lists: True or False. If false, explain why.
A - True
Q - What is the purpose of a parameterized constructor?
A - The purpose of a parameterized constructor is to initialize the instance variables of an object when the object is instantiated.
Q - The same set of instance variables can often be initialized in more than one way using overloaded constructors: True or False? If false, explain why.
A - True.
Q - It is not necessary to provide a constructor in Java. True or False? If false, explain why.
A - True. .
Q - You can think of the default constructor as a constructor which doesn't take any parameters: True or False? If false, explain why.
A - True.
Q - In Java, if you provide any constructors, the default constructor is no longer provided automatically: True or False? If false, explain why.
A - True.
Q - In Java, if you need both a parameterized constructor and a constructor which doesn't take parameters (often called a default constructor), you must provide them both: True or False? If false, explain why.
A - True.
Q - In Java, you can instantiate objects in static memory at compile time, or you can use the new operator to request memory from the operating system at runtime and use the constructor to instantiate the object in that memory: True or False? If false, explain why.
A - False. In Java, objects can only be instantiated on the heap at runtime.
Q - Provide a code fragment consisting of a single statement that illustrates how the constructor is typically used in Java to declare, instantiate, and initialize an object. Assume a parameterized constructor with three parameters of type int.
A - MyClass myObject = new MyClass(1,2,3);
Q - Provide a code fragment consisting of a single statement that illustrates how the default constructor is typically used in Java to declare and instantiate an object.
A - MyClass myObject = new MyClass();
Q - What are the three actions performed by the following statement?
MyClass myObject = new MyClass(1,2,3);
A - This statement performs three actions in one.
Q - In Java, if you attempt to instantiate an object and the Java Virtual Machine cannot allocate the requisite memory, the system will: ________________________________________.
A - Throw an OutOfMemoryError.
Q - The following is a valid method call: True or False. If false, explain why.
obj.myFunction(new myClassConstructor(1,2,3) );//Java version |
A - True.
Q - In Java, when a method begins execution, all of the parameters are created as local automatic variables: True or False? If false, explain why.
A - True.
Q - In the following statement, an object is instantiated and initialized and passed as a parameter to a function. What will happen to that object when the function terminates?
obj.myFunction(new myClassConstructor(1,2,3) );//Java version
A - It will become eligible for garbage collection.
Q - In Java, you declare and implement a constructor just like you would implement any other method in your class, except that: _______________________________________________
A - you do not specify a return type and must not include a return statement.
Q - The name of the constructor must be the same as the name of the ___________________.
A - class.
Q - Usually in cases of inheritance, you will want the subclass to cause the constructor for the superclass to execute last to initialize those instance variables which derive from the superclass: True or False? If false, explain why.
A - False. You will want the subclass to cause the constructor for the superclass to execute first.
Q - Provide a code fragment that you would include at the beginning of your constructor for a subclass to cause the constructor for the superclass to be invoked prior to the execution of the body of the constructor.
A - super(optional
parameters);Q - Every object has a finalize method which is inherited from the class named ________________.
A - object.
Q - Before an object is reclaimed by the garbage collector, the _______________ method for the object is called.
A - finalize
Q - In Java, the destructor is always called when an object goes out of scope: True or False? If false, explain why.
A - False. Java does not support the concept of a destructor.
Q - Write a Java program that meets the following specifications.
/*File SampProg18.java from lesson 42 Copyright 1997, R.G.Baldwin Without viewing the solution that follows, write a Java application that illustrates: 1. Instantiating an object by calling the default constructor. 2. Instantiating an object by calling a parameterized constructor as a parameter to a function call. The program should display the following output: Starting Program Object contains 100 Terminating, Baldwin **********************************************************/ class TestClass{ int instanceVar; TestClass(int inData){//parameterized constructor instanceVar = inData;//put inData in instance variable }//end parameterized constructor void showInstanceVar(){ System.out.println( "Object contains " + instanceVar); }//end showInstanceVar }//end TestClass class SampProg18 { //define the controlling class public static void main(String[] args){ //main method System.out.println("Starting Program"); //Instantiate obj of this type by calling default // constructor SampProg18 obj = new SampProg18(); //Call function with call to parameterized constructor // as a parameter obj.myFunction(new TestClass(100)); System.out.println("Terminating, Baldwin"); }//end main //The following function receives an object and calls // one of its methods to display the data contained in // the object. void myFunction(TestClass objIn){ objIn.showInstanceVar(); }//end myFunction }//End SampProg18 class. |
Q - Write a Java program that meets the following specifications.
/*File SampProg19.java from lesson 42 Copyright 1997, R.G.Baldwin Without viewing the solution which follows, write a Java application that illustrates the use of java.lang.system.runFinalizersOnExit(). The output from the program should be: Terminating, Baldwin Finalizing TestClassA object Finalizing TestClassB object **********************************************************/ class TestClassA{ protected void finalize(){//override finalization method System.out.println("Finalizing TestClassA object"); }//end overridden finalize() method }//end TestClassA class TestClassB{ protected void finalize(){//override finalization method System.out.println("Finalizing TestClassB object"); }//end overridden finalize() method }//end TestClassB class SampProg19{//controlling class public static void main(String[] args){//main method //Guarantee finalization of all objects on exit System.runFinalizersOnExit(true); //Instantiate two objects to be finalized TestClassA objA = new TestClassA(); TestClassB objB = new TestClassB(); System.out.println("Terminating, Baldwin"); }//end main }//end class SampProg19 |
-end-