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
Introduction to Computer Science Unit 12 • Packages • Polymorphism • Interfaces • Exceptions • Streams – input/output Packages • Java classes are placed into directories (or folders) on the computer • The classes in each directory form a package • This helps organize classes, and also gives another way of controlling access among classes • Example: java.applet is the package of classes in subdirectory "applet" under the directory "java" (whose location varies depending on the system) 12- 2 Sample Packages that come with the Java API (continually evolving) java.applet Classes for implementing applets java.awt For graphics, windows, GUIs java.awt.event For AWT event-handling model java.awt.image Classes for image processing java.awt.peer Interface defs, platform-indep. GUIs java.io Classes for input and output java.lang Basic language classes (String, Math) java.net Classes for networking java.util Useful auxiliary classes, like Date 12- 3 Direct Use of Java API Class • To use, for example, the class Math in the package java.lang, it is possible to use the fully-qualified name: x = java.lang.Math.sqrt(3); 12- 4 Importing Classes • Of course, it's more convenient to do it the way we've been doing it, using the import statement; either import package_name.class_name; or import package_name.*; 12- 5 Importing import package_name.class_name; allows class_name to be used without giving the full package name import package_name.*; allows all classes in the package to be used without qualifying their names 12- 6 Example of import java.util.Date d = new java.util.Date( ); java.awt.Point p = new java.awt.Point(1, 2); java.awt.Button b = new java.awt.Button( ); can be abbreviated as import java.util.Date; import java.awt.*; … Date d = new Date( ); Point p = new Point(1, 2); Button b = new Button( ); 12- 7 It's Always There • Java always assumes that the classes in java.lang (basic language classes, such as String and Math) are available • It's as if you have the statement import java.lang.*; at the beginning of every program 12- 8 Packages You Define • All of the .class files placed in one directory belong to the same, unnamed package • To cause a class to be placed in a particular named package: –Put the .class file in the appropriate directory –Compile the class with the package statement, which must be the first non-comment line in the Java source file: package package-name; 12- 9 Classes in Different Packages • Classes in different packages obviously have different fullyqualified names • Classes in different packages also have different rules regarding the visibility of names 12- 10 Example: Visibility of Instance Variables • A public instance variable is visible to all other classes • A private instance variable is visible only to the methods of its class • If it is declared neither public nor private, then it has "package visibility"; it is visible to methods defined in other classes in the same package as its class • Same rules apply to static variables and instance and static methods 12- 11 Similarly for Classes • A class declared public is visible to all classes • A class not declared public is visible to the classes in its own package • A class cannot be declared private 12- 12 private, public, protected • Private variables and methods: no access by clients or by subclasses • Public variables and methods: accessed by clients and by subclasses • Default (no label): variables and methods have package visibility, accessible only to clients and subclasses in the same package 12- 13 Protected • We might want to give subclasses access to variables and methods without allowing clients to have access • That's the purpose of another category of accessibility: protected • Members declared protected are visible to other classes in the same package, and to subclasses in other packages, but not to clients in other packages 12- 14 Two Kinds of Visibility • Inherited Visibility and Direct Access Visibility: class A { int _x; … } class B extends A { … _x … // inherited visibility of x … A._x … // direct access visibility of x } 12- 15 Summary of Visibility Visibility public clients in D same package default D protected private D none clients in D different package none none none subclass in D & I same package D&I D&I none subclass in D & I different package none I I = inherited access, D = direct access none 12- 16 Using Protected • When there's a possibility that a class will have subclasses (and you want the attributes and methods to be usable in the inheritance hierarchy), you should use protected • Inherited protected variables and methods are considered to be protected members of the subclass (visible to further subclasses, and hidden from clients) • Inherited public variables and methods are considered to be public members of the subclass (visible to everyone) 12- 17 class PreciseTime extends Time { Now we override … public void printTime ( ) { printTime( ) if ((_hour == 0) && (_minute == 0)) System.out.print(“midnight”); else if ((_hour == 12) && (_minute == 0)) System.out.print(“noon”); else { if (_hour == 0) System.out.print(12); else if (_hour > 12) System.out.print(_hour - 12); else System.out.print(_hour); if (_minute < 10) System.out.print(“:0”+ else System.out.print(“:” + if (_second < 10) System.out.print(“:0”+ else System.out.print(“:” + if (_hour < 12) else } } } System.out.print(“AM”); System.out.print(“PM”); _minute); _minute); _second); _second); Doesn't Work • Problem is, it doesn't work: the new printTime accesses _hour and _minute, private instance variables of class Time • So we need to change the definition of class Time: class Time { protected int _hour, _minute; … } • Still no access to _hour and _minute from regular clients of Time, just to PreciseTime and other subclasses of Time 12- 19 Constructors for Subclasses • Java guarantees that a class’s constructor method is called whenever an instance of that class is created • It also guarantees that the constructor is called whenever an instance of any subclass is created • Therefore, every constructor method must call (explicitly or implicitly) its superclass constructor method 12- 20 Constructors for Subclasses • If we have an inheritance hierarchy: class B | subclass C of B | subclass D of C … subclass F of … | subclass G of F If a new object of class G is created, the constructors will be called in order B, C, D, … , F, G (i.e., G calls F, which calls E,…) 12- 21 Constructor Chaining • If the first statement in a constructor is not an explicit call to a constructor of the superclass (using super), or an explicit call to another constructor in the same class (using this), then Java implicitly inserts the call super( ) (i.e., super with no arguments) • Even if this is used to invoke another constructor in the same class, eventually some constructor will (explicitly or implicitly) invoke the superclass constructor • If the superclass has no zero-argument constructor, the implicit super( ) causes a compilation error 12- 22 super( ) • Therefore, if a superclass doesn't have a zeroargument constructor, the subclass must explicitly call the superclass's constructor with arguments: class C extends B { … public C (…) { super( B's constructor arguments ); … } } 12- 23 super( ) • The call to super(…) must be the first statement in the body of C's constructor • The constructor for PreciseTime made use of this feature, calling super(h, m) to allow the Time constructor to do initialization public PreciseTime(int h, int m, int s) { super(h, m); _second = s; } 12- 24 Dynamic Binding • I said that a PreciseTime object is also (kind of) a Time object • It's a useful way of thinking about it, and it is the basis for a very powerful feature of Java, dynamic binding • A PreciseTime object can be used almost anywhere that a Time object can be used 12- 25 Several Passes at Polymorphism • Polymorphism means “taking many forms”: a reference of a given class can adapt take the form of any of its subclasses. • First we will look at the polymorphism and dynamic binding quickly – a few key facts • Then we will look at it in depth – this set of slides adapted (with permission) from: www.cc.gatech.edu/classes/AY2002/cs1322_spring/slides/ current/CS2_22_PolymorphismDynamicBinding.ppt 12- 26 This is legal, blurring the distinction of Time and PreciseTime Time dawn; dawn = new PreciseTime(3, 45, 30); • A Time variable (like dawn) can contain a reference to a PreciseTime object • More generally, a CCC variable can contain a reference to any object of any class in CCC's subclass hierarchy (down the tree – but not up the tree) 12- 27 There's an order to this Time dawn; dawn heap 12- 28 This is legal, blurring the distinction of Time and PreciseTime Time dawn; dawn = new PreciseTime(3, 45, 30); dawn dawn Attributes: _hour = 3 _minute = 45 _second = 30 Methods: … heap 12- 29 So now what? • What happens if we send dawn the printTime( ) message? dawn.printTime( ); printTime( ) is defined in both Time and PreciseTime classes • dawn was declared as a Time variable, but the actual object in dawn is a PreciseTime object • So the printTime( ) defined in the PreciseTime class will be used 12- 30 Static Binding of Methods • Before we had subclasses, life was simple • The Java compiler could always figure out, without running the program, which instance method would be invoked • The ability to know the exact method that will be invoked every time a message is sent is called static binding of methods 12- 31 Dynamic Binding • This is no longer true when we have subclasses • It is possible to specify in a program that there's an object of some class, B (i.e., the object is defined as being referenced by a variable of type B) • B has subclasses C and D • C and D each provide their own definition of method f ( ) B newObj; B C D 12- 32 Dynamic Binding • The actual creation of the object (C or D) only occurs at runtime x might come from the user if (x == 7) newObj = new C( ); else newObj = new D( ); • Therefore, the compiler can't know, when the object newObj is sent f ( ), whether newObj.f( ); it will be handled by the C Using f( ) from definition or the D definition; class C or D? we can only know at runtime 12- 33 Polymorphism Pass 2: More In Depth • Polymorphism is an important concept that is a part of Object Oriented Programming • We often would like to deal with a collection of various types of objects. We want to process members of that group in a generic way. Yet in the end, we’d like specific and appropriate behavior to still occur. • This set of slides adapted (with permission) from: www.cc.gatech.edu/classes/AY2002/cs1322_spring/slides/current/ CS2_22_PolymorphismDynamicBinding.ppt 12- 34 Polymorphism • Example: We have an array of animals, each of which is an object of one subclass out of several possible subclasses of Animal. The array is declared to have Animal as its element type. Now we’d like to process through the array and have each element invoke a makeNoise() method. • Luckily when a method call is made, the compiler isn’t too concerned about the specifics of the method being called. It’s question is: “is there a method with a matching signature?” 12- 35 Array of Animals Animal extends Bird extends Dog extends Fish • Fill an array with Bird, Dog, Fish – but the array is an array of Animal An array of Animal – we want to fill it with different instances of animals, but send the same message to each – they each will respond differently [0] [1] [2] 12- 36 Polymorphism class Animal { public void makeNoise ( ) { System.out.println("I am an animal."); } // of makeNoise } // of Animal class Fish extends Animal { public void makeNoise( ) { System.out.println("Glug glug gurgle gurgle"); } // of makeNoise } // of Fish class Bird extends Animal { public void makeNoise( ) { System.out.println("Tweet tweet flap flap"); } // of makeNoise } // of Bird 12- 37 Polymorphism (cont’d) class Dog extends Animal { public void makeNoise( ) { System.out.println("Sniff sniff woof woof"); } // of makeNoise public void bark( ) { System.out.println("Arf Arf"); } // of bark } // of Dog 12- 38 Polymorphism public class Driver { public static void main (String[ ] argv) { Animal[ ] animalArray = new Animal[3]; int index; Output: Tweet tweet flap flap animalArray[0] = new Bird( ); Sniff sniff woof woof animalArray[1] = new Dog( ); Glug glug gurgle gurgle animalArray[2] = new Fish( ); for (index = 0; index < animalArray.length; index++) { animalArray[index].makeNoise( ); } // of for the Animal class has } // of main makeNoise, so any } // of Driver member of the array can makeNoise 12- 39 Polymorphism and Dynamic Binding • Polymorphism & Dynamic Binding together insure that the correct makeNoise( ) method will always be called. • An object of 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. Not necessarily. 12- 40 instanceof • The keyword instanceof is used to ask an object if it is an instance of the specified class, e.g., "Is this particular animal of class Dog?" (d instanceof Dog) • It’s a boolean relation, returning true or false: if (d instanceof Dog) { … } 12- 41 Casting with Polymorphism public class Driver2 { public static void main(String[ ] argv) { Animal[ ] = animalArray[3]; Dog d; int i; animalArray[0] = new Bird( ); animalArray[1] = new Dog( ); animalArray[2] = new Fish( ); for (i = 0; i < animalArray.length; i++) if (animalArray[i] instanceof Dog){ d = (Dog) animalArray[i]; d.bark( ); } // if We cast before calling bark() } // main because only dogs can bark. Not all Animals can execute the method } // Driver2 12- 42 Upcasting • Why didn’t we have to explicitly cast Bird, Dog and Fish to Animal when we put the instances into the array on the previous slide? • Because this is upcasting – casting from a derived class to a base class – and Java does it for us automatically • You can also write it explicitly if you want (no harm done) 12- 43 Casting a Superclass to a Subclass • Casting used here to give an object of a superclass the form of the appropriate subclass. If we just wrote: if (animalArray[i] instanceof Dog) { animalArray[i].bark(); } it would produce an error because objects of class Animal have no method called bark. So, we first cast the object that instanceof tells us is indeed a Dog object, as a Dog. if (animalArray[i] instanceof Dog) { d = (Dog) animalArray[i] d.bark( ); } 12- 44 Why is Casting Necessary Here? • If Java can determine that a given Animal is or is not a Dog (via instanceof), then why do we need to cast it to a Dog object before Java can recognize that it can bark? • Why can’t Java do it for us automatically? • Answer: the difference between compiletime and run-time type checking. 12- 45 Why is Casting Necessary Here? Source code Byte code Compile errors JVM Interpreter Program runs errors Compile-time Errors: Run-time Errors: • Those that are discernable without the program executing. • Those that are discernable only when the program is running with actual data values. • Question of language legality: "Is this a legal statement?" e.g., index = strName; • Question of execution legality: "Is it legal for this variable to have the actual value assigned to it?", e.g., Statement is not legal. animalArray[<badIndex>] = someAnimal Statement legal, but particular index value isn’t. 12- 46 Why is Casting Necessary Here? if (animalArray[i] instanceof Dog) { animalArray[i].bark(); } • 1st line is legal. 2nd line isn’t (unless array has Dog). We can see that 1st line guarantees 2nd is legal. • Compiler cannot see inter-statement dependencies… unless compiler runs whole program with all possible data sets! • Runtime system could tell easily. . . BUT. . . We want most checking at compile-time for reasons of both performance and correctness. if (animalArray[i] instanceof Dog) { d = (Dog) animalArray[i]; 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. 12- 47 How Objects Are Created Dog d = new Dog(); 1. d 2. 3. Object Object Object Animal Animal Animal Dog d Dog d Dog Execution Time An implicit super() calls parent class constructor first. After all, a Dog is-a Animal, && is-a Object 12- 48 Multiple References to Different Types of the Same Instance We can create new references that point to different types in the same block of memory. Object o Animal a Dog Animal a = new Dog(); Object Animal a Dog Object o = a; 12- 49 Dynamic Binding o a Object .toString() Animal .toString() Dog .toString() When calling a method on a reference, the method must be present in the type (or inherited). However, the specific implementation called is determined at runtime. That’s ‘dynamic binding’. System.out.println(o.toString()); Dynamic binding provides runtime resolution to the most specific implementation possible. 12- 50 Casting and Polymorphism o Object Animal a Dog .doYourThing() .doYourThing() o.doYourThing(); // ERROR! Dynamic binding does not work miracles. The reference type must have the method available (in the present class or inherited), or else a compilation error occurs. The calling type must have the method, either in its instance, or from its parent. 12- 51 What Should Our Array Elements Be? So, we have these three blocks, representing objects in memory, each different, holding unique references and primitive values. How can these disparate objects be held in an array, which must be heterogeneous? Object Object toString() Animal int numLegs = 2 String strType toString(); move(); Bird move(); Object toString() Animal int numLegs = 0 String strType toString(); move(); Fish move(); toString() Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); The organizing principle is the shared inherited relationship with Animal. Since Fish, Dog and Bird all extend from Animal, we can make an Animal array to hold different expressions of this class. 12- 52 What Should Our Array Elements Be? So, we have these three blocks, representing objects in memory, each different, holding unique references and primitive values. How can these disparate objects be held in an array, which must be heterogeneous? Object Object toString() Animal int numLegs = 2 String strType toString(); move(); Bird move(); Object toString() Animal int numLegs = 0 String strType toString(); move(); Fish move(); toString() Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); We could also have made an array of Object types. But we then will have problems invoking methods on members of this array, since Object lacks key methods--like move(). 12- 53 What Should Our Array Elements Be? So, we have these three blocks, representing objects in memory, each different, holding unique references and primitive values. How can these disparate objects be held in an array, which must be heterogeneous? Object Object toString() Animal int numLegs = 2 String strType toString(); move(); Bird move(); Object toString() Animal int numLegs = 0 String strType toString(); move(); Fish move(); toString() Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); So we select Animal as our common type. It’s the most specific type, and yet is still held in common by all the members we plan to hold in the array. 12- 54 Multiple References to Instance Bird bTemp = new Bird(); Object oTemp = (Object) bTemp; Animal aTemp = (Animal) bTemp; Object toString() Animal Here, the casting is not needed since we are upcasting. It’s shown to be explicit. int numLegs = 2 String strType toString(); move(); Bird move(); oTemp aTemp bTemp Recall that we can have many reference types pointing to the same Fish or Dog or Bird instance in memory. Thus, since arrays must be of a single type, we just polymorph any references we have into Animal references. 12- 55 Array Elements are of a Single Type Object toString(); Animal [ ] final int length = 3 [1] [0] Object Object int numLegs = 2 String strType toString(); move(); Bird move(); Object toString() toString() toString() Animal [2] Animal int numLegs = 0 String strType toString(); move(); Fish move(); Thus, when we have many objects in memory, we polymorph our references to the instances. This gives us an array of Animal types, even though each instance is a different subclass. Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); Let’s invoke some methods on these objects. 12- 56 Object toString() Is our picture correct? Animal int numLegs = 2 String strType toString(); move(); Bird What if we executed this code: Object oTemp; oTemp = animalArray[1]; move(); Object toString(); Object toString() Animal [ ] Animal int numLegs = 0 String strType toString(); move(); final int length = 3 Object oTemp Fish move(); Object toString() Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); No. The code works. But our drawing is pointing to the wrong part of our object Object toString() Is our picture correct? Animal int numLegs = 2 String strType toString(); move(); Bird What if we executed this code: Object oTemp; oTemp = animalArray[1]; move(); Object toString(); Object toString() Animal [ ] Animal int numLegs = 0 String strType toString(); move(); final int length = 3 oTemp Fish move(); Object toString() Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); Much better. This fussiness will pay off shortly. Object toString() Does this work? Animal int numLegs = 2 String strType toString(); move(); Bird move(); Object Now, let’s add another line of code: Object oTemp; oTemp = animalArray[1]; oTemp.move(); toString(); Object toString() Animal [ ] Animal int numLegs = 0 String strType toString(); move(); final int length = 3 Fish oTemp move(); Object toString() Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); NO. The class Object has no method called move() Object toString() Animal int numLegs = 2 String strType toString(); move(); Bird move(); Object toString(); Object The fix is in: Object oTemp; oTemp = animalArray[1]; Animal aTemp = (Animal) oTemp; toString() Animal [ ] Animal int numLegs = 0 String strType toString(); move(); final int length = 3 Fish oTemp move(); Object toString() aTemp Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); Note the explicit down casting was necessary. Object toString() Animal int numLegs = 2 String strType toString(); move(); Bird move(); Object toString(); Object The fix is in: Object oTemp; oTemp = animalArray[1]; Animal aTemp = (Animal) oTemp; aTemp.move(); toString() Animal [ ] Animal int numLegs = 0 String strType toString(); move(); final int length = 3 Fish oTemp move(); Object toString() aTemp Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); Hmm... Let’s look at this closely. Object oTemp; oTemp = animalArray[1]; Animal aTemp = (Animal) oTemp; aTemp.move(); Object Object toString() toString() Animal int numLegs = 2 String strType toString(); move(); Animal Bird move(); Object toString(); Object Animal int numLegs = 0 String strType toString(); move(); It looks like both Animal and Fish have move( ) methods. Which one gets called when the aTemp.move( ) line executes? Fish toString() Animal [ ] final int length = 3 oTemp int numLegs = 3 String strType toString(); move(); aTemp move(); Fish move(); Object toString() Animal int numLegs = 3 String strType toString(); move(); Dog move(); bark(); The reference type we are using is an Animal type. Would that determine whether Animal or Fish has their method called? Dynamic Binding Object oTemp; oTemp = animalArray[1]; Animal aTemp = (Animal) oTemp; aTemp.move(); Object aTemp toString() Animal Understand this term. Understand what is does. It is a CORE feature of any Object Oriented language. int numLegs = 3 String strType toString(); move(); Fish move(); Here, the principle of dynamic binding will ensure that at run time, the most specific behavior will be invoked. Here, the Fish move() method is more specific than its parent method. So, the Fish’s move() method gets called with the aTemp.move() line. 12- 63 Sanity Check Object oTemp; oTemp = animalArray[1]; Animal aTemp = (Animal) oTemp; System.out.println (oTemp.toString()); What Happens Here? oTemp Object toString() Animal int numLegs = 3 String strType toString(); move(); Fish Does casting somehow overpower dynamic binding? move(); What about: System.out.println ( ((Object)oTemp).toString() ); 12- 64 Sanity Check Object oTemp; oTemp = animalArray[1]; Animal aTemp = (Animal) oTemp; System.out.println (oTemp.toString()); What Happens Here? Object oTemp toString() Animal int numLegs = 3 String strType toString(); move(); Fish move(); No matter how you cast things, dynamic binding takes hold. It’s like the law of gravity. What about: System.out.println ( ((Object)oTemp).toString( ) ); 12- 65 Sanity Check Object oTemp; oTemp = animalArray[1]; Animal aTemp = (Animal) oTemp; System.out.println (oTemp.toString()); What if Fish had its own toString()? Object oTemp toString() Animal int numLegs = 3 String strType toString(); move(); Fish move(); toString(); No matter how you cast things, dynamic binding takes hold. It’s like the law of gravity. Dynamic binding will always resolve, at run time, to the most specific version of the method. ALWAYS. 12- 66 “Always?” Object oTemp; oTemp = animalArray[1]; oTemp.move(); // WRONG! Does dynamic binding also work miracles? That is, does it let you find methods in extending classes, if the present class does not have such a method? Object oTemp toString() Animal int numLegs = 3 String strType toString(); move(); No such method move() in Object Fish move(); toString(); NO. This would cause a compile time error. Java is strongly typed, meaning that each time you invoke a method, the method MUST be present in the class--even if dynamic binding would later find a more specific version. So: no, dynamic binding does not defeat type safety in Java. 12- 67 Another example – A Hierarchy Diagram Object File RestrictedFile Has method (among others): public String toString() Has methods: public boolean isOpen() public void open() public void close() public String getName() Has methods: public boolean isLocked() public void lock() public void unlock(long key) Redefines open() (only open file if it is unlocked; it’s locked at creation) 12- 68 Sub-classes as Sub-types • We can view a RestrictedFile object from 3 different points of views: –As a RestrictedFile. This is the most narrow point of view (the most specific). This point of view ‘sees’ the full functionality of the object. –As a File. This is a wider point of view (a less specific one). We forget about the special characteristics the object has as a RestrictedFile (we can only open and close the file). –As a plain Object. 12- 69 Variables can Reference Sub-class Values • We view an object by using an object reference. • A variable of type ‘reference to File’ can only refer to any object which is a File. File f = new File(“story.txt”); • But a RestrictedFile is also a File, so f can also refer to a RestrictedFile object. File f = new RestrictedFile(“visa.dat”, 12345); • The type of the reference we use determines the point of view we will have on the object. 12- 70 RestrictedFile point of view • If we refer to a RestrictedFile object using a RestrictedFile reference we have the RestrictedFile point of view – we see all the methods that are defined in RestrictedFile and up the hierarchy tree. RestrictedFile f = new RestrictedFile(“visa.dat”, 12345); f.close(); f.lock(); f.unlock(12345); String s = f.toString(); 12- 71 File point of view • If we refer to a RestrictedFile object using a File reference we have the File point of view – which lets us use only methods that are defined in class File and up the hierarchy tree. File f = new RestrictedFile(“visa.dat”, 12345); f.close(); f.lock(); //Can’t use this method f.unlock(12345); //Can’t use this method String s = f.toString(); 12- 72 Object point of view • If we refer to a RestrictedFile object using an Object reference we have the Object point of view – which let us see only methods that are defined in class Object. Object f = new RestrictedFile(“visa.dat”, 12345); f.close(); //Can’t use this method f.lock(); //Can’t use this method f.unlock(12345); //Can’t use this method String s = f.toString(); 12- 73 Points of View RestrictedFile ... isOpen isLocked key toString() ... isOpen() open() close() lock() unlock(key) isLocked() 12- 74 Compile time-type vs. run-time type • A variable of a reference type has a declared type that is known at compile time and never changes. File f; • A reference variable may hold values of any subclass of its declared type f = new RestrictedFile(“visa.dat”,12345); • The type of the values held may change during the running of the algorithm and is not known during compile time f = new File(“visa.dat”); • The run-time type is always some subclass of the compile-time type. 12- 75 Widening • Changing our point of view of an object, to a wider one (a less specific one) is called widening. File file; file = new RestrictedFile(“visa.dat”, 1234); File reference File point of view RestrictedFile reference RestrictedFile point of view Widening 12- 76 Point – distanceFrom /** * A point on a grid. */ public class Point { /** * Computes the distance from another point * @param p The given point. */ public double distanceFrom(Point p) { int dx = x-p.x; int dy = y-p.y; return Math.sqrt(dx*dx+dy*dy); } // ... more methods } 12- 77 Pixel /** * Represents a pixel on a graphical area. */ public class Pixel extends Point { // The color of the pixel private Color color; /** * Constructs a new pixel. * @param x,y The coordinates of the pixel. * @param color The color of the pixel. */ public Pixel(int x, int y, Color color) { super(x,y); this.color = color; } // ... more methods } 12- 78 Widening parameters • In the following example, the method distanceFrom() expects a ‘reference to Point’ and gets ‘a reference to Pixel’, we are thus widening our point of view of the Pixel object. Point p1; Pixel p2; p1 = new Point(2, 3); p2 = new Pixel(5, 6, Color.red); double d = p1.distanceFrom(p2); 12- 79 Compile-time vs. run-time: method invocation • When we invoke a method on an object we always do it through a reference • The implementation of the method which is most specific will be chosen. • Java methods are virtual, i.e. the method which will be invoked is determined by the run-time type of object and not on the compile-time type of reference. • The identity of the invoked method is determined at runtime. • There are languages that use different mechanisms for method invocation. 12- 80 Type of Method is Determined at Runtime File file; if (Math.random() >= 0.5) file = new File(“visa.dat”); else file = new RestrictedFile(“visa.dat”, 76543); file.open(); Will the file be opened if the number randomly generated is less than 0.5? 12- 81 Another Example: Mouse in a Maze public class Mouse { private instance variables public Point tellLocation( ) { … } public int tellDirection( ) { … } public Mouse(Maze m) { … } public void makeMove( ) { … } private boolean outside ( ) { … } private boolean facingWall( ) { … } private void stepForward( ) { … } private void turnLeft( ) { … } private void turnRight ( ) { … } } 12- 82 Specialization through Inheritance • So now we want to create two kinds of mice; only makeMove( ) will change • RightMouse will have one strategy (right paw on wall) • StraightMouse will have another strategy (go straight, turn right when can't go straight) • No need to duplicate code; just make Mouse be a superclass, and RightMouse and StraightMouse only need to provide their own makeMove( ) code 12- 83 abstract • We'll let the user specify which kind of mouse he wants at runtime • The Mouse class will have all the methods and instance variables, except makeMove( ) • The Mouse class isn't intended to be used directly; we expect it to be subclassed • To enforce this, we explicitly specify in Mouse that makeMove( ) is supposed to be implemented in a subclass 12- 84 abstract public abstract void makeMove( ); • This “prototype” is included in the Mouse class • "abstract" tells Java that makeMove( ) is expected to be defined in a subclass • Any class that includes an abstract method is itself abstract, and has to be declared abstract itself • Such an abstract class cannot be instantiated by a client, you just have to subclass it and define its abstract methods • So the Mouse class will be abstract 12- 85 abstract Class, subclassed abstract class Mouse protected boolean _started; protected int _direction; etc. public abstract void makeMove( ); etc. protected boolean facingWall( ) { return theMaze.checkWall (_direction, location); } extends extends RightMouse StraightMouse public RightMouse(…) {…} public void makeMove( ) {…} public StraightMouse(…) {…} public void makeMove( ) {…} 12- 86 The new class Mouse abstract class Mouse { public final int NORTH=0, EAST=1, SOUTH=2, WEST=3; protected Maze _theMaze; protected boolean _started = false; //true once the maze is entered protected Point _location; //location of this mouse protected int _direction; //direction mouse is facing public Point tellLocation( ) { return _location; } public int tellDirection( ) { return _direction; } 12- 87 public Mouse (Maze m) { // Where do I start? _location = m.getStartLocation( ); // In what direction do I face initially? _direction = m.getStartDirection( ); _theMaze = m; } public abstract void makeMove( ); protected boolean outside ( ) { // Am I outside the maze? return _theMaze.outside(_location); } protected boolean facingWall ( ) { return _theMaze.checkWall(_direction, _location); } protected void stepForward( ) { switch (direction) { case NORTH: _location.y--; break; case EAST: _location.x++; break; case SOUTH: _location.y++; break; case WEST: _location.x--; break; } } protected void turnLeft( ) { _direction = (_direction + 3) % 4; } protected void turnRight( ) { _direction = (_direction + 1) % 4; } } Definition of class RightMouse class RightMouse extends Mouse { public RightMouse (Maze aMaze ) { super(aMaze); } public void makeMove( ) { if (_started) { if ( !outside( ) ) { turnRight( ); while ( facingWall( ) ) { turnLeft( ); } stepForward( ); } } else { stepForward( ); _started = true; } } } 12- 90 Definition of class StraightMouse class StraightMouse extends Mouse { public StraightMouse (Maze aMaze ) { super(aMaze); } public void makeMove( ) { if (_started) { if ( !outside( ) ) { if ( facingWall( ) ) { turnRight( ); makeMove( ); } else stepForward( ); } } else { stepForward( ); _started = true; } } } 12- 91 Dynamic Generation of the Kind of Mouse • Now, the main( ) method lets the user dynamically specify whether the mouse will be a RightMouse object or a StraightMouse object • The variable referencing the mouse will be of class "Mouse", but the actual generation of the object (either type) occurs at runtime 12- 92 Runtime Selection public class MouseMaze { public static void main (String[ ] args) { Maze theMaze = new Maze( ); Mouse speedy = selectMouse(theMaze); … } private static Mouse selectMouse(Maze theMaze) { SimpleInput sinp = new SimpleInput(System.in); while (true) { System.out.print("Choose RightMouse (0) " + "or StraightMouse (1): "); int i = sinp.readInt( ); if ( i == 0 ) return new RightMouse(theMaze); if ( i == 1 ) return new StraightMouse(theMaze); } } } 12- 93 Class Inheritance plus Dynamic Method Binding • speedy is a Mouse, but it might be a RightMouse or a StraightMouse • When speedy is sent the makeMove( ) message, it will do whatever is appropriate for the kind of mouse it is • The combination of Class Inheritance plus Dynamic Method binding is very powerful, allowing reuse of code, but flexible response to messages 12- 94 Interfaces • Java has a way of specifying "classes" that contain nothing but declarations of abstract methods • These provide no code, just a list of methods that every subclass has to define • It allows clients to define a method having formal parameters of the abstract "class", but whose actual parameters can belong to any class in its hierarchy 12- 95 Interface Declaration • This is so common that Java provides a special way of specifying it: interface declarations • Interface declarations are like abstract classes, but are restricted to containing abstract method declarations and symbolic constant definitions: interface interface_name { definitions of symbolic constants, and declarations of abstract methods } • Like classes, interfaces are placed in separate files, with the .java extension 12- 96 Syntax Differences • Classes that contain real definitions of these abstract methods write "implements interface_name" instead of "extends…" • All the methods in the interface are abstract, so you don't write "abstract" in front of them • All symbolic constants are assumed to be public, static, and final, so don't write those keywords, either 12- 97 More Importantly! • A class can implement more than one interface, while it can only be a subclass of one class; this allows us to use interfaces more flexibly • We might have interfaces I1 and I2, with methods that class C defines; even if C is a subclass of B, we could still write: class C extends B implements I1, I2 { … } 12- 98 One Use for Interfaces • Sometimes interfaces are used to give definitions of symbolic constants to be used in several classes public interface Direction { int NORTH = 0, EAST = 1, … } • Then several other classes implement this interface: public class Maze implements Direction { … } 12- 99 Another use for interfaces • Define a plotting function that accepts a function as one of its arguments: void plot (double x0, double x1, double delta, Function f) { // plot f on values x0, x0+delta, …, x1 … } • We can't pass a function as an argument • But we can define different objects that respond to the message "apply( )", and each different kind of object contains a function 12- 100 interface Imposing a Requirement on Classes interface Function double apply(double x); implements SineFunction … double apply(double x ) {…} … interface implements EmpiricalFunction … double apply(double x ) {…} … 12- 101 The Role of Interfaces • Interfaces play a role like the abstract classes we saw in previous slides • Interfaces are data types in Java, just like classes are • When a class implements an interface, instances of the class can be assigned to variables of the interface type 12- 102 What it Looks Like (these are in different files) void plot (double x0, double x1, double delta, Function f) { // plot f on values x0, x0+delta, …, x1 … } interface Function{ double apply(double x); } class SineFunction implements Function { double apply(double x) { return Math.sin(x); } } class EmpiricalFunction implements Function { double apply(double x) { … } } 12- 103 Calling them • Then these are legal calls (assuming appropriate constructors for SineFunction and EmpiricalFunction): plot(0.0, 10.0, 0.1, new SineFunction( )); and plot(0.0, 10.0, 0.1, new EmpiricalFunction(60, 0.0, 59.0)); 12- 104 The Point • The point is, we can use objects of type SineFunction and of type EmpiricalFunction in the same context (e.g., as an argument of the plot( ) method) • They'll both implement the interface Function; they both provide a definition of apply( ) • But they may not belong in the same class hierarchy, logically, and we need not require that they be in the same hierarchy 12- 105 Interfaces can also Inherit • Interfaces can also be organized conveniently into inheritance hierarchies interface I extends J … • I actually consists of all the methods and constants in I itself together with those in J • A class implementing I has to implement all those methods and constants 12- 106 Exception Handling • Java provides us with a relatively clean way of catching run-time errors — exceptions • An exception is an object • An exception is a signal that some unusual situation (like an error) has occurred • When "something goes wrong", an exception object is generated and passed back in a special way 12- 107 Exceptions in Java • Java actually uses the notion of exception for 3 related (but different) purposes: – Errors: an internal Java implementation error was discovered » E.g: out of memory – Runtime exceptions: a programming logic error was discovered » E.g. division by 0 – Checked Exceptions: an exceptional case was discovered » E.g. file not found 12- 108 Exceptions in Java • Errors and Runtime exceptions will usually cause the program to crash • Checked exceptions should usually be handled by the programmer 12- 109 Occurrence of a runtime exception public class ExceptionExample { public static void main(String[] args) { int[] a = {2, 4, 6, 8}; for(int j = 0; j <= a.length ; j++) System.out.println(a[j]); } } 12- 110 Program Crash due to a runtime exception 12- 111 Runtime exceptions in the Java API • • • • • java.lang.ArithmeticException java.lang.NullPointerException java.lang.IllegalArgumentException java.lang.NegativeArraySizeException java.lang.ArrayIndexOutOfBoundsExcepti on • java.lang.ClassCastException 12- 112 try, catch, finally • The try clause establishes a block of code that might have exceptions or abnormal exits • The try block is followed by zero or more catch clauses that specify code to handle various types of exceptions • the finally clause specifies code that will always be performed, if any part of the try block is executed (good for cleanup, closing files, etc.) 12- 113 try { // Normally this code runs from top of block to bottom // without problems. But it sometimes may raise // exceptions or exit the block via a break, continue, or // return statement } catch (SomeException e1) { // Handle an exception object e1 of type SomeException // or of a subclass of that type } catch (AnotherException e2) { // Handle an exception object e2 of type AnotherException // or of a subclass of that type } finally { // Always execute this code, after we leave the try clause, // regardless of whether we leave it, normally, with an // exception that is caught or not caught, or because of a // break, continue, or return statement } How are throws caught? Recall the runtime stack, and method calls main (String[ ] args) { ... ... ... obj.e(); ... } top void e ( ) { ... ... obj.f( ); ... } void f ( ) { System.out.println(“Hi there!”); System.out.println(“Nice weather!”); ... obj.g( ); System.out.println(“That was fun!”); System.out.println(“Time to move on!”); } void g ( ) { System.out.println(“A!”); System.out.println(“B!”); … System.out.println(“C!”); System.out.println(“D!”); } Method g( ) Where we came from: f, line 4 Method f( ) Where we came from: e, line 3 Method e( ) Where we came from: main, line 4 Method main( ) STACK 12- 115 throws are handled up the calling hierarchy • If an exception object were generated in g( ) (like an array index was out of bound), the system would first look if it is caught in g( ) • If it's not caught there, the system sees if it is caught in f( ); then in e( ); finally in main( ) • If not caught at all, the exception object causes the Java interpreter to print an error message and a stack trace and exit main (String[ ] args) { ... ... ... obj.e(); ... } void e ( ) { ... ... obj.f( ); ... } void f ( ) { System.out.println(“Hi there!”); System.out.println(“Nice weather!”); ... obj.g( ); System.out.println(“That was fun!”); System.out.println(“Time to move on!”); } void g ( ) { System.out.println(“A!”); System.out.println(“B!”); … System.out.println(“C!”); System.out.println(“D!”); } 12- 116 Exception Objects • An exception in Java is an object that is an instance of some subclass of java.lang.Throwable • Throwable has two standard subclasses, java.lang.Error and java.lang.Exception. • Exceptions that are subclasses of Error are generally unrecoverable and need not be caught • Exceptions that are subclasses of Exception indicate conditions that may be caught and recovered from 12- 117 Exception Objects • Since exceptions are objects, they can contain data and define methods • The Throwable object (at the top of the exception hierarchy) includes a String message to describe the exception, and this is inherited by all its descendants; the message is extracted via the method getMessage( ) • A few descendants add their own data (e.g., java.io.InterruptedIOException adds the field public int bytesTransferred; to signify how much of the I/O was complete before the exception occurred) 12- 118 Exception life-cycle When a program performs an illegal operation the following happens: – The regular flow of the program stops – An exception object is created, which encapsulates the information about the problem that occurred – The method may try to catch and handle the exceptional situation – If the method ignores the exception the method execution ceases. – An exception then appears at the place in which the method was called – If the exception is not handled anywhere, the program crashes. 12- 119 Declaring Exceptions • Certain kinds of exceptions need to be declared in a method’s declaration, if they are not handled inside the method: public void open_file( ) throws IOException { // Statements that might generate an // uncaught java.io.IOException } • You only need to declare exceptions that are not subclasses of Error or of RuntimeException; these are just too common to require declaring • An example that does not need to be declared is ArrayIndexOutOfBoundsException 12- 120 Creating our own Exceptions • We can declare a new exception class, and put it in the hierarchy of the class Exception • We need two constructors: one with no argument, and one with a single String argument giving a description of the fault • Let's look at an existing example from the Java API, to understand how to define our own 12- 121 class FileNotFoundException package java.io; public class FileNotFoundException extends IOException { public FileNotFoundException( ) { super( ); } } public FileNotFoundException(String s) { super(s); } 12- 122 How would we use it? package java.io; public class FileInputStream extends InputStream { } public FileInputStream(String name) throws FileNotFoundException { … try { fd = new FileDescriptor( ); open(name); } catch (IOException e) { throw new FileNotFoundException(name); } } 12- 123 OK, so let's create our own • Declare a new class, GetOuttaHereException, and have it extend the Exception class • Provide it with two constructors, one that takes no arguments and one that takes a string argument (and both call super) • throw new GetOuttaHereException with or without a string argument 12- 124 GetOuttaHereException definition public class GetOuttaHereException extends Exception { public GetOuttaHereException( ) { super( ); } } public GetOuttaHereException(String s) { super(s); } 12- 125 Now, use the exception in a method, gateKeeper( ) void gateKeeper (Person candidate) throws GetOuttaHereException { … if ( isBum(candidate) ) throw new GetOuttaHereException("You're a bum!"); if ( isBroke(candidate) ) throw new GetOuttaHereException("You're broke!"); if ( doesNotKnowJava(candidate) ) throw new GetOuttaHereException("You don't know Java!"); … } 12- 126 We will catch the exception in the calling method, joinClub void joinClub (Person candidate) { try { gateKeeper(candidate); } catch (GetOuttaHereException e) { System.out.println("No, you can't join the club because " + e.getMessage( )); return; } System.out.println("Welcome to the club!"); } 12- 127 Exceptions Hierarchy • All the classes for indicating run-time errors are derived from the class java.lang.Throwable. • The object you deliver to the throw statement must be an instance of class Throwable • The constructor of class Throwable initializes all the information about the location where the exception occurred, the state of the run-time stack etc. In this way this information is set for every exception object. • The following diagram explains the inheritance hierarchy for exceptions. 12- 128 Throwable class hierarchy Throwable Error Exception RuntimeException 12- 129 Input / Output • A program often needs to communicate with other devices. In other words it should receive input and send output. • There are many types of input sources: – Reading a file from a local disk / diskette – Receiving a web page from a remote server – Receiving a communication message through a network. Receiving a signal from a sensor of a robot – Scanner, video camera, ... – Mouse, keyboard, joystick, ... 12- 130 Input / Output • Similarly, there are many types of output destinations: – Writing to a file on a local disk / diskette – Sending query information to a remote web server – Sending communication message to a remote host. Sending a command to a robot controller. – Printing a document to a printer / fax – Displaying graphics on the screen – ... 12- 131 GUI inputs and outputs • GUI related inputs and outputs are usually treated separately. They are given special API • GUI inputs and outputs include receiving mouse, keyboard and similar events, and displaying graphics on the screen 12- 132 IO API - design goal • We want to make a distinction between the content of the data an application receives/sends and the source/destination of the data • The same kind of data can be stored on different types of media • Similarly a given media can store different types of data 12- 133 Scenario • Suppose we have an image processing application. It can read images, manipulate them and store them on a permanent storage. • We want our application to be able to read images from different types of sources: – local image files, remote images from the web, receiving an image from a scanner, ... • We want to be able to output the image to various types of destinations: – save the image to a local file, print the image on a printer, send the image to a fax recipient, ... 12- 134 Scenario Application 12- 135 IO Streams • We can achieve the separation by designing a common interface for reading any kind of data, and common interface for writing any kind of data. • This interface is implemented by the notion of input and output streams. • Any input can be represented as a sequence of bits. For convenience we divide the sequence into a sequence of bytes. • Similarly any output can be represented as a growing sequence of bytes. 12- 136 IO Streams 12 72 32 17 83 11 7 91 108 Input stream reading direction 43 55 writing direction 31 37 34 13 17 1 15 Output stream 12- 137 Input streams • An input stream is a sequence of bytes that is attached to some input source. • You can read data from the stream in a sequential order. One byte at a time or several bytes at a time. • Input streams are represented by the abstract class java.io.InputStream. • Subclasses of InputStream defines input streams that are related to various data sources • Class InputStream gives a common interface for receiving data from various types of data sources 12- 138 Specific input streams InputStream ... FileInputStream PipedInputStream ByteArrayInputStream 12- 139 Class InputStream • Class java.io.InputStream defines several methods that support the abstraction of allowing sequential reading from a stream: public abstract int read() throws IOException Reads the next byte from the stream. Return -1 if the end of the stream was reached. public int read(byte[] b) throws IOException Reads up to b.length bytes from the stream into the array b. Returns the number of bytes that were read. 12- 140 Input streams public int read(byte[] b, int offset, int length) throws IOException Reads up to length bytes from the stream into the array ‘b’ from the index ‘offset’. Returns the number of bytes that were read. public void close() throws IOException Closes this input stream and releases any system resources associated with the stream. • Few additional methods (look up in the API) 12- 141 Output streams • An output stream is attached to an output destination to which you can write data. • You can write data to the stream in a sequential order. One byte at a time or several bytes at a time. • Output streams are represented by the abstract class java.io.OutputStream. • Subclasses of OutputStream defines output streams that are related to various data destinations • Class OutputStream gives a common interface for sending data to various types of data destinations 12- 142 Specific output streams OutputStream ... FileOutputStream PipedOutputStream ByteArrayOutputStream 12- 143 Class OutputStream • Class java.io.OutputStream defines several methods that support the abstraction of allowing sequential writing to a stream: public abstract void write(int b) throws IOException Writes the specified byte (given as an int) to this output stream. public void write(byte[] b) throws IOException Writes b.length bytes from the specified byte array to this output stream. 12- 144 Input streams public void write(byte[] b, int offset, int length) throws IOException Writes length bytes from the specified byte array starting at offset off to this output stream. public void close() throws IOException Closes this output stream and releases any system resources associated with the stream. • Few additional methods (look up in the API) 12- 145 Reading/Writing from/to files • java.io.FileInputStream is a subclass of InputStream that let you read a file (viewed as a sequence of bytes) • java.io.FileOutputStream is a subclass of OutputStream that let you write data to a file (as a sequence of bytes) • Both classes have constructors that get the path of the file as a parameter 12- 146 Writing to a file import java.io.*; class GenerateDiceData { static final int NUMBER_OF_TOSSES = 100000; public static void main(String[] args) { try { OutputStream output = new FileOutputStream(“dice.dat”); for (long i=0; i<NUMBER_OF_TOSSES; i++) { int randomThrow = (int)(Math.random()*6)+1; output.write(randomThrow); } output.close(); } catch (IOException ioe) { System.err.println(“Couldn’t write to file”); } } } 12- 147 Reading from a file import java.io.*; public class Count6Occurrences { static final int LOOK_FOR = 6; public static void main(String[] args) { long count = 0; try { InputStream input = new FileInputStream(“dice.dat”); int result; while ((result = input.read()) != -1) if (result == LOOK_FOR) count++; input.close(); System.out.println(count + “ occurrences”); } catch (IOException ioe) { System.err.println(“Couldn’t read from file”); } }} 12- 148 Downloading a file from the web page import java.io.*; import java.net.URL; // This program downloads a file from a given url // and saves it to the local file // Usage: java Download <url> <filename> public class Download { public static void main(String[] args) { try { download(args[0], args[1]); } catch (ArrayIndexOutOfBoundsException aioobe) { System.err.println(“Wrong usage.”); } catch (IOException ioe) { System.err.println(“Download failed”); } } 12- 149 Downloading a file from the web (cont.) // Downloads a remote file to the local disk. // source - The url of the remote file // filename - The name of the target file. private static void download(String source, String filename) throws IOException { InputStream input =(new URL(source)).openStream(); OutputStream output=new FileOutputStream(filename); int b; while ((b=input.read())!=-1) { output.write(b); } output.close(); } } 12- 150 Textual vs. binary data • We often make a distinction between textual data and other kind of data • We refer to files that stores text as ‘text files’ and to other files as ‘binary files’. • Binary files stores their information in various formats. In order to understand the content of a binary file you need to have a viewer that knows how to read the format the file is written with. • The structure of text files is more simple. It uses an encoding that gives a numeric code for each symbol and the text is stored as a list of numbers. 12- 151 Java & Unicode • One of the important aspects of Java is its platform independence • Therefore Java uses Unicode • However, many environments don’t support Unicode yet but only use ASCII. • Unicode uses two bytes per character while ASCII uses one byte • Java IO library overcomes this problem using Readers and Writers that translate between internal Unicode representation and external ASCII representation (with local extensions). 12- 152 Writers Writer writer = new FileWriter(“mail.txt”); writer.write(‘a’); writer.write(‘\u0590’); // Hebrew Aleph 97 1424 Automatic platform dependent translation made by the writer standard ASCII no conversion needed 97 97 224 224 conversion to the platform specific code for aleph 12- 153 Readers Reader reader = new FileReader(“mail.txt”); char c = reader.read(); // c = ‘\u0590’ c = reader.read(); // c = ‘a’ 97 1424 Automatic platform dependent translation made by the reader standard ASCII no conversion needed 97 97 224 224 conversion from the platform specific code for aleph 12- 154 Readers & Writers • java.io.Reader is an abstract class that defines a common interface for reading textual data • It is the counterpart of InputStream • You can read from a reader characters in a sequential manner. One character at a time, or several characters at a time. • Similarly, java.io.Writer is an abstract class that defines a common interface for reading textual data. • It is the counterpart of OutputStream 12- 155 Specific readers Reader ... FileReader CharArrayReader PipedReader StringReader 12- 156 Specific writers Writer ... FileWriter CharArrayWriter PipedWriter StringWriter 12- 157 java.io.Reader public abstract int read() throws IOException Read a single character. Returns the character as an int or -1 if the end of the stream was reached. public int read(char[] buffer) throws IOException Reads up to buffer.length characters into ‘buffer’, returns the number of characters read. public void close() throws IOException Closes the reader. • Few additional methods (look up in the API) 12- 158 java.io.Writer public abstract void write(int c) throws IOException Writes a single character given as an int. public void write(char[] buffer) throws IOException Writes a given char array. public void close() throws IOException Closes the writer. • Few additional methods (look up in the API) 12- 159 ToUpper import java.io.*; // This class reads a text file and writes it into // another text file after converting all letters to // uppercase. // Usage: java ToUpper <source> <target> class ToUpper { public static void main(String[] args) { if (args.length!=2) { System.err.println(“Invalid usage.”); return; } String sourceName = args[0]; String targetName = args[1]; 12- 160 ToUpper (cont.) try { Reader reader = new FileReader(sourceName); Writer writer = new FileWriter(targetName); int c; while ((c=reader.read())!=-1) { c = Character.toUpperCase((char)c); writer.write(c); } write.close(); //very important !!! } catch (IOException ioe) { System.err.println(“Copying failed.”); } } } 12- 161 Reading “non-primitive” data • The data we want to read/write usually has more complex structure: primitive data types other than char or short, lines, or even more complex: tables, images, compressed/encrypted data... • Basic solution: Extend existing input or output streams – Provide methods for handling the non-primitive data 12- 162 Reading a Short public class ShortInputStream extends SomeInputStream{ // ... public short readShort() throws EOFException { int hi,low; if ((hi = this.read()) == -1) throw new EOFException(); if ((low = this.read()) == -1) throw new EOFException(); return (short)(hi << 8 | low ); } } 12- 163 Reading a Line public class LineReader extends SomeReader { // ... public String readLine() throws EOFException { StringBuffer line = new StringBuffer();int c; while ((c = this.read())!=-1) { if (c!=‘\n’) line.append((char)c); else return line.toString(); } if (line.equals(“”)) throw new EOFException(); } } 12- 164 Design Problem • There are many enhancements for reading/writing data – Reading complex types of data (lines, objects, ints) – Buffering – “pushing back” something that was already read • There are many types of input/output streams • If we would include all enhancements in all types of streams we will end up with a lot of duplicated code and it would be hard to add new enhancements or new types of streams. • We usually don’t need all combinations at once 12- 165 Solution - Decorator Pattern • Use a “decorator”: a class that is derived from Reader, and has another Reader object as a member (received by the constructor of the new class). • All Reader methods are “forwarded” to the inner Reader object. • New attributes (methods) use the inner Reader object as well. • We gain two things: The “old” interface is preserved, and we can “chain” several functionalities. • Same solution concept for Writer, InputStream and OutputStream. • In Java, “decorators” are called “Filters”, and the base class for adding attributes is FilterXXX. 12- 166 Example: BufferedReader BufferedReader BufferedReader (Reader r) readLine() read() ... ... Reader read() ... ... 12- 167 Example: DataInputStream DataInputStream readShort() read() ... ... InputStream read() ... ... 12- 168 Printing a text file public class type { public static void main(String[] args) { try { BufferedReader reader = new BufferedReader(new FileReader(args[0])); String line; while ((line=reader.readLine())!=null) System.out.println(line); } catch (IOException ioe) { System.err.println(“Reading failed.”); } } } 12- 169 Filters in java.io.* • DataInputStream & DataOutputStream – Read and write all primitive Java data types • ObjectInputStream & ObjectOutputStream – Read and write full objects – Objects must be “Serializable” • BufferedReader/Writer/InputStream/OutputStream – Provide buffering – BufferedReader/Writer allow also reading complete lines • LineNumberReader & LineNumberInputStream – Allow access to input line numbers 12- 170 Example: Chaining Decorators try { DataInputStream input = new DataInputStream( new BufferedInputStream( new FileInputStream(args[0]))); } catch (FileNotFoundException fnfe) { // ... } read() Data read() Buffered read() File readShort() 12- 171 InputStreamReader • InputStreamReader bridges InputStream and Reader classes: URL url = new URL(“...“); BufferedReader reader = new BufferedReader( new InputStreamReader(url.openStream())); String line; while ((line = reader.readLine())!=null) { System.out.println(line); } 12- 172