} // Overload test for two integer parameters. void test(int a, int b) { System.out.println(\"a and b: \" + a + \" \" + b); } // overload test for a double parameter void test(double a) { System.out.println(\"Inside test(double) a: \" + a); } } class Overload { public static void main(String args[]) { OverloadDemo ob = new OverloadDemo(); int i = 88; ob.test(); ob.test(10, 20); ob.test(i); // this will invoke test(double) ob.test(123.2); // this will invoke test(double) } } This program generates the following output: No parameters a and b: 10 20 Inside test(double) a: 88 Inside test(double) a: 123.2 As you can see, this version of OverloadDemo does not define test. Therefore, when test is called with an integer argument inside Overload, no matching method is found. However, Java can automatically convert an integer into a double, and this conversion can be used to resolve the call. Therefore, after test is not found, Java elevates i to double and then calls test. Of course, if test had been defined, it would have been called instead. Java will employ its automatic type conversions only if no exact match is found. CU IDOL SELF LEARNING MATERIAL (SLM) 101
Method overloading supports polymorphism because it is one way that Java implements the “one interface, multiple methods” paradigm. To understand how, consider the following. In languages that do not support method overloading, each method must be given a unique name. However, frequently you will want to implement essentially the same method for different types of data. Consider the absolute value function. In languages that do not support overloading, there are usually three or more versions of this function, each with a slightly different name. For instance, in C, the function absreturns the absolute value of an integer, labs returns the absolute value of a long integer, and FABS returns the absolute value of a floating-point value. Since C does not support overloading, each function has to have its own name, even though all three functions do essentially the same thing. This makes the situation more complex, conceptually, than it actually is. Although the underlying concept of each function is the same, you still have three names to remember. This situation does not occur in Java, because each absolute value method can use the same name. Indeed, Java’s standard class library includes an absolute value method, called abs. This method is overloaded by Java’s Math class to handle all numeric types. Java determines which version of absto call based upon the type of argument. The value of overloading is that it allows related methods to be accessed by use of a common name. Thus, the name abs represents the general action which is being performed. It is left to the compiler to choose the right specific version for a particular circumstance. You, the programmer, need only remember the general operation being performed. Through the application of polymorphism, several names have been reduced to one. Although this example is fairly simple, if you expand the concept, you can see how overloading can help you manage greater complexity. When you overload a method, each version of that method can perform any activity you desire. There is no rule stating that overloaded methods must relate to one another. However, from a stylistic point of view, method overloading implies a relationship. Thus, while you can use the same name to overload unrelated methods, you should not. For example, you could use the name SQR to create methods that return the square of an integer and the square root of a floating-point value. But these two operations are fundamentally different. Applying method overloading in this manner defeats its original purpose. In practice, you should only overload closely related operations. 3.7 GARBAGE COLLECTION Since objects are dynamically allocated by using the new operator, you might be wondering how such objects are destroyed and their memory released for later reallocation. In some languages, such as C++, dynamically allocated objects must be manually released by use of a delete operator. Java takes a different approach; it handles deallocation for you automatically. The technique that accomplishes this is called garbage collection. It works like this: when no references to an object exist, that object is assumed to be no longer needed, and the memory CU IDOL SELF LEARNING MATERIAL (SLM) 102
occupied by the object can be reclaimed. There is no explicit need to destroy objects as in C++. Garbage collection only occurs sporadically (if at all) during the execution of your program. It will not occur simply because one or more objects exist that are no longer used. Furthermore, different Java run-time implementations will take varying approaches to garbage collection, but for the most part, you should not have to think about it while writing your programs. 3.8 FINAL, FINALIZE AND FINALLY METHOD FINAL Final (lowercase) is a reserved keyword in java. We can’t use it as an identifier as it is reserved. We can use this keyword with variables, methods and also with classes. The final keyword in java has different meaning depending upon it is applied to variable, class or method. 1. Final with Variables: The value of variable cannot be changed once initialized. class A { public static void main(String[] args) { // Non final variable int a = 5; // final variable final int b = 6; // modifying the non final variable : Allowed a++; // modifying the final variable : // Immediately gives Compile Time error. b++; } } CU IDOL SELF LEARNING MATERIAL (SLM) 103
If we declare any variable as final, we can’t modify its contents since it is final, and if we modify it then we get Compile Time Error. 2. Final with Class: The class cannot be subclassed. Whenever we declare any class as final, it means that we can’t extend that class or that class can’t be extended or we can’t make subclass of that class. final class RR { public static void main(String[] args) { int a = 10; } } // here gets Compile time error that // we can't extend RR as it is final. class KK extends RR { // more code here with main method } 3. Final with Method: The method cannot be overridden by a subclass. Whenever we declare any method as final, then it means that we can’t override that method. class QQ { final void rr() {} public static void main(String[] args) { } } class MM extends QQ { // Here we get compile time error // since can't extend rr since it is final. void rr() {} CU IDOL SELF LEARNING MATERIAL (SLM) 104
} Note: If a class is declared as final then by default all of the methods present in that class are automatically final but variables are not. // Java program to illustrate final keyword final class G { // by default it is final. void h() {} // by default it is not final. static int j = 30; public static void main(String[] args) { // See modified contents of variable j. j = 36; System.out.println(j); } } Output: 36 FINALLY Just as final is a reserved keyword, so in same way finally is also a reserved keyword in java i.e., we can’t use it as an identifier. The finally keyword is used in association with a try/catch block and guarantees that a section of code will be executed, even if an exception is thrown. The finally block will be executed after the try and catch blocks, but before control transfers back to its origin. // A Java program to demonstrate finally. Class Geek { // A method that throws an exception and has finally. // This method will be called inside try-catch. static void A() { try { CU IDOL SELF LEARNING MATERIAL (SLM) 105
System.out.println(\"inside A\"); throw new RuntimeException(\"demo\"); } finally { System.out.println(\"A's finally\"); } } // This method also calls finally. This method // will be called outside try-catch. static void B() { try { System.out.println(\"inside B\"); return; } finally { System.out.println(\"B's finally\"); } } public static void main(String args[]) { try { A(); } catch (Exception e) { CU IDOL SELF LEARNING MATERIAL (SLM) 106
System.out.println(\"Exception caught\"); } B(); } } Output: inside A A's finally Exception caught inside B B's finally FINALIZE METHOD It is a method that the Garbage Collector always calls just before the deletion/destroying the object which is eligible for Garbage Collection, so as to perform clean-up activity. Clean-up activity means closing the resources associated with that object like Database Connection, Network Connection or we can say resource de-allocation. Remember it is not a reserved keyword. Once the finalize method completes immediately Garbage Collector destroy that object. Finalize method is present in Object class and its syntax is: Protected void finalize throws Throwable{} Since Object class contains the finalize method hence finalize method is available for every java class since Object is the superclass of all java classes. Since it is available for every java class hence Garbage Collector can call finalize method on any java object Now, the finalize method which is present in the Object class, has an empty implementation, in our class clean-up activities are there, then we have to override this method to define our own clean-up activities. Cases related to finalize method: class Hello { public static void main(String[] args) { String s = new String(\"RR\"); s = null; CU IDOL SELF LEARNING MATERIAL (SLM) 107
// Requesting JVM to call Garbage Collector method System.gc(); System.out.println(\"Main Completes\"); } // Here overriding finalize method public void finalize() { System.out.println(\"finalize method overriden\"); } } Output: Main Completes Note: Here above output came only Main Completes and not “finalize method overridden” because Garbage Collector calls finalize method on that class object which is eligible for Garbage collection. Here above we have done-> s = null and‘s’ is the object of String class, so String class finalize method is going to be called and not our class (i.e., Hello class). So we modify our code like-> Hello s = new Hello (); s = null; Now our class i.e., Hello class finalize method is called. Output: Finalize method overridden Main Completes So basically, Garbage Collector calls finalize method on that class object which is eligible for Garbage collection. So if String object is eligible for Garbage Collection then String class finalize method is going to be called and not the Hello class finalize method. 3.9 COMPARISON BETWEEN JAVA AND C++ There are many differences and similarities between the C++ programming language and Java. A list of top differences between C++ and Java are given below: CU IDOL SELF LEARNING MATERIAL (SLM) 108
Comparison Index C++ Java Platform-independent C++ is platform-dependent. Java is platform-independent. Mainly used for C++ is mainly used for system Java is mainly used for application programming. programming. It is widely used in Windows-based, web-based, enterprise, and mobile applications. Design Goal C++ was designed for systems Java was designed and created as an and applications programming. interpreter for printing systems but It was an extension of the C later extended as a support network programming language. computing. It was designed to be easy to use and accessible to a broader audience. Goto C++ supports the got Java doesn't support the goto statement. statement. Multiple inheritance C++ supports multiple Java doesn't support multiple inheritances. inheritances through class. It can be achieved by using interfaces in java. Operator Overloading C++ supports operator Java doesn't support operator overloading. overloading. Pointers C++ supports pointers. You Java supports pointer internally. can write a pointer program in However, you can't write the pointer C++. program in java. It means java has restricted pointer support in java. Compiler and Interpreter C++ uses compiler only. C++ Java uses both compiler and is compiled and run using the interpreter. Java source code is compiler which converts converted into bytecode at source code into machine code compilation time. The interpreter so, C++ is platform dependent. executes this bytecode at runtime and produces output. Java is interpreted that is why it is platform- independent. Call by Value and Call by C++ supports both calls by Java supports call by value only. CU IDOL SELF LEARNING MATERIAL (SLM) 109
reference value and call by reference. There is no call by reference in java. Structure and Union C++ supports structures and Java doesn't support structures and unions. unions. Thread Support C++ doesn't have built-in Java has built-in thread support. support for threads. It relies on third-party libraries for thread support. Documentation comment C++ doesn't support Java supports documentation documentation comments. comment (/** ... */) to create documentation for java source code. Virtual Keyword C++ supports virtual keyword Java has no virtual keyword. We can so that we can decide whether override all non-static methods by or not to override a function. default. In other words, non-static methods are virtual by default. unsigned right shift >>> C++ doesn't support >>> Java supports unsigned right shift operator. >>> operator that fills zero at the top for the negative numbers. For positive numbers, it works same like >> operator. Inheritance Tree C++ always creates a new Java always uses a single inheritance inheritance tree. tree because all classes are the child of the Object class in Java. The Object class is the root of the inheritance tree in java. Hardware C++ is nearer to hardware. Java is not so interactive with hardware. Object-oriented C++ is an object-oriented Java is also an object-oriented language. However, in the C language. However, everything language, a single root (except fundamental types) is an hierarchy is not possible. object in Java. It is a single root hierarchy as everything gets derived from java.lang.Object. Note CU IDOL SELF LEARNING MATERIAL (SLM) 110
Java doesn't support default arguments like C++. Java does not support header files like C++. Java uses the import keyword to include different classes and methods. C++ Program Example File: main.cpp #include <iostream> using namespace std; int main() { cout << \"Hello C++ Programming\"; return 0; } Output: Hello C++ Programming Java Program Example File: Simple.java class Simple{ public static void main(String args[]){ System.out.println(\"Hello Java\"); } } Output: Hello Java 3.10 SUMMARY Sometimes a method will need to refer to the object that invoked it. To allow this, Java defines the keyword. This can be used inside any method to refer to the current object. That is, this is always a reference to the object on which the method was invoked. In general, there are two ways that a computer language can pass an argument to a subroutine. The first way is call-by-value. This method copies the value of an argument into the formal parameter of the subroutine. Therefore, changes made to the parameter of the subroutine have no effect on the argument. The second way an CU IDOL SELF LEARNING MATERIAL (SLM) 111
argument can be passed is call-by-reference. In this method, a reference to an argument (not the value of the argument) is passed to the parameter. In Java it is possible to define two or more methods within the same class that share the same name, as long as their parameter declarations are different. Method overloading is one of the ways that Java implements polymorphism. If you have never used a language that allows the overloading of methods, then the concept may seem strange at first. But as you will see, method overloading is one of Java’s most exciting and useful features. Final(lowercase) is a reserved keyword in java. We can’t use it as an identifier as it is reserved. We can use this keyword with variables, methods and also with classes. The final keyword in java has different meaning depending upon it is applied to variable, class or method. The technique that accomplishes this is called garbage collection. It works like this: when no references to an object exist, that object is assumed to be no longer needed, and the memory occupied by the object can be reclaimed. There is no explicit need to destroy objects as in C++. Garbage collection only occurs sporadically (if at all) during the execution of your program. 3.11 KEYWORDS This: The keyword refers to the current object in a method or constructor. The most common use of the keyword is to eliminate the confusion between class attributes and parameters with the same return the current class object. Objects: It is a basic unit of Object-Oriented Programming and represents the real life entities. A typical Java program creates many objects, which as you know, interact by invoking methods. An object consists of : State: It is represented by attributes of an object. Method: A method is a block of code which only runs when it is called. You can pass data, known as parameters, into a method. Methods are used to perform certain actions, and they are also known as functions. Finalize: The finalize method of Object class is a method that the Garbage Collector always calls just before the deletion/destroying the object which is eligible for Garbage Collection, so as to perform clean-up activity. Finally: The finally block in java is used to put important codes such as clean up code e.g. closing the file or closing the connection. The finally block executes whether exception rise or not and whether exception handled or not. A finally contains all the crucial statements regardless of the exception occur or not. CU IDOL SELF LEARNING MATERIAL (SLM) 112
3.12 LEARNING ACTIVITY 1. Conduct a seminar on method overloading. ________________________________________________________________________ __________________________________________________________________ 2. Discuss the comparison between Java and C++. ________________________________________________________________________ __________________________________________________________________ 3.13 UNIT END QUESTIONS A. Descriptive Questions Short Questions: 1. What is this keyword? 2. What is argument passing? 3. What is returning objects? 4. What is method overloading? 5. What is garbage collection? Long Questions: 1. What is this keyword explain with example? 2. What is argument passing explain with example? 3. What is method overloading explain with example? 4. What are final, finalize, and finally method explain with example? 5. What are the difference between Java and C++? B. Multiple Choice Questions 1. What is the keyword 'this' is used as? a. As reference to current object b. Explicit constructor invocation c. In open recursion d. All the above 2. What is overloaded methods in java? CU IDOL SELF LEARNING MATERIAL (SLM) 113
a. Compiler uses method signature to determine which method to invoke. They may have different functionality ans b. They are not available in fundamental classes c. They have the same name and signature d. None of these 3. What isthe return types must be to successfully overload a method in Java,? a. Same b. Different c. Same but using superclass or subclass types also work d. None of these 4. What is final keyword in java is used with? a. Class b. Class fields c. Class methods d. All of these 5. Which keyword is used to refer current object of a class in Java? a. This b. New c. Current d. None Answers 1-d, 2-a, 3-c, 4-d, 5-a. 3.14 REFERENCES References Powell, Thomas A, and Schneider, Fritz, 2012. JavaScript: The Complete Reference, Crockford, Douglas, 2008. JavaScript: The Good Parts. Patrick Naughton, Herbert Schildt. Java 2: The Complete Reference, third edition. The McGraw-Hill Companies, 1999. CU IDOL SELF LEARNING MATERIAL (SLM) 114
Textbooks Goodman, Danny, with Morrison, Michael, 2004. JavaScript Bible, 5th Edition. Wiley Publishing, Inc., Indianapolis, USA. Gosling, James; Joy, Bill; Steele, Guy; Bracha, Gillad (2005). Java Language Specification 3rd ed. Powell, Thomas A, and Schneider, Fritz, 2012. JavaScript: The Complete Reference, Third Edition. Website https://www.mygreatlearning.com https://www.w3schools.com https://beginnersbook.com CU IDOL SELF LEARNING MATERIAL (SLM) 115
UNIT–4:WRAPPER CLASS 1 STRUCTURE 4.0 Learning Objectives 4.1 Introduction 4.4Inheritance 4.4.1 Inheritance Basics 4.5 Interface 4.5.1 Defining an Interface 4.5.2 Implementing Interfaces 4.5.3 Applying Interfaces 4.5.4 Variables in Interfaces 4.5.5 Interfaces Can Be Extended 4.6 Abstract Class 4.7 Packages 4.7.1 Defining a Package 4.7.2 Access Protection 4.7.2 Importing Packages 4.9Java I/O Classes and Interfaces 4.10 I/O Stream Classes 4.11 Input and Output Stream 4.12 Input Stream and Output Stream Hierarchy 4.13 Summary 4.14Keywords 4.15 Learning Activity 4.16 Unit End Questions 4.17 References 4.0 LEARNING OBJECTIVES After studying this unit, you will be able to: CU IDOL SELF LEARNING MATERIAL (SLM) 116
Explain the Inheritance and basic of inheritance Illustrate an interface and implementing interfaces Describe the applying interfaces and variables in Interfaces Explain Java I/O Classes and Interfaces 4.1 INTRODUCTION JAVA was developed by James Gosling at Sun Microsystems Inc in the year 1991, later acquired by Oracle Corporation. It is a simple programming language. Java makes writing, compiling, and debugging programming easy. It helps to create reusable code and modular programs. Java is a class-based, object-oriented programming language and is designed to have as few implementation dependencies as possible. A general-purpose programming language made for developers to write once run anywhere that is compiled Java code can run on all platforms that support Java. Java applications are compiled to byte code that can run on any Java Virtual Machine. The syntax of Java is similar to c/c++. History Java’s history is very interesting. It is a programming language created in 1991. James Gosling, Mike Sheridan, and Patrick Naughton, a team of Sun engineers known as the Green team initiated the Java language in 1991. Sun Microsystems released its first public implementation in 1996 as Java 1.0. It provides no-cost -run-times on popular platforms. Java1.0 compiler was re-written in Java by Arthur Van Hoff to strictly comply with its specifications. With the arrival of Java 2, new versions had multiple configurations built for different types of platforms. In 1997, Sun Microsystems approached the ISO standards body and later formalized Java, but it soon withdrew from the process. At one time, Sun made most of its Java implementations available without charge, despite their proprietary software status. Sun generated revenue from Java through the selling of licenses for specialized products such as the Java Enterprise System. On November 13, 2006, Sun released much of its Java virtual machine as free, open-source software. On May 8, 2007, Sun finished the process, making all of its JVM’s core code available under open-source distribution terms. The principles for creating java were simple, robust, secured, high performance, portable, multi-threaded, interpreted, dynamic, etc. James Gosling in 1995 developed Java, who is known as the Father of Java. Currently, Java is used in mobile devices, internet programming, games, e-business, etc. CU IDOL SELF LEARNING MATERIAL (SLM) 117
4.2 INHERITANCE Inheritance is one of the cornerstones of object-oriented programming because it allows the creation of hierarchical classifications. Using inheritance, you can create a general class that defines traits common to a set of related items. This class can then be inherited by other, more specific classes, each adding those things that are unique to it. In the terminology of Java, a class that is inherited is called a superclass. The class that does the inheriting is called a subclass. Therefore, a subclass is a specialized version of a superclass. It inherits all of the instance variables and methods defined by the super class and add its own, unique elements. 4.2.1 Inheritance Basics To inherit a class, you simply incorporate the definition of one class into another by using the keyword. To see how, let’s begin with a short example. The following program creates a superclass called A and a subclass called B. Notice how the keyword extends is used to create a subclass of A. // A simple example of inheritance. // Create a superclass. class A { int i, j; void showij() { System.out.println(\"i and j: \" + i + \" \" + j); } } // Create a subclass by extending class A. class B extends A { int k; void showk() { System.out.println(\"k: \" + k); } void sum() { System.out.println(\"i+j+k: \" + (i+j+k)); } } CU IDOL SELF LEARNING MATERIAL (SLM) 118
class SimpleInheritance { public static void main(String args[]) { A superOb = new A(); B subOb = new B(); // The superclass may be used by itself. superOb.i = 10; superOb.j = 20; System.out.println(\"Contents of superOb: \"); superOb.showij(); System.out.println(); /* The subclass has access to all public members of its superclass. */ subOb.i = 7; subOb.j = 8; subOb.k = 9; System.out.println(\"Contents of subOb: \"); subOb.showij(); subOb.showk(); System.out.println(); System.out.println(\"Sum of i, j and k in subOb:\"); subOb.sum(); } } The output from this program is shown here: Contents of superOb: i and j: 10 20 Contents of subOb: i and j: 7 8 CU IDOL SELF LEARNING MATERIAL (SLM) 119
k: 9 Sum of i, j and k in subOb: i+j+k: 24 As you can see, the subclass B includes all of the members of its super class, A. This is why subOb can access i and j and call showij( ). Also, inside sum( ), i and j can be referred to directly, as if they were part of B. Even though A is a superclass for B, it is also a completely independent, stand-alone class. Being a super class for a subclass does not mean that the super class cannot be used by itself. Further, a subclass can be a superclass for another subclass. The general form of a class declaration that inherits a superclass is shown here: class subclass-name extends superclass-name { // body of class } You can only specify one superclass for any subclass that you create. Java does not support the inheritance of multiple superclasses into a single subclass. You can, as stated, create a hierarchy of inheritance in which a subclass becomes a superclass of another subclass. However, no class can be a superclass of itself. 4.3 INTERFACE Using the keyword interface, you can fully abstract a class’ interface from its implementation. That is, using interface, you can specify what a class must do, but not howit does it. Interfaces are syntactically similar to classes, but they lack instance variables, and their methods are declared without any body. In practice, this means that you can define interfaces which don’t make assumptions about how they are implemented. Once it is defined, any number of classes can implement an interface. Also, one class can implement any number of interfaces. To implement an interface, a class must create the complete set of methods defined by the interface. However, each class is free to determine the details of its own implementation. By providing the interface keyword, Java allows you to fully utilize the “one interface, multiple methods” aspect of polymorphism. Interfaces are designed to support dynamic method resolution at run time. Normally, in order for a method to be called from one class to another, both classesneed to be present at compile time so the Java compiler can check to ensure that themethod signatures are compatible. This requirement by itself makes for a static andnon- extensible classing environment. Inevitably in a system like this, functionality getspushed up higher and higher in the class hierarchy so that the mechanisms will beavailable to more and more subclasses. Interfaces are designed to avoid this problem.They disconnect the definition CU IDOL SELF LEARNING MATERIAL (SLM) 120
of a method or set of methods from the inheritance hierarchy. Since interfaces are in a different hierarchy from classes, it is possible forclasses that are unrelated in terms of the class hierarchy to implement the sameinterface. This is where the real power of interfaces is realized. NOTE: Interfaces add most of the functionality that is required for many applications which would normally resort to using multiple inheritances in a language such as C++. 4.3.1 Defining an Interface An interface is defined much like a class. This is the general form of an interface: access interface name { return-type method-name1(parameter-list); return-type method-name2(parameter-list); type final-varname1 = value; type final-varname2 = value; // ... return-type method-nameN(parameter-list); type final-varnameN = value; } Here, access is either public or not used. When no access specified is included, then default access results, and the interface is only available to other members of the package in which it is declared. When it is declared as public, the interface can be used by any other code. name is the name of the interface, and can be any valid identifier. Notice that the methods which are declared have no bodies. They end with a semicolon after the parameter list. They are, essentially, abstract methods; there can be no default implementation of any method specified within an interface. Each class that includes an interface must implement all of the methods. Variables can be declared inside of interface declarations. They are implicitly final and static, meaning they cannot be changed by the implementing class. They must also be initialized with a constant value. All methods and variables are implicitly public if the interface, itself, is declared as public. Here is an example of an interface definition. It declares a simple interface which contains one method called callback( ) that takes a single integer parameter. interface Callback { void callback(int param); } CU IDOL SELF LEARNING MATERIAL (SLM) 121
4.3.2 Implementing Interfaces Once an interface has been defined, one or more classes can implement that interface. To implement an interface, include the implements clause in a class definition, and then create the methods defined by the interface. The general form of a class that includes the implements clause looks like this: Access class classname [extends superclass] [implements interface [,interface...]] { // class-body } Here, access is either public or not used. If a class implements more than one interface, the interfaces are separated with a comma. If a class implements two interfaces that declare the same method, then the same method will be used by clients of either interface. The methods that implement an interface must be declared public. Also, the type signature of the implementing method must match exactly the type signature specified in the interface definition. Here is a small example class that implements the Callback interface shown earlier. class Client implements Callback { // Implement Callback's interface public void callback(int p) { System.out.println(\"callback called with \" + p); } } Notice that callback( ) is declared using the public access specifier. It is both permissible and common for classes that implement interfaces to define additional members of their own. For example, the following version of Client implements callback( ) and adds the method nonIfaceMeth( ): class Client implements Callback { // Implement Callback's interface public void callback(int p) { System.out.println(\"callback called with \" + p); CU IDOL SELF LEARNING MATERIAL (SLM) 122
} void nonIfaceMeth() { System.out.println(\"Classes that implement interfaces \" + \"may also define other members, too.\"); } } 4.3.3 Applying Interfaces There are many ways to implement a stack. For example, the stack can be of a fixed size or it can be “grow able.” The stack can also be held in an array, a linked list, a binary tree, and so on. No matter how the stack is implemented, the interface to the stack remains the same. That is, the methods push( ) and pop( ) define the interface to the stack independently of the details of the implementation. Because the interface to a stack is separate from its implementation, it is easy to define a stack interface, leaving it to each implementation to define the specifics. Let’s look at two examples. First, here is the interface that defines an integer stack. Put this in a file called IntStack.java. This interface will be used by both stack implementations. // Define an integer stack interface. interface IntStack { void push(int item); // store an item int pop(); // retrieve an item } The following program creates a class called FixedStack that implements a fixed-length version of an integer stack: // An implementation of IntStack that uses fixed storage. class FixedStack implements IntStack { private int stck[]; private int tos; // allocate and initialize stack FixedStack(int size) { CU IDOL SELF LEARNING MATERIAL (SLM) 123
stck = new int[size]; tos = -1; } // Push an item onto the stack public void push(int item) { if(tos==stck.length-1) // use length member System.out.println(\"Stack is full.\"); else stck[++tos] = item; } // Pop an item from the stack public int pop() { if(tos < 0) { System.out.println(\"Stack underflow.\"); return 0; } else return stck[tos--]; } } class IFTest { public static void main(String args[]) { FixedStack mystack1 = new FixedStack(5); FixedStack mystack2 = new FixedStack(8); // push some numbers onto the stack for(int i=0; i<5; i++) mystack1.push(i); CU IDOL SELF LEARNING MATERIAL (SLM) 124
for(int i=0; i<8; i++) mystack2.push(i); // pop those numbers off the stack System.out.println(\"Stack in mystack1:\"); for(int i=0; i<5; i++) System.out.println(mystack1.pop()); System.out.println(\"Stack in mystack2:\"); for(int i=0; i<8; i++) System.out.println(mystack2.pop()); } } Following is another implementation of IntStack that creates a dynamic stack by use of the same interface definition. In this implementation, each stack is constructed with an initial length. If this initial length is exceeded, then the stack is increased in size. Each time more room is needed, the size of the stack is doubled. // Implement a \"growable\" stack. class DynStack implements IntStack { private int stck[]; private int tos; // allocate and initialize stack DynStack(int size) { stck = new int[size]; tos = -1; } // Push an item onto the stack public void push(int item) { // if stack is full, allocate a larger stack if(tos==stck.length-1) { CU IDOL SELF LEARNING MATERIAL (SLM) 125
int temp[] = new int[stck.length * 2]; // double size for(int i=0; i<stck.length; i++) temp[i] = stck[i]; stck = temp; stck[++tos] = item; } else stck[++tos] = item; } // Pop an item from the stack public int pop() { if(tos < 0) { System.out.println(\"Stack underflow.\"); return 0; } else return stck[tos--]; } } class IFTest2 { public static void main(String args[]) { DynStack mystack1 = new DynStack(5); DynStack mystack2 = new DynStack(8); // these loops cause each stack to grow for(int i=0; i<12; i++) mystack1.push(i); for(int i=0; i<20; i++) mystack2.push(i); System.out.println(\"Stack in mystack1:\"); CU IDOL SELF LEARNING MATERIAL (SLM) 126
for(int i=0; i<12; i++) System.out.println(mystack1.pop()); System.out.println(\"Stack in mystack2:\"); for(int i=0; i<20; i++) System.out.println(mystack2.pop()); } } The following class uses both the FixedStack and DynStack implementations. It does so through an interface reference. This means that calls to push( ) and pop( ) are resolved at run time rather than at compile time. /* Create an interface variable and access stacks through it. */ class IFTest3 { public static void main(String args[]) { IntStack mystack; // create an interface reference variable DynStack ds = new DynStack(5); FixedStack fs = new FixedStack(8); mystack = ds; // load dynamic stack // push some numbers onto the stack for(int i=0; i<12; i++) mystack.push(i); mystack = fs; // load fixed stack for(int i=0; i<8; i++) mystack.push(i); mystack = ds; System.out.println(\"Values in dynamic stack:\"); for(int i=0; i<12; i++) CU IDOL SELF LEARNING MATERIAL (SLM) 127
System.out.println(mystack.pop()); mystack = fs; System.out.println(\"Values in fixed stack:\"); for(int i=0; i<8; i++) System.out.println(mystack.pop()); } } In this program, mystack is a reference to the IntStack interface. Thus, when it refers to ds, it uses the versions of push( ) and pop( ) defined by the DynStack implementation. When it refers to fs, it uses the versions of push( ) and pop( ) defined by FixedStack. As explained, these determinations are made at run time. Accessing multiple implementations of an interface through an interface reference variable is the most powerful way that Java achieves run-time polymorphism. 4.3.4 Variables in Interfaces You can use interfaces to import shared constants into multiple classes by simply declaring an interface that contains variables which are initialized to the desired values. When you include that interface in a class (that is, when you “implement” the interface), all of those variable names will be in scope as constants. This is similar to using a header file in C/C++ to create a large number of #defined constants or const declarations. If an interface contains no methods, then any class that includes such an interface doesn’t actually implement anything. It is as if that class were importing the constant variables into the class name space as final variables. The next example uses this technique to implement an automated “decision maker”: import java.util.Random; interface SharedConstants { int NO = 0; int YES = 1; int MAYBE = 2; int LATER = 3; int SOON = 4; int NEVER = 5; } CU IDOL SELF LEARNING MATERIAL (SLM) 128
class Question implements SharedConstants { Random rand = new Random(); int ask() { int prob = (int) (100 * rand.nextDouble()); if (prob < 30) return NO; // 30% else if (prob < 60) return YES; // 30% else if (prob < 75) return LATER; // 15% else if (prob < 98) return SOON; // 13% else return NEVER; // 2% } } class AskMe implements SharedConstants { static void answer(int result) { switch(result) { case NO: System.out.println(\"No\"); break; case YES: System.out.println(\"Yes\"); break; case MAYBE: System.out.println(\"Maybe\"); break; case LATER: CU IDOL SELF LEARNING MATERIAL (SLM) 129
System.out.println(\"Later\"); break; case SOON: System.out.println(\"Soon\"); break; case NEVER: System.out.println(\"Never\"); break; } } public static void main(String args[]) { Question q = new Question(); answer(q.ask()); answer(q.ask()); answer(q.ask()); answer(q.ask()); } } Notice that this program makes use of one of Java’s standard classes: Random. This class provides pseudorandom numbers. It contains several methods which allow you to obtain random numbers in the form required by your program. In this example, the method nextDouble( ) is used. It returns random numbers in the range 0.0 to 1.0. In this sample program, the two classes, Question and Ask Me, both implement the SharedConstants interface where NO, YES, MAYBE, SOON, LATER, and NEVER are defined. Inside each class, the code refers to these constants as if each class had defined or inherited them directly. Here is the output of a sample run of this program. Note that the results are different each time it is run. Later Soon CU IDOL SELF LEARNING MATERIAL (SLM) 130
No Yes 4.3.5 Interfaces Can Be Extended One interface can inherit another by use of the keyword extends. The syntax is the same as for inheriting classes. When a class implements an interface that inherits another interface, it must provide implementations for all methods defined within the interface inheritance chain. Following is an example: // One interface can extend another. interface A { void meth1(); void meth2(); } // B now includes meth1() and meth2() -- it adds meth3(). interface B extends A { void meth3(); } // This class must implement all of A and B class MyClass implements B { public void meth1() { System.out.println(\"Implement meth1().\"); } public void meth2() { System.out.println(\"Implement meth2().\"); } public void meth3() { System.out.println(\"Implement meth3().\"); } } CU IDOL SELF LEARNING MATERIAL (SLM) 131
class IFExtend { public static void main(String arg[]) { MyClass ob = new MyClass(); ob.meth1(); ob.meth2(); ob.meth3(); } } As an experiment you might want to try removing the implementation for meth1( ) in MyClass. This will cause a compile-time error. As stated earlier, any class that implements an interface must implement all methods defined by that interface, including any that are inherited from other interfaces. Although the examples we’ve included in this book do not make frequent use of packages or interfaces, both of these tools are an important part of the Java programming environment. Virtually all real programs and applets that you write in Java will be contained within packages. A number will probably implement interfaces as well. It is important, therefore, that you be comfortable with their usage. 4.4 ABSTRACT CLASS There are situations in which you will want to define a superclass that declares the structure of a given abstraction without providing a complete implementation of every method. That is, sometimes you will want to create a superclass that only defines a generalized form that will be shared by all of its subclasses, leaving it to each subclass to fill in the details. Such a class determines the nature of the methods that the subclasses must implement. One way this situation can occur is when a superclass is unable to create a meaningful implementation for a method. This is the case with the class Figure used in the preceding example. The definition of area( ) is simply a placeholder. It will not compute and display the area of any type of object. As you will see as you create your own class libraries, it is not uncommon for a method to have no meaningful definition in the context of its superclass. You can handle this situation two ways. One way, as shown in the previous example, is to simply have it report a warning message. While this approach can be useful in certain situations such as debugging it is not usually appropriate. You may have methods which must be overridden by the subclass in order for the subclass to have any meaning. Consider the class Triangle. It has no meaning if area( ) is not defined. In this case, you want some way to ensure that a subclass does, indeed, override all necessary methods. Java’s solution to this problem is the abstract method. You can require that certain methods be overridden by subclasses by specifying the abstract type modifier. These methods are sometimes referred to as sub `classer responsibility because CU IDOL SELF LEARNING MATERIAL (SLM) 132
they have no implementation specified in the superclass. Thus, a subclass must override them it cannot simply use the version defined in the superclass. To declare an abstract method, use this general form: Abstract type name(parameter-list); As you can see, no method body is present. Any class that contains one or more abstract methods must also be declared abstract. To declare a class abstract, you simply use the abstract keyword in front of the class keyword at the beginning of the class declaration. There can be no objects of an abstract class. That is, an abstract class cannot be directly instantiated with the new operator. Such objects would be useless, because an abstract class is not fully defined. Also, you cannot declare abstract constructors, or abstract static methods. Any subclass of an abstract class must either implement all of the abstract methods in the superclass, or be itself declared abstract. Here is a simple example of a class with an abstract method, followed by a class which implements that method: // A Simple demonstration of abstract. abstract class A { abstract void callme(); // concrete methods are still allowed in abstract classes void callmetoo() { System.out.println(\"This is a concrete method.\"); } } class B extends A { void callme() { System.out.println(\"B's implementation of callme.\"); } } class AbstractDemo { public static void main(String args[]) { B b = new B(); b.callme(); b.callmetoo(); CU IDOL SELF LEARNING MATERIAL (SLM) 133
} } Notice that no objects of class A are declared in the program. As mentioned, it is not possible to instantiate an abstract class. One other point: class A implements a concrete method called callmetoo( ). This is perfectly acceptable. Abstract classes can include as much implementation as they see fit. Although abstract classes cannot be used to instantiate objects, they can be used to create object references, because Java’s approach to run-time polymorphism is implemented through the use of superclass references. Thus, it must be possible to create a reference to an abstract class so that it can be used to point to a subclass object. You will see this feature put to use in the next example. Using an abstract class, you can improve the Figure class shown earlier. Since there is no meaningful concept of area for an undefined two-dimensional figure, the following version of the program declares area( ) as abstract inside Figure. This, of course, means that all classes derived from Figure must override area( ). // Using abstract methods and classes. abstract class Figure { double dim1; double dim2; Figure(double a, double b) { dim1 = a; dim2 = b; } // area is now an abstract method abstract double area(); } class Rectangle extends Figure { Rectangle(double a, double b) { super(a, b); CU IDOL SELF LEARNING MATERIAL (SLM) 134
} // override area for rectangle double area() { System.out.println(\"Inside Area for Rectangle.\"); return dim1 * dim2; } } class Triangle extends Figure { Triangle(double a, double b) { super(a, b); } // override area for right triangle double area() { System.out.println(\"Inside Area for Triangle.\"); return dim1 * dim2 / 2; } } class AbstractAreas { public static void main(String args[]) { // Figure f = new Figure(10, 10); // illegal now Rectangle r = new Rectangle(9, 5); Triangle t = new Triangle(10, 8); Figure figref; // this is OK, no object is created figref = r; System.out.println(\"Area is \" + figref.area()); figref = t; System.out.println(\"Area is \" + figref.area()); } CU IDOL SELF LEARNING MATERIAL (SLM) 135
} As the comment inside main( ) indicates, it is no longer possible to declare objects of type Figure, since it is now abstract. And, all subclasses of Figure must override area, To prove this to yourself, try creating a subclass that does not override area( ). You will receive a compile-time error. Although it is not possible to create an object of type Figure, you can create a reference variable of type Figure. The variable figure f is declared as a reference to Figure, which means that it can be used to refer to an object of any class derived from Figure. As explained, it is through superclass reference variables that overridden methods are resolved at run time. 4.5 PACKAGES The unique name had to be used for each class to avoid name collisions. After a while, without some way to manage the name space, you could run out of convenient, descriptive names for individual classes. You also need some way to be assured that the name you choose for a class will be reasonably unique and not collide with class names chosen by other programmers. (Imagine a small group of programmers fighting over who gets to use the name “Foobar” as a class name. Or, imagine the entire Internet community arguing over who first named a class “Espresso.”) Thankfully, Java provides a mechanism for partitioning the class name space into more manageable chunks. This mechanism is the package. The package is both a naming and a visibility control mechanism. You can define classes inside a package that are not accessible by code outside that package. You can also define class members that are only exposed to other members of the same package. This allows your classes to have intimate knowledge of each other, but not expose that knowledge to the rest of the world. 4.5.1 Defining a Package To create a package is quite easy: simply include a package command as the first statement in a Java source file. Any classes declared within that file will belong to the specified package. The package statement defines a name space in which classes are stored. If you omit the package statement, the class names are put into the default package, which has no name. (This is why you haven’t had to worry about packages before now.) While the default package is fine for short, sample programs, it is inadequate for real applications. Most of the time, you will define a package for your code. This is the general form of the package statement: package pkg; Here, pkg is the name of the package. For example, the following statement creates a package called MyPackage. package MyPackage; CU IDOL SELF LEARNING MATERIAL (SLM) 136
Java uses file system directories to store packages. For example, the .class files for any classes you declare to be part of MyPackage must be stored in a directory called MyPackage. Remember that case is significant, and the directory name must match the package name exactly. More than one file can include the same package statement. The package statement simply specifies to which package the classes defined in a file belong. It does not exclude other classes in other files from being part of that same package. Most real-world packages are spread across many files. You can create a hierarchy of packages. To do so, simply separate each package name from the one above it by use of a period. The general form of a multileveled package statement is shown here: package pkg1[.pkg2[.pkg3]]; A package hierarchy must be reflected in the file system of your Java development system. For example, a package declared as package java.awt.image; Needs to be stored in java/awt/image, java\\awt\\image, or java:awt:image on your UNIX, Windows, or Macintosh file system, respectively. Be sure to choose your package names carefully. You cannot rename a package without renaming the directory in which the classes are stored. 4.5.2 Access Protection Packages add another dimension to access control. As you will see, Java provides many levels of protection to allow fine-grained control over the visibility of variables and methods within classes, subclasses, and packages. Classes and packages are both means of encapsulating and containing the name space and scope of variables and methods. Packages act as containers for classes and Java’s smallest unit of abstraction. Because of the interplay between classes and packages, Java addresses four categories of visibility for class members: Subclasses in the same package Non-subclasses in the same package Subclasses in different packages Classes that are neither in the same package nor subclasses The three access specifies, private, public, and protected, provide a variety of ways to produce the many levels of access required by these categories. While Java’s access control mechanism may seem complicated, we can simplify it as follows. Anything declared public can be accessed from anywhere. Anything declared private cannot be seen outside of its class. When a member does not have an explicit access specification, it is visible to subclasses as well as to other classes in the same package. This is the default access. If you want to allow an element to be seen outside your current package, but only to classes that subclass your CU IDOL SELF LEARNING MATERIAL (SLM) 137
class directly, then declare that element protected. Table 9-1 applies only to members of classes. A class has only two possible access levels: default and public. When a class is declared as public, it is accessible by any other code. If a class has default access, then it can only be accessed by other code within its same package. 4.5.3 Importing Packages Given that packages exist and are a good mechanism for compartmentalizing diverse classes from each other, it is easy to see why all of the built-in Java classes are stored in packages. There are no core Java classes in the unnamed default package; all of the standard classes are stored in some named package. Since classes within packages must be fully qualified with their package name or names, it could become tedious to type in the long dot-separated package path name for every class you want to use. For this reason, Java includes the import statement to bring certain classes, or entire packages, into visibility. Once imported, a class can be referred to directly, using only its name. The import statement is a convenience to the programmer and is not technically needed to write a complete Java program. If you are going to refer to a few dozen classes in your application, however, the import statement will save a lot of typing. In a Java source file, import statements occur immediately following the package statement (if it exists) and before any class definitions. This is the general form of the import statement: import pkg1[.pkg2].(classname|*); Here, pkg1 is the name of a top-level package, and pkg2 is the name of a subordinate package inside the outer package separated by a dot (.). There is no practical limit on the depth of a package hierarchy, except that imposed by the file system. Finally, you specify either an explicit classname or a star (*), which indicates that the Java compiler should import the entire package. This code fragment shows both forms in use: import java.util.Date; import java.io.*; All of the standard Java classes included with Java are stored in a package called java. The basic language functions are stored in a package inside of the java package called java.lang. Normally, you have to import every package or class that you want to use, but since Java is useless without much of the functionality in java.lang, it is implicitly imported by the compiler for all programs. This is equivalent to the following line being at the top of all of your programs: import java.lang.*; If a class with the same name exists in two different packages that you import using the star form, the compiler will remain silent, unless you try to use one of the classes. In that case, you will get a compile-time error and have to explicitly name the class specifying its package. Any place you use a class name, you can use its fully qualified name, which includes its full package hierarchy. For example, this fragment uses an import statement: import java.util.*; CU IDOL SELF LEARNING MATERIAL (SLM) 138
class MyDate extends Date { } The same example without the import statement looks like this: class MyDate extends java.util.Date { } 4.6 JAVA I/O CLASSES AND INTERFACES The I/O classes defined by java.io are listed here: BufferedInputStream FileWriter PipedInputStream BufferedOutputStream FilterInputStream PipedOutputStream BufferedReader FilterOutputStream PipedReader BufferedWriter FilterReader PipedWriter ByteArrayInputStream FilterWriter PrintStream ByteArrayOutputStream InputStream PrintWriter CharArrayReader InputStreamReader PushbackInputStream CharArrayWriter LineNumberReader PushbackReader DataInputStream ObjectInputStream RandomAccessFile DataOutputStream ObjectInputStream. GetField Reader File ObjectOutputStream SequenceInputStream FileDescriptor ObjectOutputStream.PutField SerializablePermission FileInputStream ObjectStreamClass StreamTokenizer FileOutputStream ObjectStreamField StringReader FilePermission OutputStream StringWriter FileReader OutputStreamWriter Writer The ObjectInputStream.GetField and ObjectOutputStream.PutField inner classes were added by Java 2. The java.io package also contains two classes that were deprecated by Java 2 and are not shown in the preceding table: LineNumberInputStream and StringBufferInputStream. These classes should not be used for new code. The following interfaces are defined by java.io: DataInput FilenameFilter ObjectOutput DataOutput ObjectInput ObjectStreamConstants CU IDOL SELF LEARNING MATERIAL (SLM) 139
Externalizable ObjectInputValidation Serializable FileFilter The File Filter interface was added by Java 2. As you can see, there are many classes and interfaces in the java.io package. These include byte and character streams, and object serialization. This chapter examines several of the most commonly used I/O components, beginning with one of the most unique: File. FILE Although most of the classes defined by java.io operate on streams, the File class does not. It deals directly with files and the file system. That is, the File class does not specify how information is retrieved from or stored in files; it describes the properties of a file itself. A File object is used to obtain or manipulate the information associated with a disk file, such as the permissions, time, date, and directory path, and to navigate subdirectory hierarchies. Files are a primary source and destination for data within many programs. Although there are severe restrictions on their use within applets for security reasons, files are still a central resource for storing persistent and shared information. A directory in Java is treated simply as a File with one additional property a list of filenames that can be examined by the list( ) method. The following constructors can be used to create File objects: File(String directoryPath) File(String directoryPath, String filename) File(File dirObj, String filename) File(URI uriObj) Here, directoryPath is the path name of the file, filename is the name of the file, dirObj is a File object that specifies a directory, and uriObj is a URI object that describes a file. The fourth constructor was added by Java 2, version 1.4. The following example creates three files: f1, f2, and f3. The first File object is constructed with a directory path as the only argument. The second includes two arguments the path and the filename. The third includes the file path assigned to f1 and a filename; f3 refers to the same file as f2. File f1 = new File(\"/\"); File f2 = new File(\"/\",\"autoexec.bat\"); File f3 = new File(f1,\"autoexec.bat\"); Java does the right thing with path separators between UNIX and Windows conventions. If you use a forward slash (/) on a Windows version of Java, the path will still resolve correctly. Remember, if you are using the Windows convention of a backslash character (\\), you will CU IDOL SELF LEARNING MATERIAL (SLM) 140
need to use its escape sequence (\\\\) within a string. The Java convention is to use the UNIX- and URL-style forward slash for path separators. File defines many methods that obtain the standard properties of a File object. For example, getName( ) returns the name of the file, getParent( ) returns the name of the parent directory, and exists( ) returns true if the file exists, false if it does not. The File class, however, is not symmetrical. By this, we mean that there are many methods that allow you to examine the properties of a simple file object, but no corresponding function exists to change those attributes. The following example demonstrates several of the File methods: // Demonstrate File. import java.io.File; class FileDemo { static void p(String s) { System.out.println(s); } public static void main(String args[]) { File f1 = new File(\"/java/COPYRIGHT\"); p(\"File Name: \" + f1.getName()); p(\"Path: \" + f1.getPath()); p(\"Abs Path: \" + f1.getAbsolutePath()); p(\"Parent: \" + f1.getParent()); p(f1.exists() ? \"exists\" : \"does not exist\"); p(f1.canWrite() ? \"is writeable\" : \"is not writeable\"); p(f1.canRead() ? \"is readable\" : \"is not readable\"); p(\"is \" + (f1.isDirectory() ? \"\" : \"not\" + \" a directory\")); p(f1.isFile() ? \"is normal file\" : \"might be a named pipe\"); p(f1.isAbsolute() ? \"is absolute\" : \"is not absolute\"); p(\"File last modified: \" + f1.lastModified()); p(\"File size: \" + f1.length() + \" Bytes\"); } } When you run this program, you will see something similar to the following: CU IDOL SELF LEARNING MATERIAL (SLM) 141
File Name: COPYRIGHT Path: /java/COPYRIGHT Abs Path: /java/COPYRIGHT Parent: /java exists is writeable is readable is not a directory is normal file is absolute File last modified: 812465204000 File size: 695 Bytes Most of the File methods are self-explanatory. isFile( ) and isAbsolute( ) are not. isFile( ) returns true if called on a file and false if called on a directory. Also, isFile( ) returns false for some special files, such as device drivers and named pipes, so this method can be used to make sure the file will behave as a file. The isAbsolute( ) method returns true if the file has an absolute path and false if its path is relative. File also includes two useful utility methods. The first is renameTo( ), shown here: boolean renameTo(File newName) Here, the filename specified by newName becomes the new name of the invoking File object. It will return true upon success and false if the file cannot be renamed (if you either attempt to rename a file so that it moves from one directory to another or use an existing filename, for example). The second utility method is delete( ), which deletes the disk file represented by the path of the invoking File object. It is shown here: Boolean delete( ) You can also use delete( ) to delete a directory if the directory is empty. delete( ) returns true if it deletes the file and false if the file cannot be removed. Here are some other File methods that you will find helpful. (They were added by Java 2.) Method Description void deleteOnExit( ) Removes the file associated with the invoking object when the Java Virtual CU IDOL SELF LEARNING MATERIAL (SLM) 142
Machine terminates. Boolean isHidden( ) Returns true if the invoking file is hidden. Returns false otherwise. Boolean setLastModified(long Sets the time stamp on the invoking millisec) file to that specified by millisec, which is the number of milliseconds from? January 1, 1970, Coordinated Universal Time (UTC). boolean setReadOnly( ) Sets the invoking file to read-only. Also, because File supports the Comparable interface, the method compare To ( ) is also supported. 4.7 I/O STREAM CLASSES Java’s stream-based I/O is built upon four abstract classes: Input Stream, Output Stream, Reader, and Writer. They are used to create several concrete stream subclasses. Although your programs perform their I/O operations through concrete subclasses, the top-level classes define the basic functionality common to all stream classes. Input Stream and Output Stream are designed for byte streams. Reader and Writer are designed for character streams. The byte stream classes and the character stream classes form separate hierarchies. In general, you should use the character stream classes when working with characters or strings, and use the byte stream classes when working with bytes or other binary objects. 4.8 INPUT AND OUTPUT STREAM An I/O Stream represents an input source or an output destination. A stream can represent many different kinds of sources and destinations, including disk files, devices, other programs, and memory arrays. Streams support many different kinds of data, including simple bytes, primitive data types, localized characters, and objects. Some streams simply pass on data; others manipulate and transform the data in useful ways. No matter how they work internally, all streams present the same simple model to programs that use them: A stream is a sequence of data. A program uses an input stream to read data from a source, one item at a time: CU IDOL SELF LEARNING MATERIAL (SLM) 143
Figure 4.1: Reading information into a program. A program uses an output stream to write data to a destination, one item at time: Figure 4.2: Writing information from a program. In this lesson, we'll see streams that can handle all kinds of data, from primitive values to advanced objects. The data source and data destination pictured above can be anything that holds, generates, or consumes data. Obviously this includes disk files, but a source or destination can also be another program, a peripheral device, a network socket, or an array. In the next section, we'll use the most basic kind of streams, byte streams, to demonstrate the common operations of Stream I/O. For sample input, we'll use the example file xanadu.txt, which contains the following verse: In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea Byte Streams CU IDOL SELF LEARNING MATERIAL (SLM) 144
Programs use byte streams to perform input and output of 8-bit bytes. All byte stream classes are descended from InputStream and OutputStream. There are many byte stream classes. To demonstrate how byte streams work, we'll focus on the file I/O byte streams, FileInputStream and FileOutputStream. Other kinds of byte streams are used in much the same way; they differ mainly in the way they are constructed. Using Byte Streams We'll explore FileInputStream and FileOutputStream by examining an example program named CopyBytes, which uses byte streams to copy xanadu.txt, one byte at a time. import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class CopyBytes { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream(\"xanadu.txt\"); out = new FileOutputStream(\"outagain.txt\"); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); CU IDOL SELF LEARNING MATERIAL (SLM) 145
} } } } CopyBytes spends most of its time in a simple loop that reads the input stream and writes the output stream, one byte at a time, as shown in the following figure. Figure 4.3: Simple byte stream input and output. Always Close Streams Closing a stream when it's no longer needed is very important so important that CopyBytes uses a finally block to guarantee that both streams will be closed even if an error occurs. This practice helps avoid serious resource leaks. One possible error is that CopyBytes was unable to open one or both files. When that happens, the stream variable corresponding to the file never changes from its initial null value. That's why CopyBytes makes sure that each stream variable contains an object reference before invoking close. When Not to Use Byte Streams CopyBytes seems like a normal program, but it actually represents a kind of low-level I/O that you should avoid. Since xanadu.txt contains character data, the best approach is to use character streams, as discussed in the next section. There are also streams for more complicated data types. Byte streams should only be used for the most primitive I/O. So why talk about byte streams? Because all other stream types are built on byte streams CU IDOL SELF LEARNING MATERIAL (SLM) 146
Character Streams The Java platform stores character values using Unicode conventions. Character stream I/O automatically translates this internal format to and from the local character set. In Western locales, the local character set is usually an 8-bit superset of ASCII. For most applications, I/O with character streams is no more complicated than I/O with byte streams. Input and output done with stream classes automatically translates to and from the local character set. A program that uses character streams in place of byte streams automatically adapts to the local character set and is ready for internationalization all without extra effort by the programmer. If internationalization isn't a priority, you can simply use the character stream classes without paying much attention to character set issues. Later, if internationalization becomes a priority, your program can be adapted without extensive recoding. See the Internationalization trail for more information. USING CHARACTER STREAMS All character stream classes are descended from Reader and Writer. As with byte streams, there are character stream classes that specialize in file I/O: FileReader and FileWriter. The CopyCharacters example illustrates these classes. import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyCharacters { public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader(\"xanadu.txt\"); outputStream = new FileWriter(\"characteroutput.txt\"); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { CU IDOL SELF LEARNING MATERIAL (SLM) 147
if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } } CopyCharacters is very similar to CopyBytes. The most important difference is that CopyCharacters uses FileReader and FileWriter for input and output in place of FileInputStream and FileOutputStream. Notice that both CopyBytes and CopyCharacters use an int variable to read to and write from. However, in CopyCharacters, the int variable holds a character value in its last 16 bits; in CopyBytes, the int variable holds a byte value in its last 8 bits. CHARACTER STREAMS THAT USE BYTE STREAMS Character streams are often \"wrappers\" for byte streams. The character stream uses the byte stream to perform the physical I/O, while the character stream handles translation between characters and bytes. FileReader, for example, uses FileInputStream, while FileWriter uses FileOutputStream. There are two general-purpose byte-to-character \"bridge\" streams: InputStreamReader and OutputStreamWriter. Use them to create character streams when there are no pre-packaged character stream classes that meet your needs. The sockets lesson in the networking trail shows how to create character streams from the byte streams provided by socket classes. LINE-ORIENTED I/O Character I/O usually occurs in bigger units than single characters. One common unit is the line: a string of characters with a line terminator at the end. A line terminator can be a carriage-return/line-feed sequence (\"\\r\\n\"), a single carriage-return (\"\\r\"), or a single line-feed (\"\\n\"). Supporting all possible line terminators allows programs to read text files created on any of the widely used operating systems. Let's modify the CopyCharacters example to use line-oriented I/O. To do this, we have to use two classes we haven't seen before, BufferedReader and PrintWriter. We'll explore these classes in greater depth in Buffered I/O and Formatting. Right now, we're just interested in their support for line-oriented I/O. CU IDOL SELF LEARNING MATERIAL (SLM) 148
The CopyLines example invokes BufferedReader.readLine and PrintWriter.println to do input and output one line at a time. import java.io.FileReader; import java.io.FileWriter; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.IOException; public class CopyLines { public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader(\"xanadu.txt\")); outputStream = new PrintWriter(new FileWriter(\"characteroutput.txt\")); String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } } CU IDOL SELF LEARNING MATERIAL (SLM) 149
Invoking readLine returns a line of text with the line. CopyLines outputs each line using print in, which appends the line terminator for the current operating system. This might not be the same line terminator that was used in the input file. Input Stream and Output Stream Hierarchy As described earlier, a stream can be defined as a sequence of data. The InputStream is used to read data from a source and the OutputStream is used for writing data to a destination. Here is a hierarchy of classes to deal with Input and Output streams. Figure 4.4: Input and Output streamshierarchy The two important streams are FileInputStream and FileOutputStream, which would be discussed in this tutorial. FILEINPUTSTREAM This stream is used for reading data from the files. Objects can be created using the keyword new and there are several types of constructors available. Following constructor takes a file name as a string to create an input stream object to read the file − InputStream f = new FileInputStream(\"C:/java/hello\"); Following constructor takes a file object to create an input stream object to read the file. First we create a file object using File() method as follows − CU IDOL SELF LEARNING MATERIAL (SLM) 150
Search
Read the Text Version
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334