Published: January 1, 2004
By Richard G. Baldwin
Purpose
The purpose of this miniseries is to help you study for the Advanced Placement Examinations designed by the College Board.
Once you understand everything in this miniseries, plus the material in the lessons that I published earlier on Java Data Structures, you should understand the Java programming features that the College Board considers essential for the first two semesters of object-oriented programming education at the university level.
Approach
These lessons provide questions, answers, and explanations designed to help you to understand the subset of Java features covered by the Java Advanced Placement Examinations (as of October, 2001).
Please see the first lesson in the miniseries entitled Java Advanced Placement Study Guide: Introduction to the Lessons, Primitive Types, for additional background information. The previous lesson was entitled The super Keyword, final Keyword, and static Methods.
Supplementary material
In addition to the material in these lessons, I recommend that you also study the other lessons in my extensive collection of online Java tutorials, which are designed from a more conventional textbook approach. You will find those lessons published at Gamelan.com. However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there. You will find a consolidated index at Baldwin's Java Programming Tutorials.
What is Included?
Click here for a preview of the
Java
programming features covered by this lesson.
public class Ap110{ |
2. What output is produced by the following program?
public class Ap111{ |
3. What output is produced by the following program?
public class Ap112{ |
4. What output is produced by the following program? Note that 6.283185307179586 is a correct numeric value.
public class Ap113{ |
5. What output is produced by the following program? Note that 6.283185307179586 is a correct numeric value.
public class Ap114{ |
6. What output is produced by the following program? Note that 6.283185307179586 is a correct numeric value.
public class Ap115{ |
7. What output is produced by the following program? Note that 6.283185307179586 is a correct numeric value.
public class Ap116{ |
8. What output is produced by the following program?
public class Ap117{ |
9. What output is produced by the following program?
public class Ap118{ |
10. What output is produced by the following program?
public class Ap119{ |
Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two. He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.
Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.
public static final MY_CONSTANT
= initialization expression;
There are at least four ways to establish initial values for instance variables (you may be able to think of others):
The following fragment illustrates the first
two
of those four ways.
class Worker{ |
In the above fragment, the instance variables named myInt and myDouble receive their initial values from initialization expressions. In these two cases, the initialization expressions are very simple. They are simply literal expressions. However, they could be much more complex if needed.
The variable named myBoolean in the above fragment is allowed to take on its default value of false.
Replacing the default noarg constructor
The next fragment shows one of the two
overloaded
constructors in the class named Worker. This constructor
is
a replacement for the default noarg constructor.
class Worker{ |
Using hard-coded values for initialization
This fragment illustrates the third of the four ways listed earlier to establish the initial value of the instance variables of an object of the class named Worker. In particular, this fragment assigns the hard-coded value 20 to the instance variable named myInt, thus overwriting the value of 100 previously established for that variable by an initialization expression.
(All objects instantiated from the Worker class using this noarg constructor would have the same initial value for the variable named myInt.)Note, that this constructor does not disturb the initial values of the other two instance variables that were earlier established by an initialization expression, or by taking on the default value. Thus, the initial values of these two instance variables remain as they were immediately following the declaration of the variables.
Initial values using this noarg constructor
When an object of the Worker class is instantiated using this constructor and the values of the three instance variables are displayed, the results are as shown below:
20 222.0 false
The value of myInt is 20 as established by the constructor. The value of myDouble is 222.0 as established by the initialization expression, and the value of myBoolean is false as established by default.
Using constructor parameters for initialization
The next fragment shows the last of the four
ways
listed earlier for establishing the initial value of an instance
variable.
public class Ap119{ |
A parameterized constructor
The above fragment shows the second of two overloaded constructors for the class named Worker. This constructor uses two incoming parameter values to establish the values of two of the instance variables, overwriting whatever values may earlier have been established for those variables.
The above fragment uses this constructor to instantiate an object of the Worker class, assigning incoming parameter values of 5 and true to the instance variables named myInt and myBoolean respectively. This overwrites the value previously placed in the variable named myInt by the initialization expression. It also overwrites the default value previously placed in the instance variable named myBoolean.
(Note that this constructor doesn't disturb the value for the instance variable named myDouble that was previously established through the use of an initialization expression.)Initial values using parameterized constructor
After instantiating the new object, this fragment causes the values of all three instance variables to be displayed. The result is:
5 222.0 true
As you can see, the values contained in the instance variables named myInt and myBoolean are the values of 5 and true placed there by the constructor, based on incoming parameter values. The value in the instance variable named myDouble is the value placed there by the initialization expression when the variable was declared.
Default initialization
If you don't take any steps to initialize instance variables, they will be automatically initialized. Numeric instance variables will be initialized with zero value for the type of variable involved. Instance variables of type boolean will be initialized to false. Instance variables of type char will be initialized to a Unicode value with all 16 bits set to zero. Reference variables will be initialized to null.
Initialization expression
If you provide an initialization expression for an instance variable, the value of the expression will overwrite the default value, and the value of the initialization expression will become the initial value for the instance variable.
Assignment in constructor code
If you use an assignment statement in a constructor to assign a value to an instance variable, that value will overwrite the value previously placed in the instance variable either by default, or by use of an initialization expression.
Note that some of this initialization information goes beyond the testing requirements of the AP CS exam.
When you define a class, you are not required
to define a constructor for the class. If you do not define a
constructor
for the class, a default constructor that takes no arguments will be
provided
on your behalf. You can instantiate new objects of the class by
applying
the new operator to the default constructor as shown in the
following
code fragment from Question 8.
new Worker().display(); |
Behavior of the default constructor
As illustrated in Question 8, when you don't provide a constructor that purposely initializes the values of instance variables, they will automatically be initialized to the default values described in Question 8.
Defining overloaded constructors
You can also define one or more overloaded constructors having different formal argument lists. The typical intended purpose of such constructors is to use incoming parameter values to initialize the values of instance variables in the new object.
A parameterized constructor
This is illustrated in the following code fragment. This
fragment
receives two incoming parameters and uses the values of those two
parameters
to initialize the values of two instance variables belonging to the new
object.
class Worker{ |
If you define any constructors ...
However, there is a pitfall that you must never forget.
If you define any constructors in your new class, you must define all constructors that will ever be required for your new class.If you define any constructors, the default constructor will no longer be provided automatically. Therefore, if a constructor that takes no arguments will ever be needed for your new class, and you define one or more parameterized constructors, you must define the noarg constructor when you define your class.
A parameterized constructor for Worker
The class named Worker in this program defines a constructor that receives two incoming parameters, one of type int and the other of type boolean. It uses those two incoming parameters to initialize two instance variables of the new object.
Oops!
However, it does not define a constructor with no arguments in the formal argument list (commonly called a noarg constructor).
Invoking the missing noarg constructor
The following code in the main()
method
of the controlling class attempts to instantiate two objects of the Worker
class. The first invocation of the constructor passes no
parameters
to the constructor. Thus, it requires a noarg constructor
in order to instantiate the object.
public class Ap118{ |
A compiler error
Since there is no constructor defined in the Worker class with an empty formal argument list (and the default version is not provided), the program produces the following compiler error.
Ap118.java:11: cannot resolve symbol
symbol : constructor Worker
()
location: class Worker
new Worker().display();
According to the subset document, the AP CS exam apparently doesn't cover the fact that all instance variables are automatically initialized to default values if the author of the class doesn't take explicit steps to cause them to initialized to other values. However, if you are going to program in Java, you need to know that.
The default values
Numeric variables are automatically initialized to zero, while boolean variables are automatically initialized to false. Instance variables of type char are initialized to a Unicode value with all 16 bits set to zero. Reference variables are initialized to null.
Compared to many programming environments, the Java compiler is very forgiving. However, there is a limit to how far even the Java compiler is willing to go to keep us out of trouble.
Initializing the value of a static variable
We can initialize the value of a static variable using an initialization expression as follows:
public static final MY_CONSTANT
= initialization expression;
Important point
It is necessary for the compiler to be able to evaluate the initialization expression when it is encountered.
Illegal forward reference
This program attempts to use an initialization expression that makes use of the value of another static variable that has not yet been established at that point in the compilation process. As a result, the program produces the following compiler error under JDK 1.3.
Ap116.java:18: illegal forward reference
= 2 * myPI;
Reverse the order of the variable declarations
The problem can be resolved by reversing the order of the two static
variable
declarations that are highlighted in boldface in the following revised
version of the program.
public class Ap116{ |
This revised version of the program compiles and executes successfully.
Question 5 illustrated the fact that a public static final member variable of a class can be accessed via a reference to an object instantiated from the class.
Not the only way to access a static variable
However, that is not the only way in which static member variables can be accessed. More importantly, publicstatic member variables of a class can be accessed simply by referring to the name of the class and the name of the member variable joined by a period.
(Depending on other factors, it may not be necessary for the static variable to also be declared public, but that is the most general approach.)A public static final member variable
In this program, the Worker class
declares
and initializes a public static final member variable named twoPI
as shown in the following fragment.
class Worker{ |
Accessing the static variable
The single statement in the main()
method
of the controlling class accesses and displays the value of the public
static final member variable named twoPI as shown in the
following
fragment.
public class Ap115{ |
Objects share one copy of static variables
Basically, when a member variable is declared static, no matter how many objects are instantiated from a class (including no objects), they all share a single copy of the variable.
Sharing can be dangerous
This sharing of a common variable leads to the same kind of problems that have plagued programs that use global variables for years. If the code in any object changes the value of the static variable, it is changed insofar as all objects are concerned.
Should you use non-final static variables?
Most authors will probably agree that in most cases, you should not use static variables unless you also make them final.
(There are some cases, such as counting the number of objects instantiated from a class, where a non-final static variable may be appropriate. However, the appropriate uses of non-final static variables are few and far between.)Should you also make static variables public?
If you make your variables static and final, you will often also want to make the public so that they are easy to access. There are numerous examples in the standard Java class libraries where variables are declared as public, static, and final. This is the mechanism by which the class libraries create constants and make them available for easy access on a widespread basis.
The Color class
For example, the Color class defines a number of public static final variables containing the integer values that represent generic colors such as orange, pink, and magenta. If you need generic colors and not custom colors, you can easily access and use these color values without the requirement to mix red, green, and blue to produce the desired color values.
This program declares a public static final member variable
named
twoPI
in the class named Worker, and properly initializes it when it
is
declared. From that point forward in the program, this member
variable
behaves like a constant, meaning that any code that attempts to change
its value will cause a compiler error (as in the previous program).
class Worker{ |
Accessing the static variable
The following single statement that appears in the main()
method
of the controlling class instantiates a new object of the Worker
class, accesses, and displays the public static final member
variable
named
twoPI.
public static void main( |
(Note for future discussion that the variable named twoPI is accessed via a reference to an object instantiated from the class named Worker.)This causes the double value 6.283185307179586 to be displayed on the standard output device.
When a member variable of a class (not a local variable) is declared final, its value must be established when the variable is declared. This program attempts to assign a value to a final member variable after it has been declared, producing the following compiler error under JDK 1.3.
Ap113.java:20: cannot assign a value to
final variable twoPI
twoPI = 2 * Math.PI;
This program illustrates two different uses of the this keyword, one of which is tested on the AP CS exam, and one which is apparently not tested on the AP CS exam.
Disambiguating a reference to a variable
Consider first the use of this that is
not covered on the exam, as illustrated in the following code fragment.
class Worker{ |
Very common usage
The code in the above fragment is commonly used by many Java programmers. I believe that all aspiring Java programmers need to know how to read such code, even if they elect not to use it. In addition, understanding this code should enhance your overall understanding of the use and nature of the this keyword.
A parameterized constructor
The above fragment shows a parameterized constructor for the class named Worker. This constructor illustrates a situation where there is a local parameter named data that has the same name as an instance variable belonging to the object.
Casting a shadow
The existence of the local parameter named data casts a shadow on the instance variable having the same name, making it inaccessible by using its name alone.
(A local variable having the same name as an instance variable casts a similar shadow on the instance variable.)In this shadowing circumstance, when the code in the constructor refers simply to the name data, it is referring to the local parameter having that name. In order for the code in the constructor to refer to the instance variable having the name data, it must refer to it as this.data.
In other words ...
In other words, this.data is a reference to an instance variable named data belonging to the object being constructed by the constructor.
Not always necessary
You could always use this syntax to refer to an instance variable of the object being constructed if you wanted to. However, the use of this syntax is necessary only when a local parameter or variable has the same name as the instance variable and casts a shadow on the instance variable. When this is not the case, you can refer to the instance variable simply by referring to its name without the keyword this.
Finally, the main point ...
Now consider the main point of this
program.
The following fragment shows the main() method of the
controlling
class for the application.
public class Ap112{ |
Four different objects of type Worker
The code in the above fragment instantiates four different objects from the class named Worker, passing a different value to the constructor for each object. Thus, individual instance variable in each of the four objects contain the int values 11, 22, 33, and 44 respectively.
Invoke an instance method on one object
Then the code in the main() method invokes the instance method named doThis() on only one of the objects, which is the one referred to by the reference variable named obj2.
An overridden toString() method of the Worker class is eventually invoked to return a string representation of the value stored in the instance variable named data for the purpose of displaying that value on the standard output device.
Overridden toString() method
The next fragment shows the overridden toString()
method for the Worker class. As you can see, this
overridden
method constructs and returns a reference to a string representation of
the int value stored in the instance variable named data.
Thus, depending on which object the toString() method is
invoked
on, different string values will be returned by the overridden method.
public String toString(){ |
Passing reference to this object to println method
The next fragment shows the doThis()
instance
method belonging to each object instantiated from the Worker
class.
When this method is invoked on a specific object instantiated from the
Worker
class, it uses the this keyword to pass that specific object's
reference
to the println() method. The println() method
uses
that reference to invoke the toString() method on that specific
object. This, in turn causes a string representation of the value
of the instance variable named data belonging to that specific
object
to be displayed.
public void doThis(){ |
The bottom line
In this program, the instance variable in the object referred to by obj2 contains the value 22. The instance variables in the other three objects instantiated from the same class contain different values.
The bottom line is that the following
statement
in the main() method causes the value 22 to be displayed on the
standard output device. Along the way, the this keyword
is
used to cause the println() method to get and display the value
stored in a specific object, and to ignore three other objects that
were
instantiated from the same class.
obj2.doThis(); |
The key to an understanding of this program lies in an understanding
of the boldface portion of the single statement that appears in the
method
named doThis(), as shown in the following fragment.
public void doThis(){ |
The keyword named this has several uses in Java, some of which are explicit, and some of which take place behind the scenes.
What do you need to know about the this keyword?
According to the subset document, which describes the topics that are covered on the CS Advanced Placement exam, "The use of this is restricted to passing the implicit parameter in its entirety to another method (e.g. obj.method(this)) and to descriptions such as "the implicit parameter this"."
That is exactly what this program does. But what is the implicit parameter named this anyway?
Every object holds a reference to itself
This implicit reference can be accessed using the keyword this in a non-static (instance) method belonging to the object. (The implicit reference named this cannot be accessed from within a static method for reasons that won't be discussed here.)
Invoking an instance method
An instance method can only be invoked by referring to a specific
object
and joining that object's reference to the name of the instance method
using a period as the joining operator. This is illustrated in
the
following statement, which invokes the method named doThis() on
a reference to an object of the class named Worker.
new Worker().doThis(); |
An anonymous object
The above statement creates an anonymous object of the class named Worker. (Remember, an anonymous object is an object whose reference is not assigned to a named reference variable.)
The code to the left of the period returns a reference to the new object. Then the code invokes the instance method named doThis() on the reference to the object.
Which object is this object?
When the code in the instance method named doThis() refers
to
the keyword this, it is a reference to the specific object on
which
the method was invoked. The statement in the following fragment
passes
a reference to that specific instance of the Worker class to a
method
named helpMe() in a new object of the Helper class.
public void doThis(){ |
A little help here please
The helpMe() method is shown in the following fragment.
class Helper{ |
Using the incoming reference
The code in the helpMe() method uses the incoming reference to the object of the Worker class to invoke the getData() method on that object.
Thus code in the helpMe() method is able to invoke a method in the object containing the method that invoked the helpMe() method in the first place.
A callback scenario
When a method in one object invokes a method in another object, passing this as a parameter, that makes it possible for the method receiving the parameter to make a callback to the object containing the method that passed this as a parameter.
The getData() method returns a String representation of the int instance variable with a value of 33 contained in the object of the Worker class.
Display the value
The code in the helpMe() method causes that string to be displayed on the computer screen.
And the main point is ...
Any number of objects can be instantiated from a given class. A given instance method can be invoked on any of those objects. When the code in such an instance method refers to this, it is referring to the specific object on which it was invoked, and is not referring to any of the many other objects that may have been instantiated from the same class.
The purpose of this question is simply to give you a wakeup call. The declaration for the method named getData() indicates that the method returns a reference to an object of the class String. However, the code in the method attempts to return an int. The program produces the following compiler error under JDK 1.3.
found : int
required: java.lang.String
return data;
Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two. He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.
Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.
-end-