Published: February 4, 2002
By Richard G. Baldwin
Purpose
The purpose of this miniseries is to help you study for, and successfully complete, 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.
Hopefully, that will help you to take and successfully complete the Advanced Placement Examinations.
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 lesson immediately prior to this one was entitled Java Advanced Placement Study Guide: Relational Operators, Increment Operator, and Control Structures.
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 Ap039{ public static void main( String args[]){ new Worker().doLogical(); }//end main() }//end class definition class Worker{ public void doLogical(){ int x = 5, y = 6; if((x > y) || (y < x/0)){ System.out.println("A"); }else{ System.out.println("B"); }//end else }//end doLogical() }//end class definition |
2. What output is produced by the following program?
public class Ap040{ public static void main( String args[]){ new Worker().doLogical(); }//end main() }//end class definition class Worker{ public void doLogical(){ int x = 5, y = 6; if((x < y) || (y < x/0)){ System.out.println("A"); }else{ System.out.println("B"); }//end else }//end doLogical() }//end class definition |
3. What output is produced by the following program?
public class Ap041{ public static void main( String args[]){ new Worker().doLogical(); }//end main() }//end class definition class Worker{ public void doLogical(){ int x = 5, y = 6; if(!(x < y) && !(y < x/0)){ System.out.println("A"); }else{ System.out.println("B"); }//end else }//end doLogical() }//end class definition |
4. What output is produced by the following program?
public class Ap042{ public static void main( String args[]){ new Worker().doCast(); }//end main() }//end class definition class Worker{ public void doCast(){ boolean x = true; int y = (int)x; System.out.println(y); }//end doCast() }//end class definition |
5. What output is produced by the following program?
public class Ap043{ public static void main( String args[]){ new Worker().doCast(); }//end main() }//end class definition class Worker{ public void doCast(){ double w = 3.7; double x = -3.7; int y = (int)w; int z = (int)x; System.out.println(y + " " + z); }//end doCast() }//end class definition |
6. What output is produced by the following program?
public class Ap044{ public static void main( String args[]){ new Worker().doCast(); }//end main() }//end class definition class Worker{ public void doCast(){ double w = 3.5; double x = -3.499999999999; System.out.println(doIt(w) + " " + doIt(x)); }//end doCast() private int doIt(double arg){ if(arg > 0){ return (int)(arg + 0.5); }else{ return (int)(arg - 0.5); }//end else }//end doIt() }//end class definition |
7. What output is produced by the following program?
public class Ap045{ public static void main( String args[]){ new Worker().doConcat(); }//end main() }//end class definition class Worker{ public void doConcat(){ double w = 3.5; int x = 9; boolean y = true; String z = w + "/" + x + "/" + y; System.out.println(z); }//end doConcat() }// end class |
8. Which of the following best approximates the output from this program?
public class Ap046{ public static void main( String args[]){ new Worker().doConcat(); }//end main() }//end class definition class Worker{ public void doConcat(){ Dummy y = new Dummy(); System.out.println(y); }//end doConcat() }// end class class Dummy{ private String name = "Joe"; private int age = 35; private double weight = 162.5; }//end class dummy |
9. Which of the following best approximates the output from this program?
public class Ap047{ public static void main( String args[]){ new Worker().doConcat(); }//end main() }//end class definition class Worker{ public void doConcat(){ Dummy y = new Dummy(); System.out.println(y); }//end doConcat() }// end class class Dummy{ private String name = "Joe"; private int age = 35; private double weight = 162.5; public String toString(){ String x = name + " " + " Age = " + age + " " + " Weight = " + weight; return x; } }//end class dummy |
10. Which of the following best approximates the output from this program when it is executed sometime during the year 2001? (Note the use of the constructor for the Date class that takes no parameters.)
import java.util.*; public class Ap048{ public static void main( String args[]){ new Worker().doConcat(); }//end main() }//end class definition class Worker{ public void doConcat(){ Date w = new Date(); String y = w.toString(); System.out.println(y); System.out.println(w.getTime()); }//end doConcat() }// end class |
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.
The Date class has a constructor that takes no parameters and is described in the documentation as follows:
"Allocates a Date object and initializes it so that it represents the time at which it was allocated, measured to the nearest millisecond."In other words, this constructor can be used to instantiate a Date object that represents the current date and time according to the system clock.
A property named time of type long
The actual date and time information encapsulated in a Date object is apparently stored in a property named time as a long integer.
(The AP CS test doesn't test for long integers, but this is one situation where you will need to know that they exist, and that they have the ability to store a very wide range of values.)Milliseconds since the epoch
The long integer encapsulated in a Date object represents the total number of milliseconds for the encapsulated date and time, relative to the epoch, which was Jan 01 00:00:00 GMT 1970.
Earlier dates are represented as negative values. Later dates are represented as positive values.
An overridden toString method
An object of the Date class has an overridden toString() method that converts the value in milliseconds to a form that is more useful for a human observer, such as:
Tue Feb 06 09:56:09 CST 2001
Instantiate a Date object with the noarg constructor
This program instantiates an object of the Date class using the constructor that takes no parameters.
Invoke the overridden toString method
Then it invokes the overridden toString() method to populate a String object that represents the Date object.
Following this, it displays that String object, producing the output shown above. (The actual date and time will vary depending on when the program is executed.)
Get the time property value
Then it invokes the getTime() method to get and display the value of the time property.
This is a representation of the same date and time shown above, but in milliseconds:
981474969593
The program used for this Question is an upgrade to the program that was used for Question 8.
Dummy class overrides the toString method
In particular, in this program, the class named Dummy overrides the toString() method in such a way as to return a String representing the object that would be useful to a human observer.
The String that is returned contains the values of the instance variables of the object: name, age, and weight.
Overridden toString method code
The overridden toString() method for the Dummy class is
shown below for easy reference.
public String toString(){ String x = name + " " + " Age = " + age + " " + " Weight = " + weight; return x; }//end toString() |
The code in the overridden toString() method is almost trivial.
The important thing is not the specific code in a specific overridden version of the toString() method.
Why override the toString method?
Rather, the important thing is to understand why you should probably override the toString() method in most new classes that you define.
In fact, you should probably override the toString() method in all new classes that you define if a String representation of an instance of that class will ever be needed.
The code will vary
The code required to override the toString() method will vary from one class to another. The important point is that the code must return a reference to a String object. The String object should encapsulate information that represents the original object in a format that is meaningful to a human observer.
This program instantiates a new object of the Dummy class, and passes that object's reference to the method named println().
The purpose of the println() method is to display a representation of the new object that is meaningful to a human observer. In order to do so, it requires a String representation of the object.
The toString method
The class named Object defines a default version of a method named toString().
All classes inherit the toString() method.
A child of the Object class
Those classes that extend directly from the class named Object inherit the default version of the toString() method.
Grandchildren of the Object class
Those classes that don't directly extend the class named Object also inherit a version of the toString() method.
May be default or overridden version
The inherited toString method may be the default version, or it may be an overridden version, depending on whether the method has been overridden in a superclass of the new class.
The purpose of the toString() method
The purpose of the toString() method defined in the Object class is to be overridden in new classes.
The body of the overridden version should return a reference to a String object, which represents an object of the new class.
Whenever a String representation of an object is required
Whenever a String representation of an object is required for any purpose in Java, the toString() method is invoked on a reference to the object.
The String that is returned by the toString() method is taken to be a String that represents the object.
When toString has not been overridden
When the toString() method is invoked on a reference to an object for which the method has not been overridden, the default version of the method is invoked.
The default String representation of an object
The String returned by the default version consists of the following:
Other than the name of the class from which the object was instantiated, this is not particularly useful to a human observer.
Dummy class does not override toString method
In this program, the class named Dummy extends the Object class directly, and doesn't override the toString() method.
Therefore, when the toString() method is invoked on a reference to an object of the Dummy class, the String that is returned looks something like the following:
Dummy@273d3c
This program illustrates String concatenation.
The plus (+) operator is what is commonly called an overloaded operator.
What is an overloaded operator
An overloaded operator is an operator whose behavior depends on the types of its operands.
Plus (+) as a unary operator
The plus operator can be used as either a unary operator or a binary operator. However, as a unary operator, with only one operand to its right, it doesn't do anything useful. This is illustrated by the following two statements, which are functionally equivalent.
x = y;
x = +y;
Plus (+) as a binary operator
As a binary operator, the plus operator requires two operands, one on either side. (This is called infix notation.) When used as a binary operator, its behavior depends on the types of its operands.
Two numeric operands
If both operands are numeric operands, the plus operator performs arithmetic addition.
If the two numeric operands are of different types, the narrower operand is converted to the type of the wider operand, and the addition is performed as the wider type.
Two String operands
If both operands are references to objects of type String, the plus operator creates and returns a new String object that contains the concatenated values of the two operands.
One String operand and one of another type
If one operand is a reference to an object of type String and the other operand is of some type other than String, the plus operator causes a new String object to come into existence.
This new String object is a String representation of the non-String operand,
Then it concatenates the two String objects, producing another new String object, which is the concatenation of the two.
How is the new String operand created?
The manner in which it creates the new String object that represents the non-String operand varies with the actual type of the operand.
A primitive operand
The simplest case is when the non-String operand is one of the primitive types. In these cases, the capability already exists to produce a String object that represents the value of the primitive type.
A boolean operand
For example, if the operand is of type boolean, the new String object that represents the operand will either contain the word true or the word false.
A numeric operand
If the operand is one of the numeric types, the new String object will be composed of some of the following:
In this program ...
In this program, a numeric double value, a numeric int value, and a boolean value were concatenated with a pair of slash characters to produce a String object containing the following:
3.5/9/true
When a reference to this String object was passed as a parameter to the println() method, the code in that method extracted the character string from the String object, and displayed that character string on the screen.
The toString method
If one of the operands to the plus operator is a reference to an object,
the toString method is invoked on the reference to produce a string
that represents the object. The toString method may be overridden
by the author of the class from which the object was instantiated.
The method named doIt() in this program illustrates an algorithm that can be used with a numeric cast operator (int) to cause double values to be rounded to the nearest integer.
Different than truncation toward zero
Note that this is different from simply truncating to the next integer closer to zero (as was illustrated in Question 5).
When a double value is cast to an int, the fractional part of the double value is discarded.
This produces a result that is the next integer value closer to zero.
This is true regardless of whether the double is positive or negative. This is sometimes referred to as its "truncation toward zero" behavior.
Not the same as rounding
If each of the values assigned to the variables named w and x
in this program were rounded to the nearest integer, the result would be
4 and -4, not 3 and -3 as produced by the program.
A boolean type cannot be cast to any other type. This program produces the following compiler error:
Ap042.java:13: inconvertible types
found : boolean
required: int
int y = (int)x;
The logical and operator (&&) performs an and operation between its two operands, which must both be of type boolean. If both operands are true, the operator returns true. Otherwise, it returns false.
The boolean negation operator (!)
The (!) operator is a unary operator, meaning that it always has only one operand. That operand must be of type boolean, and the operand always appears immediately to the right of the operator.
The behavior of this operator is to change its right operand from true to false, or from false to true.
Evaluation from inside out
Now, consider the following code fragment from
this program.
int x = 5, y = 6; if(!(x < y) && !(y < x/0)){ System.out.println("A"); }else{ System.out.println("B"); }//end else |
The individual operands of the && operator are evaluated from the inside out.
Consider the left operand of the && operator, which reads !(x<y).
(x < y) is true
In this case, x is less than y, so the expression inside the parentheses evaluates to true.
!(x < y) is false
This true result becomes the right operand for the ! operator at this point.
You might think of the state of the evaluation process at this point as being something like !true.
When the ! operator is applied to the true result, the combination of the two become a false result.
Short-circuit evaluation applies
This, in turn, causes the left operand of the && operator to be false.
At that point, the final outcome of the logical expression has been determined. It doesn't matter whether the right operand is true or false. The final result will be false regardless.
No attempt is made to evaluate the right operand
Therefore, no attempt is made to evaluate the right operand of the && operator in this case.
No attempt is made to divide the integer variable
x
by zero, no exception is thrown, and the program doesn't terminate abnormally.
It runs to completion and displays a B on the screen.
Question 1 was intended to set the stage for this question.
This Question, in combination with Question 1, is intended to help you understand and remember the concept of short-circuit evaluation.
What is short-circuit evaluation?
Logical expressions are evaluated from left to right. That is, the left operand of a logical operator is evaluated before the right operand of the same operator is evaluated.
When evaluating a logical expression, the final outcome can often be determined without the requirement to evaluate all of the operands.
Once the final outcome is determined, no attempt is made to evaluate the remainder of the expression. This is short-circuit evaluation.
Code from Question 1
Consider the following code fragment from Question 1:
int x = 5, y = 6; if((x > y) || (y < x/0)){ ... |
The (||) operator is the logical or operator.
Boolean operands required
This operator requires that its left and right operands both be of type boolean. This operator performs an inclusive or on its left and right operands. The rules for an inclusive or are:
If either of its operands is true, the operator returns true. Otherwise, it returns false.Left operand is false
In this particular expression, the value of x is not greater than the value of y. Therefore, the left operand of the logical or operator is not true.
Right operand must be evaluated
This means that the right operand must be evaluated in order to determine the final outcome.
Right operand attempts to divide by zero
However, when an attempt is made to evaluate the right operand, an attempt is made to divide x by zero. This throws an exception, which is not caught and handled by the program, so the program terminates as described in Question 1.
Similar code from Question 2
Now consider the following code fragment from Question 2.
Note that the right operand of the logical or operator still
contains an expression that attempts to divide the integer x by
zero.
int x = 5, y = 6; if((x < y) || (y < x/0)){ System.out.println("A"); ... |
No runtime error in this case
This program does not terminate with a runtime error. Why not?
And the answer is ...
In this case, x is less than y. Therefore, the left operand of the logical or operator is true.
Remember the rule for inclusive or
It doesn't matter whether the right operand is true or false. The final outcome is determined as soon as it is determined that the left operand is true.
The bottom line
Because the final outcome has been determined as soon as it is determined that the left operand is true, no attempt is made to evaluate the right operand.
Therefore, no attempt is made to divide x by zero, and no runtime error occurs.
Short-circuit evaluation
This behavior is often referred to as short-circuit evaluation.
Only as much of a logical expression is evaluated as is required to determine the final outcome.
Once the final outcome is determined, no attempt is made to evaluate the remainder of the logical expression.
This is not only true for the logical or operator, it is also
true for the logical and (&&) operator.
Whenever a Java program attempts to evaluate an expression requiring that a value of one of the integer types be divided by zero, it will throw an ArithmeticException. If this exception is not caught and handled by the program, it will cause the program to terminate.
Attempts to divide x by 0
This program attempts to evaluate the expression (y < x/0), which attempts to divide the variable named x by zero. This causes the program to terminate with the following error message when running under JDK 1.3:
java.lang.ArithmeticException: / by zero
at Worker.doLogical(Ap039.java:13)
at Ap039.main(Ap039.java:6)
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-