Download Notes - Tom Kleen

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Chapter 10: Thinking in Objects
10.2 Immutable Objects and Classes
If you want to create an object and forbid the changing of its data, such an object is called
immutable. The following class appears to be immutable, but it is not.
public class Student
{
private int id;
private String name;
private java.util.Date dateCreated;
public Student (int ssn, String newName)
{
id = ssn;
name = newName;
dateCreated = new java.util.Date();
}
public int getID()
{
return id;
}
public String getName()
{
return name;
}
public java.util.Date getDateCreated()
{
return dateCreated;
}
}
Consider the following main program that tests the Student class:
public class StudentTest
{
public static void main(String[] args)
{
Student student = new Student (111223333, "John");
System.out.println ("Date created: " + student.getDateCreated());
java.util.Date dateCreated = student.getDateCreated();
dateCreated.setTime(200000);
System.out.println ("Date created: " + student.getDateCreated());
}
}
10.3 The Scope of Variables
Variables declared in a class file are either (1) instance variables or (2) static
variables.
Instance variables and scope
An instance variable is one that is created for each object that is created. It is
declared outside of any method in the class (and almost always at the top of the
class). If 20 objects of a particular class are created in a program, there will be 20
occurrences of an instance variable. You can think of instance variables as
belonging to specific objects (instances of the class).
The scope of instance variables is the entire class file (even if the instance variable
was not declared at the top).
Static variables and scope
A static variable is a variable for which only one copy exists for the entire class. It is
declared outside of any method in the class (and almost always at the top of the
class). If 20 objects of a particular class are created in a program, there will be only
1 occurrence of the static variable. You can think of static variables as belonging to
the class, rather than to a specific object.
The scope of static variables is the entire class file (even if the static variable was
not declared at the top).
Local variables and scope
A local variable is a variable that is declared inside of a method and is only available
inside of the method. The scope of a local variable is always from the line where it
is declared to the end of the enclosing block.
Parameters and scope
A parameter's scope is identical to the scope of a local variable. The memory for the
parameter is allocated when the subprogram is entered, and it is returned to the
system when the subprogram ends. The scope of a parameter is from the beginning
of the method to the end of the method.
10.4 The this Reference
The keyword this is used to refer to an object's fields within the class file. It is only
needed if you have a parameter with the same name as a class field. I prefer to
avoid the use of the word this by naming my parameters different names from the
object's field variables.
10.5 Class Abstraction and Encapsulation
An object should be encapsulated. That is, the only way that its private data can be
accessed is through its methods. This allows the programmer who created the class
to control access to the class's objects.
A programmer who wants to use a given class does not need to know anything
about the implementation details of the class. He only needs to know the names of
the methods and what those methods do.
Example
The diagram on page 376 (figure 10.4) gives the methods that can be used to
access a Loan object. This is a Unified Modeling Language diagram. A UML model
can be used to describe a class.





The class name goes in the top box.
The class variables go in the second box.
The class methods go in the third box.
A minus sign preceding a variable or method indicates that it is private.
A plus sign preceding a variable or method indicates that it is public.
All the programmer needs to know to work with a Loan object is how to use these
methods. He does not need to know the names of any variables inside of the class
or anything else. The programmer should be able to write a program that uses the
Loan class without knowing anything more than these methods.
Below is the information from page 376 (translated from UML to Java). It is a list of
the methods that can be used to manipulate a Loan object. This is all that a
programmer needs to know to be able to use the Loan class!
private double annualInterestRate;
private int numberOfYears;
private double loanAmount;
private java.util.Date loanDate;
public Loan()
/** Default constructor */
public Loan(double annualInterestRate, int numberOfYears, double
loanAmount)
/** Return annualInterestRate */
public double getAnnualInterestRate()
/** Set a new annualInterestRate */
public void setAnnualInterestRate(double annualInterestRate)
/** Return numberOfYears */
public int getNumberOfYears()
/** Set a new numberOfYears */
public void setNumberOfYears(int numberOfYears)
/** Return loanAmount */
public double getLoanAmount()
/** Set a newloanAmount */
public void setLoanAmount(double loanAmount)
/** Find monthly payment */
public double getMonthlyPayment()
/** Find total payment */
public double getTotalPayment()
/** Return loan date */
public java.util.Date getLoanDate()
We can write a program to use the Loan class without knowing a thing about the
implementation details. Here is such a program (from page 376 of Liang).
import java.util.Scanner;
public class TestLoanClass {
/** Main method */
public static void main(String[] args) {
// Create a Scanner
Scanner input = new Scanner(System.in);
// Enter yearly interest rate
System.out.print(
"Enter yearly interest rate, for example, 8.25: ");
double annualInterestRate = input.nextDouble();
// Enter number of years
System.out.print("Enter number of years as an integer: ");
int numberOfYears = input.nextInt();
// Enter loan amount
System.out.print("Enter loan amount, for example, 120000.95: ");
double loanAmount =
input.nextDouble();
// Create Loan object
Loan loan =
new Loan(annualInterestRate, numberOfYears, loanAmount);
// Display loan date, monthly payment, and total payment
System.out.printf("The loan was created on %s\n" +
"The monthly payment is %.2f\nThe total payment is %.2f\n",
loan.getLoanDate().toString(), loan.getMonthlyPayment(),
loan.getTotalPayment());
}
}
And here is the implementation of the Loan class:
public class Loan {
private double annualInterestRate;
private int numberOfYears;
private double loanAmount;
private java.util.Date loanDate;
/** Default constructor */
public Loan() {
this(2.5, 1, 1000);
}
/** Construct a loan with specified annual interest rate,
number of years and loan amount
*/
public Loan(double annualInterestRate, int numberOfYears,
double loanAmount) {
this.annualInterestRate = annualInterestRate;
this.numberOfYears = numberOfYears;
this.loanAmount = loanAmount;
loanDate = new java.util.Date();
}
/** Return annualInterestRate */
public double getAnnualInterestRate() {
return annualInterestRate;
}
/** Set a new annualInterestRate */
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
/** Return numberOfYears */
public int getNumberOfYears() {
return numberOfYears;
}
/** Set a new numberOfYears */
public void setNumberOfYears(int numberOfYears) {
this.numberOfYears = numberOfYears;
}
/** Return loanAmount */
public double getLoanAmount() {
return loanAmount;
}
/** Set a newloanAmount */
public void setLoanAmount(double loanAmount) {
this.loanAmount = loanAmount;
}
/** Find monthly payment */
public double getMonthlyPayment() {
double monthlyInterestRate = annualInterestRate / 1200;
double monthlyPayment = loanAmount * monthlyInterestRate / (1 (Math.pow(1 / (1 + monthlyInterestRate), numberOfYears * 12)));
return monthlyPayment;
}
/** Find total payment */
public double getTotalPayment() {
double totalPayment = getMonthlyPayment() * numberOfYears * 12;
return totalPayment;
}
/** Return loan date */
public java.util.Date getLoanDate() {
return loanDate;
}
}
NOTE: the above method (getLoanDate()) allows the user to change the loan date!
This is something that it seems should not be changed.
10.6 Object-Oriented Thinking
"Classes provide more flexibility and modularity for building reusable software."
(Liang9, page 379). We will look at two solutions to the same problem. One
solution will not use objects, and the other will.
The non-object solution
import java.util.Scanner;
public class ComputeBMI {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// Prompt the user to enter weight in pounds
System.out.print("Enter weight in pounds: ");
double weight = input.nextDouble();
// Prompt the user to enter height in inches
System.out.print("Enter height in inches: ");
double height = input.nextDouble();
final double KILOGRAMS_PER_POUND = 0.45359237; // Constant
final double METERS_PER_INCH = 0.0254; // Constant
// Compute BMI
double weightInKilogram = weight * KILOGRAMS_PER_POUND;
double heightInMeters = height * METERS_PER_INCH;
double bmi = weightInKilogram / (heightInMeters * heightInMeters);
// Display result
System.out.printf("Your BMI is %5.2f\n", bmi);
if (bmi < 16)
System.out.println("You are seriously underweight");
else if (bmi < 18)
System.out.println("You are underweight");
else if (bmi < 24)
System.out.println("You are normal weight");
else if (bmi < 29)
System.out.println("You are overweight");
else if (bmi < 35)
System.out.println("You are seriously overweight");
else
System.out.println("You are gravely overweight");
}
}
And this is an object-oriented solution to the same problem:
public class BMI {
private String name;
private int age;
private double weight; // in pounds
private double height; // in inches
public static final double KILOGRAMS_PER_POUND = 0.45359237;
public static final double METERS_PER_INCH = 0.0254;
public BMI(String name, int age, double weight, double height) {
this.name = name;
this.age = age;
this.weight = weight;
this.height = height;
}
public BMI(String name, double weight, double height) {
this(name, 20, weight, height);
}
public double getBMI() {
double bmi = weight * KILOGRAMS_PER_POUND /
((height * METERS_PER_INCH) * (height * METERS_PER_INCH));
return Math.round(bmi * 100) / 100.0;
}
public String getStatus() {
double bmi = getBMI();
if (bmi < 16)
return "seriously underweight";
else if (bmi < 18)
return "underweight";
else if (bmi < 24)
return "normal weight";
else if (bmi < 29)
return "over weight";
else if (bmi < 35)
return "seriously over weight";
else
return "gravely over weight";
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getWeight() {
return weight;
}
public double getHeight() {
return height;
}
}
Here is the program to test out BMI class.
public class UseBMIClass {
public static void main(String[] args) {
BMI bmi1 = new BMI("John Doe", 18, 145, 70);
System.out.println("The BMI for " + bmi1.getName() + " is "
+ bmi1.getBMI() + " " + bmi1.getStatus());
BMI bmi2 = new BMI("Peter King", 215, 70);
System.out.println("The BMI for " + bmi2.getName() + " is "
+ bmi2.getBMI() + " " + bmi2.getStatus());
}
}
10.7 Object Composition
It is legal for a class to contain objects from other classes.
10.8 Designing a Course Class
Use the diagram on page 384 (figure 10.9) to create a Course class.
10.9 Designing a Class for Stacks
Use the diagram on page 386 (figure 10.11) to create a StackOfIntegers class.
This is Listing 10.8:
public class StackOfIntegers {
private int[] elements;
private int size;
public static final int DEFAULT_CAPACITY = 16;
/** Construct a stack with the default capacity 16 */
public StackOfIntegers() {
this(DEFAULT_CAPACITY);
}
/** Construct a stack with the specified maximum capacity */
public StackOfIntegers(int capacity) {
elements = new int[capacity];
}
/** Push a new integer into the top of the stack */
public void push(int value) {
if (size >= elements.length) {
int[] temp = new int[elements.length * 2];
System.arraycopy(elements, 0, temp, 0, elements.length);
elements = temp;
}
elements[size++] = value;
}
/** Return and remove the top element from the stack */
public int pop() {
return elements[--size];
}
/** Return the top element from the stack */
public int peek() {
return elements[size - 1];
}
/** Test whether the stack is empty */
public boolean empty() {
return size == 0;
}
/** Return the number of elements in the stack */
public int getSize() {
return size;
}
}
10.10 Designing the GuessDate Class
Students should read this on their own.
10.11 Class Design Guidelines
10.11.1 Cohesion
A class should describe a single entity.
10.11.2 Consistency
Use capital letter for class name.
Put data first, then constructors, then methods.
Supply a default (no arguments) constructor
10.11.3 Encapsulation
Make variables private and use get and set methods to provide access to the ones
you want to expose to the user.
10.11.4 Clarity
Methods should be independent of each other. The user should not have to call
them in any particular order.
Do not create a property for a value that can be computed from other values. For
example, if you have a birthdate property, you do not need an age property.
10.11.5 Completeness
10.11.6 Instance vs. Static
10.12 and 10.13 Processing Primitive Data Type Values as Objects
Many methods in the Java API require that an object be passed in, not a primitive.
So what do you do if you have a primitive and you want to pass it to a method that
is expecting an object? You have to convert the primitive into an object using what
Java calls a wrapper class. NOTE: This used to be a problem, but now Java
automatically wraps (called "boxing") and unwraps (called "unboxing") primitives.
Wrapper class: a class that is used to make a primitive data item into an object. It
"wraps" the primitive in an object.
The wrapper classes are:




Integer (also Byte, Short, and Long)
Double (also Float)
Character
Boolean
Note that six of these are the same as their primitive counterparts except that the
first letter is capitalized. The two that are different from their primitive counterparts
are Integer and Character – both are spelled out completely.
What is the difference between an int and an Integer?
An int is a primitive type. It has no properties or methods.
An Integer is an object. It has properties and methods. (More below)
How do we create wrapper objects? Each wrapper class (except for Character) has
two constructors:


a constructor that takes a parameter of the corresponding primitive type
a constructor that takes a parameter of type String (the String parameter
must be properly formed)
Examples of creating wrapper objects
// The old (explicit) way:
Integer m = new Integer(25);
Integer n = new Integer("75");
Double d = new Double(3.14159);
Double e = new Double("1.5");
Character c = new Character('X');
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("true");
// The new (implicit) way:
m = 25;
// Can't do this, though:
// n = "75";
d = 3.14159;
// Can't do this, though:
// e = "1.5";
c = 'X';
b1 = true;
// Can't do this, though;
// b2 = "true";
Try the following to see if they work:
Boolean b3 = new Boolean ("TRUE");
// should work
Character c2 = new Character ("X"); // string! should NOT work
What is the advantage of making primitive types into objects? The reason for
having primitive types in the first place is that they are very efficient. Converting
them into objects will have a cost in both execution time and memory used. The
advantage is that we can pass them to any method that is expecting to receive an
object – something we cannot do with primitive types.
Properties and methods of wrapper classes
Since wrappers are objects, they must have some properties and methods.
Extracting the primitive
Each wrapper class has methods to extract some primitives.
The Integer class and the Double class have the following methods:

intValue(), byteValue(), shortValue(), longValue(), floatValue(),
doubleValue()
The Boolean class has:

booleanValue()
The Character class has:

charValue()
These methods can be used to extract the primitive value. This primitive value can
then be used anyplace where the primitive could be used in a program. For
example i.intValue() can be used any place where an integer can be used in a Java
program. Try these:
int a = i.intValue();
double x = d.doubleValue();
boolean b = b1.booleanValue();
char ch = c.charValue();
The Character wrapper class
Converting characters into numbers
Contains two methods for returning the numeric value of a character in the various
number systems. The digit method returns the value of the character in the given
number base.
The signature is:
public static int digit(char ch, int radix)
Example calls
n = Character.digit('5', 10);
// converts 48 to 5
n = Character.digit('F', 16);
// converts 70 to 15
The signature of the second method is:
public static int getNumber(char ch)// assumes base 10
Example call
n = Character.getNumber('5');
The Character class has one method to return the character value of a number. Its
signature is:
public static char forDigit(int digit, int radix)
Example call
// prints out "f"
System.out.println (Character.forDigit(15, 16));
The Character class has two case conversion methods. Signatures:
public static char toLowerCase(char ch)
public static char toUpperCase(char ch)
Example call
ch = Character.toLowerCase(charObject);
ch = Character.toLowerCase(charObject);
The Character class also contains a variety of other methods to test whether a
character is of a specific type e.g. :
Character.isLetter(ch)
Character.isDefined(ch)
Character.isSpaceChar(), etc
Note that all of these methods are declared to be static. There are two types of
methods in Java:


Instance methods are associated with one object and use the instance
variables of that object. This is the default if static is not specified.
Static methods make no use of the instance variables of any object of the
class they are defined in. They typically take parameters and compute
something from those parameters, with no reference to instance variables.
This is typical of methods which do some kind of generic calculation. A good
example of this are the many utility methods in the predefined Math class.
Examples of static methods from the Math class
Returns the absolute value of a double value:
static double abs(double x)
Returns the absolute value of an int value:
static int abs(int x)
Returns the value of baseexponent:
static double pow(double base, double exponent)
Returns the square root of a number:
static double sqrt(double x)
Such methods are invoked by giving the class name followed by the method name.
Try these:
System.out.println (Math.abs(-25));
System.out.println (Math.pow(2, 8));
System.out.println (Math.sqrt(2));
The Integer wrapper class (also applies to Short, Byte and Long, but we won't be using them)
 has a parseInt(s) method that takes a String and parses it into the
appropriate type
 has the static methods toBinaryString(), toOctalString() and
toHexString() which take an integer value and convert it to the appropriate
String representation
Examples
System.out.println (Integer.parseInt("12345");
System.out.println (Integer.toBinaryString(255));
The Double wrapper class (also applies to Float, but we won't be using it)
Has static fields which define POSITIVE_INFINITY, NEGATIVE_INFINITY, and NaN
And the following methods to test a value




public
public
public
public
boolean isNaN()
static boolean isNaN(type value)
boolean isInfinite()
static boolean isInfinite(type value)
And methods to convert a value into a bit pattern or vice versa




public
public
public
public
static
static
static
static
int floatToIntBits(float value)
float intBitsToFloat(int bits)
long doubleToLongBits(double value)
double longBitsToDouble(long bits)
Examples
System.out.println (Double.isNaN(Double.POSITIVE_INFINITY));
Once you know how to convert a primitive to an object using a wrapper class, you
can use any of Java's API methods on these primitives that have been wrapped.
10.14 The BigInteger and BigDecimal Classes
The BigInteger and BigDecimal classes can be used to represent integers or
decimal numbers of any size and precision.
Thinking in objects
Homework assignment: CalendarDate class
This demonstrates:



Thinking in objects: How you can use a class without knowing (or caring)
how it is implemented.
A good example of the use of a static variable.
The use of public named constants.