Documente Academic
Documente Profesional
Documente Cultură
Object-Oriented Design
In an object-oriented programming language, we are dealing with class and objects. Here we review briefly some major concepts of object-oriented programming.
Inheritance Polymorphism method overriding method overloading Keyword: this Exception Interface, Abstract Classes Casting
Inheritance Inheritance allows classes (subclasses) to use the members of another class (superclass) as their own members. We use the keyword extends to make a class a subclass of a superclass. Example:
public class SalaryEmployee extends NewEmployee { // class body }
Inheritance Inheritance allows classes (subclasses) to use the members of another class (superclass) as their own members. We use the keyword extends to make a class a subclass of a superclass. Example:
public class SalaryEmployee extends NewEmployee { // class body }
Polymorphism Polymorphism adds flexibility to the programming. Java provides two kinds of polymorphism: method overriding and overloading. override: A method of a superclass is re-defined in the subclass. Example:
class S { //. . . public void a() { // . . . } } class T extends S { //. . . public void a() { // . . . } }
overloading: A method name is used by more than one method in the same class. Example:
class T { // . . . public int a() { // . . . } public void a( int x ) { // . . . } }
The Keyword this In Java, the keywork this is a reference to the current object. We may see code like the example below.
class T { private double x; public T() { this( 0.0 ); } public T( double y ) { x = y; } }
class S { int x, y;
Exceptions In Java, exceptions are objects that can be thrown and then caught. Example: void a() throws myException { //. . . if( . . . ) { throw new myException( "Error" ); } } class B { // . . . public int b() { try { // . . . a(); } catch( myException e ) { // do something } } }
Because an exception is a class, we need also a file that contains this class. Example:
class myException extends RuntimeException { public myException() {} public myException( String s ) { super( s ); } }
An example:
import java.lang.*; //Here we define some exception types of our own. //Exception classes generally have constructors but no data //or other methods. All these do is call their superclass //constructors. class MyException extends Exception { public MyException() {super();} public MyException(String s) { super(s); } } class MyOtherException extends Exception { public MyOtherException() { super();} public MyOtherException(String s) { super(s); } }
class MySubException extends MyException { public MySubException() { super(); } public MySubException(String s) { super(s); } } public class Throwtest { //This is the main() method. Note that it uses two //catch clauses to handle two standard Java exceptions. public static void main(String argv[]) { int i = 2;
//First, covert our argument to an integer. //Make sure we have an argument and that it is convertible. try { i = Integer.parseInt(argv[0]); } catch (ArrayIndexOutOfBoundsException e) {//argv is empty System.out.println("Must specify an argument"); return; } catch (NumberFormatException e) {//argv[0] is not an integer System.out.println("Must specify an integer argument"); }
catch (MyException e) { //Point 1 //Here we handle MyException and its subclass MySubException if (e instanceof MySubException) System.out.print("MySubException: "); else System.out.print("MyException: "); System.out.println(e.getMessage()); System.out.println("Handle at point 1"); } }
public static void b(int i) throws MyException { int result; try { System.out.print("i = " + i); result = c(i); System.out.print(" c(i) = " + result); } catch (MyOtherException e) { //Point 2 //Handle MyOtherException: System.out.println("MyOtherException: " + e.getMessage()); System.out.println("Handle at point 2"); } finally { //Terminate the output we printed above with a newline. System.out.print("\n"); } }
public static int c(int i) throws MyException, MyOtherException { switch (i) { case 0: //processing resumes at point 1 above throw new MyException("input too low"); case 1: //processing resumes at point 1 above throw new MyException("input still too low"); case 99: //processing resumes at point 2 above throw new MyOtherException("input too high"); default: return i*i;}}}
If you use JCreator to compile and run your program, the above program Should be changed as follows: import javax.swing.JOptionPane; public static void main(String argv[]) { int i = 2; try {i = (int) new Integer(JOptionPane.showInputDialog("Enter an integer"));}
try { i = Integer.parseInt(argv[0]); }
Interfaces Java provides a structure called interface for the implementation of application programming interface (API). An interface defines the methods that a class must implement. If a class implements a given interface, it must have all the methods defined in the interface. Example:
public interface CarDriver { public static final int speed = 100; public Reading panel(); public void shiftGear( int n ); public void brake( int n ); public void fuel( int n ); }
public class pickupTruck implements CarDriver { // . . . public Reading panel() { // . . . return rd; } public void shiftGear( int n ) { // . . . } public void brake( int n ) { // . . . } public void fuel( int n ) { // . . . } }
An example:
import java.util.*; import java.lang.*;
interface CanFight { void fight ( );} interface CanSwim { void swim ( );} interface CanFly {void fly ( );}
class ActionCharacter { public void fight( ) { }} class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { public void fight ( ) {System.out.println(Can fight!);} public void swim ( ) {System.out.println(Can swim!); } public void fly ( ) {System.out.println(Can fly!);} }
public class Adventure { static void t(CanFight x) { x.fight();} static void u(CanSwim x) { x.swim();} static void v(CanFly x) { x.fly();} static void w(ActionCharacter x) { x.fight();} public static void main (String[ ] args) { Hero h = new Hero( ); t(h); //Treat it as a CanFight u(h); //Treat it as a CanSwim v(h); //Treat it as a CanFly w(h); //Treat it as an ActionCharacter } }
CanFight implementation
CanSwim
CanFly
instantiation h
static void t(CanFight x) { x.fight();} static void u(CanSwim x) { x.swim();} static void v(CanFly x) { x.fly();} static void w(ActionCharacter x) { x.fight();} Hero h = new Hero( ) t(h); //Treat it as a CanFight u(h); //Treat it as a CanSwim v(h); //Treat it as a CanFly w(h); //Treat it as an ActionCharacter
Abstract Classes Weve reviewed this before. An abstract class contains both abstract methods and normal methods. The abstract methods must be overridden in subclasses. Because of the abstract methods, an abstract class cannot be instantiated.
Casting Recall that casting can be used to convert data types. Now let us see how it works with objects. If class T extends class S, then we can do the following reference:
T o = new T(); S so = o; S so1 = new T();
S
extends
so
assign
or
S so = new T(); T o = ( T )so;
Interfaces can be treated similar to superclasses. If class T implements interface I, we can do the following:
T I I T o = new T(); io = o; io1 = new T(); o1 = ( T )io1;
I
Impl.
io
assign
type casting
(T) io
o
8 bits
... 1 0 0 ... 1 0
A possible error by basic data type casting: int i = 258; byte b = (byte) i;
16 bits
Fragment 2:
for( int i = 0; i < n; i++ ) { a[ i ] = i * i; }
Running Time In this course, we want to compare algorithms. We want to know which algorithm is more efficient. By efficiency, we mean that an algorithm either takes less time to run or uses less memory than the other. Let us look at the running time here. Running time depends on many factors. Since we will be dealing with a collection of objects most of the time, what interests us is the relationship between running time and the number of objects.
T f (n)
Here T represents running time. In fact, we are interested in the situation when the number of objects becomes very large.
f (n)
O(1) O(logn) O(n) O(n2) O(n3)
Therefore, if running time is proportional to n3, the code will take very long time to run. Next is proportional to n2 and so on. If running time is a constant, it is fast. In fact, it is the fastest type.
Suppose that we have a task that can be done by one of the two algorithms, A and B. The running time of algorithm A is proportional to the number of objects, n while the running time of algorithm B is proportional to the square of n. Which algorithm we should use?
A Simple Rule to determine Running Time There is a simple rule to estimate running time (or the relationship, so to speek) If we have a loop like this:
for( int i = 0; i < n; i++ ) { a[ i ] = i * i; }
The running time is proportional to the square of n (when n is very large). Now we see a pattern here.
Mathematicians have a fancy symbol for running-time relationships when n is large. They call it big-Oh. Relationship constant On the order of logn on the order of n on the order of n2 on the order of n3 big-Oh Symbol
O(1)
O(logn)
O ( n)
O(n 2 )
O(n 3 )
Recursion In Java course, we have learned recursion. recursion: A method calls itself. Example: Factorial function can be expressed as 0! = 1, 1! = 1, n! = n * (n 1)!, n > 1. We can use this relationship to calculate factorial function.
n=4
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n=4
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n=3
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n=4
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); } public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n=3
n=2
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
Return 24
n=4
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); } public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
Return 6
n=3
Return 2
n=2
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
Return 1
n=1
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n=4
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); } public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n=3
2*1=2
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n=4
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
3*2=6
public static long factorial( long n ) { if( n <= 1 ) return 1; else return n * factorial( n - 1 ); }
n = 4 * 6 = 24
Running Time of a Recursion Let us take the factorial method as an example. n 1 2 3 4 Number of Function Calls 1 2 3 4
Therefore, the running time of a recursion is proposional to the number of function calls. In this case, it is propotional to n, the size of the problem.