Java for the Impatient Lecture 3 More Constructors

  • Slides: 89
Download presentation
Java for the Impatient Lecture 3: More Constructors, Scope, Wrappers, Inheritance, and Object Oriented

Java for the Impatient Lecture 3: More Constructors, Scope, Wrappers, Inheritance, and Object Oriented Design.

Object Oriented Design • Extending classes & inheriting fields/methods – Using this and super

Object Oriented Design • Extending classes & inheriting fields/methods – Using this and super – Constructor chaining – Polymorphism & casting – Single and multiple inheritance – Visibility modifiers

Inheritance Natural, hierarchical way of organizing things. Staff Member Employee Hourly Consultant (superclass) Volunteer

Inheritance Natural, hierarchical way of organizing things. Staff Member Employee Hourly Consultant (superclass) Volunteer Salaried (subclass of Staff) (subclass of Employee) (subclass of Hourly) Think in terms of “is a” relationships: An Employee is a Staff Member, as is a Volunteer. An Hourly worker is a Employee. A Consultant is a(n) Hourly employee.

class Animal { protected String str. Name = “”; protected String str. Noise =

class Animal { protected String str. Name = “”; protected String str. Noise = “”; protected int i. Num. Times. Performed = 0; // constructors, accessors & modifiers go here public void identify. Self( ) { System. out. println(“My name is “ + str. Name); } // of identify. Self public void perform ( ) { do. Your. Thing( ); Example i. Num. Times. Performed++; } // of perform public void do. Your. Thing( ) { ; // ‘no-op’ method } // of do. Your. Thing } // of Animal

Subclasses (Dog extends Animal i. e. “A dog is an animal” or “All dogs

Subclasses (Dog extends Animal i. e. “A dog is an animal” or “All dogs are animals”) class Dog extends Animal { public Dog() { str. Noise = “Woof”; } // of constructor Animal Dog Human Recall: The Animal class had a no-op method for do. Your. Thing() public void do. Your. Thing ( ) { identify. Self(); System. out. println(“I am a dog”); System. out. println(str. Noise); } // of do. Your. Thing } // of Dog Cat

Subclasses (Cat extends Animal i. e. “A cat is an animal” or “All cats

Subclasses (Cat extends Animal i. e. “A cat is an animal” or “All cats are animals”) Animal Dog class Cat extends Animal { public Cat() { str. Noise = “Miaow”; } // of constructor public void do. Your. Thing ( ) { identify. Self(); System. out. println(“I am a cat”); System. out. println(str. Noise); } // of do. Your. Thing } // of Cat Human

Example (Cont’d) Dog pickles = new Dog(); pickles. set. Name(“Pickles”); pickles. do. Your. Thing();

Example (Cont’d) Dog pickles = new Dog(); pickles. set. Name(“Pickles”); pickles. do. Your. Thing(); // output: // “My name is Pickles” // “I am a dog” // “Woof” Cat abby = new Cat(); abby. set. Name(“Abby”); abby. do. Your. Thing(); // output: // “My name is Abby” // “I am a cat” // “Miaow”

Subclasses (Human extends Animal i. e. “A human is an animal” or “All humans

Subclasses (Human extends Animal i. e. “A human is an animal” or “All humans are animals”) Animal Dog Cat Human class Human extends Animal { public Human() { str. Noise = “I think therefore I am”; } // of constructor public void do. Your. Thing ( ) { identify. Self(); System. out. println (“I am a sentient being”); System. out. println(str. Noise); } // of do. Your. Thing } // of Human

Example (Cont’d) Human descartes = new Human(); descartes. set. Name(“Rene”); descartes. do. Your. Thing();

Example (Cont’d) Human descartes = new Human(); descartes. set. Name(“Rene”); descartes. do. Your. Thing(); // output: // “My name is Rene” // “I am a sentient being” // “I think therefore I am”

Inheritance and Scope Variables (e. g. str. Noise): • Java first examines current method,

Inheritance and Scope Variables (e. g. str. Noise): • Java first examines current method, looks for local variable or parameter; • Java then examines current class (e. g. Dog); • Java then examines superclass (e. g. Animal); • Java continues up the class hierarchy until no more superclasses to examine. Methods (e. g. do. Your. Thing() or identify. Self()): • Java first examines current class; • Java then examines superclass; • Java continues up inheritance hierarchy until no more superclasses to examine.

Specifying Scope Java allows you to override the scope rules by saying which variable/method

Specifying Scope Java allows you to override the scope rules by saying which variable/method you’re referring to: Keyword super: keyword for specifying method or variable from superclass, e. g. , super. do. Your. Thing( ) Keyword this: keyword for specifying method or variable in current object, e. g. , this. do. Your. Thing( )

class Dog extends Animal { public Dog() { super. str. Noise = “Woof”; }

class Dog extends Animal { public Dog() { super. str. Noise = “Woof”; } // of constructor public void do. Your. Thing ( ) { super. identify. Self(); System. out. println(“I am a dog”); System. out. println(str. Noise); } // of do. Your. Thing } // of Dog Same (in this case) as str. Noise = “Woof”; and this. str. Noise = “Woof”; Same (in this case) as identify. Self(); or this. identify. Self();

Animal Using super Dog Cat Human class Dog extends Animal { // constructor as

Animal Using super Dog Cat Human class Dog extends Animal { // constructor as before public void do. Your. Thing() { identify. Self(); System. out. println(str. Noise); } // of do. Your. Thing I. e. this. identify. Self() (newly defined below) public void identify. Self() { super. identify. Self(); System. out. println(“I am a dog”); } // of identify. Self } // of Dog I. e. the identify. Self() (defined in Animal)

A geometry example class Shape { public final double PI = 3. 14159; protected

A geometry example class Shape { public final double PI = 3. 14159; protected String name; Shape Rectangle Circle public String get. Name () { return (this. name); } // get. Name public int area () { return (0); } // area } // Shape

class Rectangle extends Shape { private int length, width; Rectangle () { this(0, 0);

class Rectangle extends Shape { private int length, width; Rectangle () { this(0, 0); } // constructor Rectangle (int l, int w) { this( l, w, “rectangle”); } // constructor Rectangle (int l, int w, String n) { length = l; width = l; name = n; } // constructor public int area () { return (length * width); } // area public String get. Name () { if (length == width) return ("square"); else return (super. get. Name()); } // get. Name public String to. String () { String s; s = new String ("A " + get. Name() + " with length " + length + " and width " + width); return (s); } } // to. String } // Rectangle

Constructors and Inheritance Java’s rule: • If first line of constructor is not an

Constructors and Inheritance Java’s rule: • If first line of constructor is not an explicit call to a superclass constructor, Java will implicitly put super( ) as the first line, calling the superclass default constructor. public Dog() { str. Noise = “Woof”; } // of constructor implied call to Animal() here • An exception to this rule: chained constructor call to this(params) will defer super( ) call • To use superclass constructors with params, call them explicitly, e. g. , super(str. Name)

Inheritance and Scoping Examples: super(xxx) super. xxx( ) // calls a superclass constructor //

Inheritance and Scoping Examples: super(xxx) super. xxx( ) // calls a superclass constructor // accesses superclass’ variable // calls superclass’ method this(xxx) this. xxx( ) // calls a current-class constructor // accesses current class’s variable // calls current class’ method Note: cannot do super<something> (can achieve this effect via casting, but rarely should; details later. . . )

Inheritance and Scoping class Staff. Member { String str. Name; public Staff. Member( )

Inheritance and Scoping class Staff. Member { String str. Name; public Staff. Member( ) { System. out. println (“in default Staff. Mem constr; No Name”); set. Name(“No Name”); } // of constructor public Staff. Member(String str. Name) { System. out. println (“in 2 nd Staff. Mem constructior; have a Name”); set. Name(str. Name); } // of constructor public void set. Name(String str. Name) { this. str. Name = str. Name; } // of set. Name }// of Staff. Member

Inheritance and Scoping class Employee 1 extends Staff. Member { public Employee 1(String str.

Inheritance and Scoping class Employee 1 extends Staff. Member { public Employee 1(String str. Name) { set. Name(str. Name); } // of constructor } // of Employee 1 class Employee 2 extends Staff. Member { public Employee 2(String str. Name) { set. Name(str. Name); } // of constructor public void set. Name(String str. Name) { super. set. Name(str. Name); System. out. println (“Name set”); } } // of Employee 2 Note: Employee has no local set. Name() method Wha happ t ens ? ? ?

Class Object • Java provides a base class, Object • All classes that do

Class Object • Java provides a base class, Object • All classes that do not have an extends clause implicitly inherit directly fromclass java. lang. Object Examples: public boolean equals (Object o) public boolean String to. String () • When you create your own to. String( ) method for a class, you are overriding the to. String( ) provided by Object.

Object Hierarchy Object Animal Dog Cat class Object methods: String to. String() boolean equals(Object

Object Hierarchy Object Animal Dog Cat class Object methods: String to. String() boolean equals(Object obj) and a few others. . . Employee Human Salaried Hourly Object Or how about. . . Animal Dog Cat Human Employee Salaried Hourly

Wrapper Classes Primitive types (e. g. , int) are not classes But sometimes, we

Wrapper Classes Primitive types (e. g. , int) are not classes But sometimes, we may have need to make use of primitive types in a context that requires that we manipulate objects, not primitives e. g. many collection classes are collections of Objects Java provides a set of wrapper classes (a. k. a. type wrappers, a. k. a. envelope classes) to support treating primitives as objects. It does this by providing a specific class that corresponds to each primitive data type They are in java. lang, so the names are universally available

Wrapper Classes Class Boolean Character Byte Short Integer Long Float Double corresponds to Primitive

Wrapper Classes Class Boolean Character Byte Short Integer Long Float Double corresponds to Primitive boolean char byte short int long float double Each one: • allows us to manipulate primitives as objects • contains useful conversion methods. E. g. Integer contains static Integer value. Of(String s) Integer. value. Of(“ 27”) is the object corresponding to int 27 • contains useful utility methods (e. g. for hashing)

Wrapper Classes Using wrappers to bridge between objects and primitives: // create and initialize

Wrapper Classes Using wrappers to bridge between objects and primitives: // create and initialize an int i = 7; // create an Integer object and convert the int to it Integer int. Object = new Integer( i ); // retrieve the int by unwrapping it from the object System. out. println( int. Object. int. Value ); // convert a string into an Integer object String str. S = “ 27”; Integer int. Object = new Integer (Integer. value. Of(str. S) ); // then to an int i = int. Object. int. Value; A class method

Instance vs. Class Methods • Use instance methods whenever each object should have its

Instance vs. Class Methods • Use instance methods whenever each object should have its own behavior. e. g. , pickles vs. descartes vs. abby do. Your. Thing( ). • Use a class method whenever the class itself should maintain a single behavior for all instances of the class. e. g. , converting from one type to another. • Class methods cannot be used to access instance variables • So you can say Integer. value. Of(str. Value); but not int. Object. value. Of(str. Value);

Polymorphism • Several subclasses may have different methods for accomplishing the same/similar behavior –

Polymorphism • Several subclasses may have different methods for accomplishing the same/similar behavior – You don’t care about the details when you call a method, provided you know that the object can perform a method with that signature – E. g. you have a simulated ecology with animals in it (Sim. Zoo) and want to make the animals move • But animals move in diffrerent ways

Polymorphism class Animal { public void move ( ) { System. out. println(“I am

Polymorphism class Animal { public void move ( ) { System. out. println(“I am an animal and am moving. ”); } // of move } // of Animal class Fish extends Animal { public void move( ) { System. out. println(“Glug gurgle”); } // of move } // of Fish class Bird extends Animal { public void move( ) { System. out. println(“Tweet tweet flap”); } // of move } // of Bird

Polymorphism (cont’d) class Dog extends Animal { public void move ( ) { System.

Polymorphism (cont’d) class Dog extends Animal { public void move ( ) { System. out. println(“Sniff sniff woof”); } // of move public void bark ( ) { System. out. println(“Arf Arf”); } // of bark } // of Dog

Polymorphism class Driver { public static void main (String[ ] argv) { Animal[ ]

Polymorphism class Driver { public static void main (String[ ] argv) { Animal[ ] animal. Array = new Animal[3]; int i. Index; animal. Array[0] = new Bird( ); animal. Array[1] = new Dog( ); animal. Array[2] = new Fish( ); for (i. Index=0; i. Index < animal. Array. length; i. Index++) { animal. Array[i. Index]. move( ); } // of for } // of main } // of Driver Output: Tweet tweet flap Sniff sniff woof Glug gurgle All animals can move, so any member of the array can move

Polymorphism • Polymorphism means “taking many forms”. . . an object of a given

Polymorphism • Polymorphism means “taking many forms”. . . an object of a given class can adapt take the formof any of its subclasses. • Polymorphism means that the correct move( ) method will always be called. • A subclass can be substituted for its superclass, e. g. , a bird for an animal. “A bird is a animal. ” Yes. • The reverse is not true: can’t substitute superclass for a subclass, e. g. , CANNOT substitute an animal for a bird. “An animal is a bird? ” No. • A single interface for multiple behaviors: Only one interface for the method call. Multiple behaviors based on the subclass.

Polymorphism class Driver 2 { public static void main(String[ ] argv) { Animal[ ]

Polymorphism class Driver 2 { public static void main(String[ ] argv) { Animal[ ] = animal. Array[3]; Dog d; We cast before calling bark() because only dogs int i. Index; can bark. So some array animal. Array[0] = new Bird( ); members cannot execute animal. Array[1] = new Dog( ); the method animal. Array[2] = new Fish( ); for (1=0; 1 < animal. Array. length; i. Index++) if (animal. Array[i. Index] instanceof Dog) { d = (Dog) animal. Array[i. Index]; d. bark( ); } // if } // main } // Driver 2

Polymorphism Casting: • used here to give an object of a superclass the form

Polymorphism Casting: • used here to give an object of a superclass the form of the appropriate subclass, e. g. , if (animal. Array[i. Index] instanceof Dog) { animal. Array[i. Index]. bark(); } would produce an error because objects of class Animal have no method called bark. So, we first cast what instanceof tells us is a Dog as a Dog. if (animal. Array[i. Index] instanceof Dog) { d = (Dog) animal. Array[i. Index] d. bark( ); }

Casting … Why? Keyword instanceof: Used to interogate an object to see if it

Casting … Why? Keyword instanceof: Used to interogate an object to see if it is an instance of the specified class, e. g. “Is this particular animal of class Dog? ” Question: If Java can determine that a given Animal is or is not a Dog (via instanceof), then: • Why the need to cast it to a Dog object before Java can recognize that it can bark? • Why can’t Java do it for us?

Casting… Why? Answer: difference between compile-time and runtime type checking. Source code Compile errors

Casting… Why? Answer: difference between compile-time and runtime type checking. Source code Compile errors Byte code JVM Interpreter errors Program runs

Casting (con’td) Compile-time Errors: • Those that are discernable without the program executing. •

Casting (con’td) Compile-time Errors: • Those that are discernable without the program executing. • Question of language legality: “Is this a legal statement? ” e. g. , i. Index = ch. Variable; Statement is not legal. Run-time Errors: • Those that are discernable only when the program is running with actual data values. • Question of execution legality: “Is it legal for this variable to have the actual value assigned to it? ”, e. g. , animal. Array[<bad. Index>] = some. Animal Statement is legal, but particular index value isn’t.

Casting… Why? if (animal. Array[i. Index] instanceof Dog) { animal. Array[i. Index]. bark; }

Casting… Why? if (animal. Array[i. Index] instanceof Dog) { animal. Array[i. Index]. bark; } • 1 st line is legal. • 2 nd line isn’t (unless array has Dog). • We can see that 1 st line guarantees 2 nd is legal. • Compiler cannot see inter-statement dependencies… unless compiler runs whole program with all possible data sets! • Runtime system could tell easily. We want most checking at compile-time for performance and correctness.

Casting (Cont’d) if (animal. Array[i. Index] instanceof Dog) { d = (Dog) animal. Array[i.

Casting (Cont’d) if (animal. Array[i. Index] instanceof Dog) { d = (Dog) animal. Array[i. Index]; d. bark( ); • Here, legality of each line of code can be evaluated at compile time. } • Legality of each line discernable without worrying about inter-statement dependencies, i. e. , each line can stand by itself. • Can be sure that code is legal (not sometimes-legal). A Good Use for Casting: Resolving polymorphic ambiguities for the compiler.

Multiple Inheritance Some languages allow multiple inheritance: Animal Dog Pet (two superclasses) (a subclass

Multiple Inheritance Some languages allow multiple inheritance: Animal Dog Pet (two superclasses) (a subclass of two) • Multiple inheritance leads to many potentially confusing naming problems (e. g. Pet. do. Your. Thing() vs. Animal. do. Your. Thing()) • Growing consensus: the benefits of multiple inheritance aren’t worth the problems. • Java has single inheritance only • Java doesn’t allow multiple inheritance • Well, there is a restricted kind that avoids most of the problems. It involves using interfaces, which we’ll cover later)

Visibility and Access Can an object use a field or call a method? Always

Visibility and Access Can an object use a field or call a method? Always specify a visibility modifier. Visibility Modifier: Access by: public protected private Every class Yes No No A subclass Yes No An instance of the class Yes Yes Guidelines: Only make public methods that are in the class’s “contract” Make all fields private Make all other “private” methods protected Don’t leave off the modifier unless you know about packages

Animals (again!) class Animal { protected String str. Name = “”; protected String str.

Animals (again!) class Animal { protected String str. Name = “”; protected String str. Noise = “”; // constructors, accessors & modifiers go here public void identify. Self( ) { System. out. println(“My name is “ + str. Name); } // of identify. Self public void do. Your. Thing( ) { ; // ‘no-op’ method } // of do. Your. Thing } // of Animal So, any object can ask an animal to identify itself, or do its thing.

Exception Handling: When Bad Things Happen to Good Code

Exception Handling: When Bad Things Happen to Good Code

Exceptions--Why? Error Handling So far: • have done very little error handling • have

Exceptions--Why? Error Handling So far: • have done very little error handling • have assumed that things will work as intended Rules of Thumb: • programmers must test and debug to find and correct compile-time errors • compiler doesn’t solve the problem of run-time errors (e. g. , incorrect values or state) • programmer must insure that run-time errors result in “graceful” program behavior • “graceful” means “the program doesn’t just produce wrong effects or blow up!

Exceptions--Traditional Methods • In traditional procedural programming languages, there were various ways of handling

Exceptions--Traditional Methods • In traditional procedural programming languages, there were various ways of handling run-time errors. • One way is to return some kind of value indicating whether the procedure succeeded or not. For example: public boolean some. Method( ) { // if this method suceeded, return true // otherwise return false } Note the blurring of the logical distinction between procedures and functions… a bad engineering habit!

Exceptions--Traditional Methods • Traditional means of error handling can quickly become ugly, complex and

Exceptions--Traditional Methods • Traditional means of error handling can quickly become ugly, complex and unmanageable. For example: If we intend to do the sequence of calling three procedures followed by one or more instructions, e. g. , some. Method( ); some. Other. Method( ); some. Third. Method( ); /* do some intended actions*/ we can find ourselves with code that looks like. . .

Exceptions--Traditional Methods if (some. Method( ) == true) { if (some. Other. Method( )

Exceptions--Traditional Methods if (some. Method( ) == true) { if (some. Other. Method( ) == true) { if (some. Third. Method( ) == true) { // have not encountered errors; do intended actions } else { // handle some error caused by some. Third. Method( ) } } else { // handle some error caused by some. Other. Method( ) } } else { // handle some error caused by some. Method( ) }

Exceptions--Global Variables • Another way error handling is to have the value of a

Exceptions--Global Variables • Another way error handling is to have the value of a global variable represent the error. int i. Error. Value = 0; public void some. Method( ) { // do some. Method’s stuff here // if there is an error, then set i. Error. Value = 1 } public void some. Other. Method( ) { // do some. Other. Method’s stuff here // if there is an error, then set i. Error. Value = 2 } public void some. Third. Method( ) { // do some. Third. Method’s stuff here // if there is an error, then set i. Error. Value = 3 }

Exceptions--Global Variables public void do. It() { some. Method(); some. Other. Method(); some. Last.

Exceptions--Global Variables public void do. It() { some. Method(); some. Other. Method(); some. Last. Method(); if (i. Error. Value == 1). . . if (i. Error. Value == 2). . . if (i. Error. Value == 3). . . } But: What if the run-time error stopped us from continuing? For example: What if some. Method( ) failed in such a way that we cannot go on to some. Other. Method( )? To cope, we find ourselves with code that’s nearly as messy as the earlier example which featured multiple nested-ifs:

Exceptions--Global Variables public void doit( ) { some. Method( ); if (i. Error. Value

Exceptions--Global Variables public void doit( ) { some. Method( ); if (i. Error. Value == 1) {. . . } // if else { some. Other. Method( ); if (i. Error. Value == 2) {. . . } // if else { some. Third. Method( ); if (i. Error. Value == 3) { … } // if else { do intended actions } // else }// else Note: with this technique we potentially must wrap the ENTIRE program in a series of if/else clauses, duplicating code in places. (Do we prefer robustness or clarity/maintainability? )

Exceptions: Traditional Approaches • As you can see, it gets convoluted very fast even

Exceptions: Traditional Approaches • As you can see, it gets convoluted very fast even for such a simple sequence of steps. • Historically, the way programmers got around this was simply to not do any error handling at all! • Instead, they generally used one of two approaches: • Upon detecting an error, simply terminate the program, i. e. , “recognizethe error but don’t handle it, just quit”, or else … • Don’t even attempt to detect the error; i. e. , “let the program react in anarbitrary and unpredictable manner”(blow up? bad values? wrong behavior? ) • Both of these violate a basic tenet of structured programming: “Allow only a single point of exit from any procedure or function. ”

Exceptions: The Real Problem Both of these traditional approaches boil down to a case

Exceptions: The Real Problem Both of these traditional approaches boil down to a case of the programmer simply ignoring the real problem, which is: When a run-time error occurs in a method, • how can we stop the method without allowing it to do any damage? • how can we take appropriate actions to handle the error without having the program simply blow up or do something else that’s bad? It is not acceptable for programs to fail or to do “bad behavior”! • Safety critical programs • Customer satisfaction We require some mechanism to recover from unexpected or abnormal run-time situations.

Exceptions and Exception Handling: the modern programming concept for dealing with run-time errors Exception:

Exceptions and Exception Handling: the modern programming concept for dealing with run-time errors Exception: “a run-time event that may cause a method to fail or to execute incorrectly” Purpose of Exception Handling: “to allow graceful handling of and recovery from run-time errors” Common examples in Java: • Null. Pointer. Exception • Arithmetic. Exception • Array. Index. Out. Of. Bounds. Exception • Java API contains over two dozen exceptions provided by Java.

Exceptions: Terminology Exception Terminology: • When an exceptional condition occurs, an exception is “thrown”

Exceptions: Terminology Exception Terminology: • When an exceptional condition occurs, an exception is “thrown” (i. e. , the exception has been recognized). • The flow of control is tranferred to the point where the exception is “caught” (I. e. , where the exceptionhandling code responds to it). In the jargon of some other programming languages, when an exception is recognized, an exception is: • “raised” (in place of “thrown”), then • “handled” (in place of “caught”). Same ideas, different jargon.

Exceptions: General Format The general structure of Java’s exception handling: try { // here

Exceptions: General Format The general structure of Java’s exception handling: try { // here goes the code that attempts to perform the // intended action, but that could throw an exception. . . } // try catch (Exception. Type 1 e) { // here goes the code to handle exception type 1. . . } // catch Type 1 catch (Exception. Type 2 e) { // here goes the code to handle exception type 2. . . } // catch Type 2 One is usually more interested in the type of the exception than in manipulating it as an object, so “e” is just an object often thrown away.

Exceptions: Simple Example An example showing the structure of Java’s exception handling: int i.

Exceptions: Simple Example An example showing the structure of Java’s exception handling: int i. Divisor; int i. Dividend; float f. Result; try { See, we don’t Care about the exception, Just about its type being arithmetic error. // get input for divisor and dividend. . . f. Result = (float) i. Dividend / i. Divisor; System. out. println(f. Result); } catch (Arithmetic. Exception e) { System. out. println("The divisor was 0"); . . . }

Exceptions: How in Java How Java handles Exceptions: If an exception is thrown in

Exceptions: How in Java How Java handles Exceptions: If an exception is thrown in a method, then you can do one of two things in response: 1. Catch the exception right then and there, and handle the exception yourself. You would do this if you have enough information to know how to handle the error. 2. Declare in the method header that whoever called the method has to handle the error. You would do this if you don't have enough information to know how to handle the error.

Exceptions: Example Given a full Queue, where a client tries to enqueue an item.

Exceptions: Example Given a full Queue, where a client tries to enqueue an item. . . • What should you have the Queue do? • How do you know what it should do? • Should it print out a message? • Should it try to increase the Queue’s size? • Should it not enqueue the item? • Should it dequeue an item to make room for the new item? What should you do? To put it simply, you don't know. Solution: 1. Your design/documentation for enqueue should state a precondition: /** PRE/POST: The queue is not full */ 2. The code will let the exception propagate.

Exceptions: Propagation When an exception is thrown, it must be caught immediately or declared

Exceptions: Propagation When an exception is thrown, it must be caught immediately or declared to be allowed to propagate. An example of code that will not compile: class Queue {. . . public boolean is. Empty( ) {. . . } // is. Empty Dequeue is not allowed to do this public void dequeue(Object o) { if (is. Empty( ) == true) throw new Queue. Empty. Exception( ); . . . } // dequeue. . . } // class Queue

Exceptions: Propagation • Results in an error saying that dequeue must catch or declare

Exceptions: Propagation • Results in an error saying that dequeue must catch or declare Queue. Empty. Exception. • To resolve this, modify dequeue so that the exception is declared to be thrown: public void dequeue(Object o) throws Queue. Empty. Exception { if (is. Empty( ) == true) throw new Queue. Empty. Exception( ); . . . } • The method header above declares that this method can throw a Queue. Empty. Exception and that the method calling dequeue( ) must plan on catching the Queue. Empty. Exception.

Exceptions: Example Suppose you want to use this Queue class to simulate a line

Exceptions: Example Suppose you want to use this Queue class to simulate a line of Customers, and you do: class Customer {. . . } // Customer class Cashier { Queue customer. Queue = new Queue( ); . . . public void get. Next. Customer( ) { Customer cust; . . . cust = (Customer) customer. Queue. dequeue( ); . . . } // get. Next. Customer. . . } // Cashier

Exceptions: Example (cont’d) This will result in a compile-time error because method get. Next.

Exceptions: Example (cont’d) This will result in a compile-time error because method get. Next. Customer must: • catch exception Queue. Empty. Exception, or • declare that Queue. Empty. Exception propagates upwards Thus, we can repair get. Next. Customer in one of two ways: • Option 1: have it catch exception Queue. Empty. Exception • Option 2: have it declare that this method allows Queue. Empty. Exception to propagate

Option 1 An Exception: Catching It public void get. Next. Customer( ) { Customer

Option 1 An Exception: Catching It public void get. Next. Customer( ) { Customer cust; try {. . . cust = (Customer) customer. Queue. dequeue( ); . . . } // try catch (Queue. Empty. Exception e) { // handle the Queue. Empty. Exception here. . . } // catch } // get. Next. Customer

Option 2 An Exception: Declaring that it will propagate public void get. Next. Customer(

Option 2 An Exception: Declaring that it will propagate public void get. Next. Customer( ) throws Queue. Empty. Exception { Customer cust; . . . cust = (Customer) customer. Queue. dequeue( ); . . . } // get. Next. Customer This option dictates that whoever calls method get. Next. Customer( ) has the responsibility of handling the exception.

Exceptions: Another Example public class Test { What happens here? public static void A(

Exceptions: Another Example public class Test { What happens here? public static void A( ) { int array[ ] = new int[5]; try { System. out. println( "In A's try. " ); array[ 5 ] = 1; } // try Output: catch( Array. Index. Out. Of. Bounds. Exception error ) { System. out. println( "In A's catch. " ); In A's try. } // catch In A's catch. } // A() public static void main( String argv[ ] ) { After try in main. try { A( ); } // try catch( Exception e ) { System. out. println( "In main's catch. " ); } // catch System. out. println( "After try in main. " ); } // class Test

Exceptions: Creating Your Own • To recognize the exceptional state, use standard if-else logic.

Exceptions: Creating Your Own • To recognize the exceptional state, use standard if-else logic. • To respond to them, you can create your own Exceptions are objects So you really define your own classes of exception • All Exceptions you create are extensions of java. lang. Exception For example: class Queue. Empty. Exception extends Exception { public Queue. Empty. Exception( ) { } public Queue. Empty. Exception(String str. Message) { super(str. Message); } }

Inheritance and Exceptions • You can take advantage of inheritance when dealing with exceptions.

Inheritance and Exceptions • You can take advantage of inheritance when dealing with exceptions. • Suppose we had an inheritance hierarchy of exceptions like this: Exception IOException Queue. Full. Exception File. Not. Found. Exception EOFException • You can have more than one catch block for a try block. • A catch block will only catch that exception or a subclass of that exception.

Inheritance and Exceptions This sequence of catches works: Exception try {. . . }

Inheritance and Exceptions This sequence of catches works: Exception try {. . . } // try catch (Queue. Full. Exception e) {. . . } // catch Queue. Full Exception IOException File. Not. Found. Exception catch (File. Not. Found. Exception e) {. . . } // catch File. Not. Found Exception catch (IOException e) {. . . } // catch IO Exception Queue. Full. Exception EOFException

Inheritance and Exceptions This sequence of catches doesn’t work: try {. . . }

Inheritance and Exceptions This sequence of catches doesn’t work: try {. . . } // try catch (IOException e) {. . . } // catch IOException Queue. Full. Exception File. Not. Found. Exception catch (File. Not. Found. Exception e) {. . . // this code can never be reached because // File. Not. Found is subclass of IOException } // catch File. Not. Found Exception EOFException

Inheritance and Exceptions try {. . . } // try Something you can do

Inheritance and Exceptions try {. . . } // try Something you can do with exceptions: catch (Queue. Full. Exception e) {. . . } // catch Queue. Full. Exception “catch-all” handler catch (IOException e) {. . . } // catch IOException catch (Exception e) {. . . // this will catch any other kind of exception // since all exceptions extend Exception } // catch base Exception

Exception & Runtime. Exception • In Java, there actually two types of Exceptions: •

Exception & Runtime. Exception • In Java, there actually two types of Exceptions: • those subclassed from Exception • those subclassed from Runtime. Exception A technoid annoyance: Both kinds deal with run-time errors • Those subclassed from Runtime. Exception do not have to be explicitly caught or declared in the method header. • This is good. It prevents code from being cluttered with exception handling: // possible Array. Index. Out. Of. Bounds customer. Array[10] = new Customer( ); // possible Arithmetic. Exception x = y / z; • These may still be caught and/or propagated upwards like normal exceptions.

Exceptions: When to Use • There is a problem with the internal state of

Exceptions: When to Use • There is a problem with the internal state of the program • A contract is violated • A security risk arises (Security. Exception) • There is an error with an object or the data it manipulates. • Coping with bad parameters • Dealing with Truly Exceptional conditions (memory, stack).

When to use exceptions: Internal State For example: public int get. Age(int i. Social.

When to use exceptions: Internal State For example: public int get. Age(int i. Social. Security. Number) throws Record. Keeping. Exception { int index = get. Hash. Key(i. Social. Security. Number); int i. Age = my. Array[index]. get. Age(i. Social. Security. Number); /* Similar to util. ASSERT( ) statement. */ if (i. Age <= 0) throw new Record. Keeping. Exception (“Exception: Age for “ + i. Social. Security. Number + “ not in range: “ + i. Age); else return i. Age; }

When to Use Exceptions: Contract Violated public Tree. Node get. Node. Recursively (int index,

When to Use Exceptions: Contract Violated public Tree. Node get. Node. Recursively (int index, Tree. Node current. Node) throws Missing. Node. Exception { if (current. Node == null) { throw new Missing. Node. Exception (“Exception: No node with ” + index + “ found”); } // if else if (current. Node. get. Number() == index) { return current. Node; } // else if (current. Node. get. Number() > index) { return get. Node. Recursively (index, current. Node. get. Left. Child()); } // if else { return get. Node. Recursively (index, current. Node. get. Right. Child()); } // else }// get. Node. Recursively

When to Use Exceptions: Error with objects public void initialize. Tree. Node(int i. Number.

When to Use Exceptions: Error with objects public void initialize. Tree. Node(int i. Number. Nodes) { if (my. Tree == null) { if (DEBUG) System. out. println (“Null tree found!”); throw new Null. Pointer. Exception (“Null tree found”); /* NOTE: Runtime exception; no need to declare propagation */ } else { for (int i=0; i < i. Number. Nodes; i++) { { Tree. Node new. Node = new Tree. Node( i ); tree. insert. Node(new. Node); } } } // initialize. Tree. Node

When to Use Exceptions: Bad Parameters public Integer convert. Number (String str. To. Convert)

When to Use Exceptions: Bad Parameters public Integer convert. Number (String str. To. Convert) { for (int i =0; I < str. To. Convert. length(); i++) { char ch. Temp = str. To. Convert. char. At(i); if (!Character. is. Digit(ch. Temp)) { if (DEBUG) System. out. println (“Bad input String: “ + str. To. Convert); throw new Number. Format. Exception(“Exception: “ + str. To. Convert + “ is not numeric”); } } } // convert. Number

When to Use Exceptions: Truly Exceptional Circumstances public class Chat { public static void

When to Use Exceptions: Truly Exceptional Circumstances public class Chat { public static void do. Stuff() { Listener. count(”Yes it is "); try { Chit. do. Stuff(); } //try catch (Stack. Overflow. Error e){ System. exit(0); } // catch } } // Chat public class Chit { public static void do. Stuff() { Listener. count(”No it isn’t "); try { Chat. do. Stuff(); } // try catch (Stack. Overflow. Error e) { System. exit(0); } // catch } } // Chit public class Listener { static int i. Count; public static void count(String str. Speaker){ i. Count++; System. out. println (str. Speaker + " is number " + i. Count); } } // Listener

When to Use Exceptions: Truly Exceptional Circumstances public Tree. Node get. Node(int index) {

When to Use Exceptions: Truly Exceptional Circumstances public Tree. Node get. Node(int index) { Tree. Node temp. Node; try { temp. Node = my. Tree. get. Node. Recursively(new Tree. Node(index)); } // try catch(Stack. Overflow. Error e) System. exit(1); } // catch return temp. Node; } // get. Node { Or less obviously

When Catching Exceptions you can. . . • Print an error message local? •

When Catching Exceptions you can. . . • Print an error message local? • Log the exception • Retry the method (maybe with default parameters) OOA/OOD/OOP “Who”/what knows enough to handle the exception? • Restore the system to some previously known "good" state. high-level? • Set the system to some "safe" state. • Let exception propagate to whoever called the method in which the exception arose • Catch it and ignore it “Catch it and ignore it” is generally bad: If the error was serious enough to throw an exception, it should be dealt with, not ignored.

Exceptions: Review Exception Defined: Object that defines an unusual or erroneous situation What to

Exceptions: Review Exception Defined: Object that defines an unusual or erroneous situation What to do? Not handle the exception (program halts. Ugh!) Handle the exception where it occurs (try. . . catch. . . ) Handle the exception at another point in the program (exception propagation) Throwing an exception: Part of the contract of a method (throws) Responsibility of the client to handle (try…catch…)

A Few Words on Vectors

A Few Words on Vectors

Vectors • An array (list) that dynamically resizes itself to whatever size is needed.

Vectors • An array (list) that dynamically resizes itself to whatever size is needed. • A partial API for Vector: class Vector { public void add. Element( Object obj ) public boolean contains(Object elem) public Object element. At(int index) public Object first. Element() public int index. Of(Object elem) public void insert. Element. At(Object obj, int index) public boolean is. Empty() public Object last. Element() public int last. Index. Of(Object elem) public void remove. All. Elements() public boolean remove. Element(Object obj) public void remove. Element. At(int index) public void set. Element. At(Object obj, int index) public int size() } // of Vector

Vectors • Vector is a class. • Must have an object of type Vector

Vectors • Vector is a class. • Must have an object of type Vector instantiated via new( ) to get an instance of Vector. • All rules of good OO programming apply. • Thus, access by requesting services via methods, not via direct access (such an array). size( ) returns current number of elements. Common Examples element. At(int index) returns reference to element at specified index. insert. Element. At( Object obj, int index ) insertion into linked list (but slower); add. Element (Object obj) adds to end.

Vectors: • Can be populated only with objects; • Cannot be populated with primitives;

Vectors: • Can be populated only with objects; • Cannot be populated with primitives; • Can be populated with objects that contain primitives; • If you need to populate them with primitives, use type wrapper classes e. g. , Integer for int, etc. • Will allow you to populate them with any type of object. . . • Thus, good programming requires that the programmer enforce typing within a Vector, because Java doesn’t.

Vectors and casting: • Vectors are a subclass of class Object. • Thus, vectors

Vectors and casting: • Vectors are a subclass of class Object. • Thus, vectors can handle any class of object (i. e. , no type checking) • Thus, must cast any object obtained from a Vector before invoking any methods not defined in class Object. Vector v = new Vector ( ); Student s = new Student( “Joe” ); Student other. Student; v. add. Element( s ); other. Student = (Student) v. element. At(0);

Vectors and instanceof: // Assume we have vector v and int k. // Assume

Vectors and instanceof: // Assume we have vector v and int k. // Assume int k is an index within the // range [0. . (v. size( ) - 1)]. Object o; o = v. element. At(k); // no cast needed, already Object if (o instanceof Student) { // do stuff for Student } if (o instanceof Cafeteria) { // do stuff for Cafeteria } Design issue: This example is legal Java, but is it good programming?

Vectors and Arrays: statically sized Vectors: dynamically sized Arrays: can directly access, e. g.

Vectors and Arrays: statically sized Vectors: dynamically sized Arrays: can directly access, e. g. , my. Array[6] but shouldn’t (except maybe within the class in which they’re declared IF efficiency concerns; or for testing purposes. ) Vectors: must use methods to access. Vector services provide a good model for the Array services you should implement.

Vectors versus Linked Lists Can use Vectors to simulate a Linked List: • Don’t

Vectors versus Linked Lists Can use Vectors to simulate a Linked List: • Don’t want direct access to data, so. . . • Provide methods for get. Previous( ), get. Next( ), etc. that do the std. Linked List things. • While the list is implemented as a Vector, the client uses it as if it’s a Linked List. BUT. . . There are performance implications (that may or may not matter for a given instance). What is the cost of: • insertion? • deletion?

Vectors versus Linked Lists For ordered Linked Lists: • cost of traversal to locate

Vectors versus Linked Lists For ordered Linked Lists: • cost of traversal to locate target: O(N) • cost of insert/delete: O(1) • total cost: O(N) Thus, Vectors imply twice the work For ordered Vector: • cost of traversal to locate target: O(N) (if accessible via direct access, then O(1) ) • insertion or deletion of element implies (average case), moving O(N) elements • total cost: O(N) Thus, at first glance, equivalent… But what does Big Oh hide here? • Linked Lists: search thru N/2, plus insert/delete • Vectors: search thru N/2, plus moving N/2

Vector Capacity • Capacity is dynamic • Capacity can grow and shrink to fit

Vector Capacity • Capacity is dynamic • Capacity can grow and shrink to fit needs • Capacity grows upon demand • Capactiy shrinks when you tell it to do so via method trim. To. Size( ) • Using trim. To. Size( ) implies performance costs upon subsequent insertion. • When extra capacity is needed, then it grows by how much? • Depends on which of three constructors is used. . .

Vector Capacity Three Vector constructors: • public Vector (int initial. Capacity, int capacity. Increments);

Vector Capacity Three Vector constructors: • public Vector (int initial. Capacity, int capacity. Increments); • public Vector (int initial. Capacity); • public Vector( ); First constructor (2 parameters): • begins with initial. Capacity • if/when it needs to grow, it grows by size capacity. Increments. Second constructor( 1 parameter): • begins with initial. Capacity • if/when needs to grow, it grows by doubling current size. Third construtor (no parameters): • begins with capacity of 10 • if/when needs to grow, it grows by doubling current size.