Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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.