CS F213 Objected Oriented Programming Labsheet 9

Spring 2024

I/O in Java

Java I/O (Input and Output) is used to process the input and produce the output.

Java uses the concept of a stream to make I/O operation fast. The java.io package contains all the classes required for input and output operations. We can perform file handling in Java by Java I/O API

Stream

A stream is a sequence of data. It's called a stream because it is like a stream of water that continues to flow. A stream can produce or consume information and is linked to a physical device (keyboard, mouse, monitor, network socket, etc.)

In Java, 3 streams are created for us automatically. All these streams are attached with the console.

  1. System.out: standard output stream

  2. System.in: standard input stream

  3. System.err: standard error stream

Java provides two types of streams:

  • Byte streams

  • Character streams

Java Console class

The Java Console class is be used to get input from console. It provides methods to read texts and passwords. If you read password using Console class, it will not be displayed to the user. The java.io.Consoleclass is attached with system console internally.

Java Console class methods

Method
Description

Reader reader()

It is used to retrieve the reader object associated with the console

String readLine()

It is used to read a single line of text from the console

String readLine(String fmt, Object... args)

It provides a formatted prompt then reads the single line of text from the console

char[] readPassword()

It is used to read password that is not being displayed on the console

char[] readPassword(String fmt, Object... args)

It provides a formatted prompt then reads the password that is not being displayed on the console

Console format(String fmt, Object... args)

It is used to write a formatted string to the console output stream

Console printf(String format, Object... args)

It is used to write a string to the console output stream

PrintWriter writer()

It is used to retrieve the PrintWriter object associated with the console

void flush()

It is used to flush the console

Example

import java.io.Console;  
class ReadStringTest{    
public static void main(String args[]){    
Console c=System.console();//get instance of Console class    
System.out.println("Enter your name: ");    
String n=c.readLine();    
System.out.println("Welcome "+n);    
}    
}
Enter your name: John Doe
Welcome John Doe

Java Console Example to read password

import java.io.Console;  
class ReadPasswordTest{    
public static void main(String args[]){    
Console c=System.console();    
System.out.println("Enter password: ");    
char[] ch=c.readPassword();    
String pass=String.valueOf(ch);//converting char array into string    
System.out.println("Password is: "+pass);    
}    
}  
Enter password: 
Password is: 123

Java Reader and Writer class

Java Reader is an abstract class for reading character streams. The only methods that a subclass must implement are read(char[], int, int) and close(). Most subclasses, however, will override some of the methods to provide higher efficiency, additional functionality, or both.

Eample

import java.io.*;  
public class ReaderExample {  
    public static void main(String[] args) {  
        try {  
            Reader reader = new FileReader("file.txt");  
            int data = reader.read();  
            while (data != -1) {  
                System.out.print((char) data);  
                data = reader.read();  
            }  
            reader.close();  
        } catch (Exception ex) {  
            System.out.println(ex.getMessage());  
        }  
    }  
}  
"This is the content of file.txt"

Java Writer is an abstract class for writing to character streams. The methods that a subclass must implement are write(char[], int, int), flush(), and close(). Most subclasses will override some of the methods defined here to provide higher efficiency, functionality or both.

Eample

import java.io.*;  
public class WriterExample {  
    public static void main(String[] args) {  
        try {  
            Writer w = new FileWriter("output.txt");  
            String content = "Content for output";  
            w.write(content);  
            w.close();  
            System.out.println("Done");  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}   
Done 

Output.txt will contain the text "Content for output" afterwards

Java BufferedReader and BufferedWriter class

Java BufferedReader class is used to read the text from a character-based input stream. It can be used to read data line by line by readLine() method. It makes the performance fast. It inherits Reader class.

In this example, we are reading the data from the text file file.txt using Java BufferedReader class.

import java.io.*;  
public class BufferedReaderExample {  
    public static void main(String args[])throws Exception{    
          FileReader fr=new FileReader("D:\\file.txt");    
          BufferedReader br=new BufferedReader(fr);    
  
          int i;    
          while((i=br.read())!=-1){  
          System.out.print((char)i);  
          }  
          br.close();    
          fr.close();    
    }    
}

Output will contain the contents of file.txt

An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified charset. The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.

Reading data from console by InputStreamReader and BufferedReader until user writes stop

import java.io.*;  
public class BufferedReaderExample{    
public static void main(String args[])throws Exception{              
     InputStreamReader r=new InputStreamReader(System.in);    
     BufferedReader br=new BufferedReader(r);           
     String name="";    
     while(!name.equals("stop")){    
      System.out.println("Enter data: ");    
      name=br.readLine();    
      System.out.println("data is: "+name);    
     }              
    br.close();    
    r.close();    
    }    
}
Enter data: 
data goes here    
data is: data goes here
Enter data: 
stop
data is: stop

Java BufferedWriter class is used to provide buffering for Writer instances. It makes the performance fast. It inherits Writer class. The buffering characters are used for providing the efficient writing of single arrays, characters, and strings.

import java.io.*;  
public class BufferedWriterExample {  
public static void main(String[] args) throws Exception {     
    FileWriter writer = new FileWriter("D:\\testout.txt");  
    BufferedWriter buffer = new BufferedWriter(writer);  
    buffer.write("Welcome");  
    buffer.close();  
    System.out.println("Success");  
    }  
}  

This will print Success and write the text "Welcome" to testout.txt

Reading and Writing Files by FileInputStream and FileOutputStream

Java FileInputStream class obtains input bytes from a file. It is used for reading byte-oriented data (streams of raw bytes) such as image data, audio, video etc. You can also read character-stream data. But, for reading streams of characters, it is recommended to use FileReader class.

Example to read all characters from a file

import java.io.FileInputStream;  
public class DataStreamExample {  
     public static void main(String args[]){    
          try{    
            FileInputStream fin=new FileInputStream("D:\\testout.txt");    
            int i=0;    
            while((i=fin.read())!=-1){    
             System.out.print((char)i);    
            }    
            fin.close();    
          }
          catch(Exception e){System.out.println(e);}    
    }    
}  

Java FileOutputStream is an output stream used for writing data to a file. If you have to write primitive values into a file, use FileOutputStream class. You can write byte-oriented as well as character-oriented data through FileOutputStream class. But, for character-oriented data, it is preferred to use FileWriter than FileOutputStream.

import java.io.FileOutputStream;  
public class FileOutputStreamExample {  
    public static void main(String args[]){    
           try{    
             FileOutputStream fout=new FileOutputStream("D:\\testout.txt");    
             String s="Welcome.";    
             byte b[]=s.getBytes();//converting string into byte array    
             fout.write(b);    
             fout.close();    
             System.out.println("success...");    
            }catch(Exception e){System.out.println(e);}    
      }    
} 

Reading and Writing Files by FileWriter and FileReader

Java FileReader class is used to read data from the file. It returns data in byte format like FileInputStream class. It is character-oriented class which is used for file handling in java.

Example

import java.io.FileReader;  
public class FileReaderExample {  
    public static void main(String args[])throws Exception{    
          FileReader fr=new FileReader("D:\\testout.txt");    
          int i;    
          while((i=fr.read())!=-1)    
          System.out.print((char)i);    
          fr.close();    
    }    
} 

Java FileWriter class is used to write character-oriented data to a file. It is character-oriented class which is used for file handling in java. Unlike FileOutputStream class, you don't need to convert string into byte array because it provides method to write string directly.

Example

import java.io.FileWriter;  
public class FileWriterExample {  
    public static void main(String args[]){    
         try{    
           FileWriter fw=new FileWriter("D:\\testout.txt");    
           fw.write("Welcome aboard folks");    
           fw.close();    
          }catch(Exception e){System.out.println(e);}    
          System.out.println("Success...");    
     }    
}  

Serialization and Deserialization in Java

Serialization in Java is a mechanism of writing the state of an object into a byte-stream. It is mainly used in Hibernate, RMI (Remote Method Invocation) and JPA (Java Persistence API) technologies.

The reverse operation of serialization is called deserialization where byte-stream is converted into an object. The serialization and deserialization process is platform-independent, it means you can serialize an object on one platform and deserialize it on a different platform.

For serializing the object, we call the writeObject() method of ObjectOutputStream class, and for deserialization we call the readObject() method of ObjectInputStream class.

We must have to implement the Serializable interface for serializing the object.

ObjectOutputStream class

The ObjectOutputStream class is used to write primitive data types, and Java objects to an OutputStream. Only objects that support the java.io.Serializable interface can be written to streams.

ObjectInputStream class

An ObjectInputStream deserializes objects and primitive data written using an ObjectOutputStream.

Example for Serialization

import java.io.Serializable;  
public class Student implements Serializable{  
 int id;  
 String name;  
 public Student(int id, String name) {  
  this.id = id;  
  this.name = name;  
 }  
} 
class Persist{    
 public static void main(String args[]){    
  try{    
  //Creating the object    
  Student s1 =new Student(2345,"John");    
  //Creating stream and writing the object    
  FileOutputStream fout=new FileOutputStream("f.txt");    
  ObjectOutputStream out=new ObjectOutputStream(fout);    
  out.writeObject(s1);    
  out.flush();    
  //closing the stream    
  out.close();    
  System.out.println("success");    
  }catch(Exception e){System.out.println(e);}    
 }    
} 

Deserialization is the process of reconstructing the object from the serialized state. It is the reverse operation of serialization. Let's see an example where we are reading the data from a deserialized object.

Example for Deserialization

import java.io.*;  
class Depersist{  
 public static void main(String args[]){  
  try{  
  //Creating stream to read the object  
  ObjectInputStream in=new ObjectInputStream(new FileInputStream("f.txt"));  
  Student s=(Student)in.readObject();  
  //printing the data of the serialized object  
  System.out.println(s.id+" "+s.name);  
  //closing the stream  
  in.close();  
  }catch(Exception e){System.out.println(e);}  
 }  
}  
2345 John

transient keyword

During the serialization, when we do not want an object to be serialized we can use a transient keyword. The transient keyword can be used with the data members of a class in order to avoid their serialization. For example, if a program accepts a user's login details and password. But we don't want to store the original password in the file. Here, we can use transient keyword and when JVM reads the transient keyword it ignores the original value of the object and instead stores the default value of the object.

If you deserialize the serialized object, you will get the default value for transient variable

Eample

public class Student implements Serializable{    
 int id;    
 String name;    
 transient int age;//Now it will not be serialized    
 public Student(int id, String name,int age) {    
  this.id = id;    
  this.name = name;    
  this.age=age;    
 }    
}

Exercises

Exercise 1

Student Record Management

Problem Statement: You are tasked with developing a student record management system. The system should allow users to input student details, such as name, roll number, and marks, and store them in a file student.txt Users should also be able to retrieve and display the student records from the file. You have to use BufferedReader, BufferedWriter, InputStreamReader, FileWriter and FileReader to achieve the task.

Sample I/O

brownie@Cranberry:~/ubuntu$ javac Main.java
brownie@Cranberry:~/ubuntu$ java Main
1. Add student record
2. Display student records
Enter your choice: 1

Enter name: John Doe
Enter roll number: 2345
Enter marks: 8
Student record added successfully.

brownie@Cranberry:~/ubuntu$ java Main
1. Add student record
2. Display student records
Enter your choice: 2

Student Records:
- Name: John Doe, Roll Number: 2345, Marks: 8

brownie@Cranberry:~/ubuntu$

Exercise 2

Problem Statement: You are working on an employee management system. Implement serialization and deserialization to store and retrieve employee objects. The system should allow users to add new employee records (Name, Id, Department and salary), serialize them to a file, and retrieve and display the employee records from the file using deserialization. However, the department variable in the Employee class that should not be serialized.

Make two classes, one Employee that implements Serializable, and another Main to make ObjectOutputStream and ObjectInputStream for serializing and deserializing the file.

Sample I/O

brownie@Cranberry:~/ubuntu$ javac Ex2.java
brownie@Cranberry:~/ubuntu$ java Ex2
1. Add employee record
2. Display employee records
Enter your choice: 1

Enter name: John Doe
Enter employee ID: 2345
Enter department: Sales
Enter salary: 20000
Employee record added successfully.

brownie@Cranberry:~/ubuntu$ java Ex2
1. Add employee record
2. Display employee records
Enter your choice: 2

Employee Records:
- Name: John Doe, Employee ID: 2345, Department: null, Salary: 20000.0

Notice that department was not serialized and therfore while printing it's value is null


While both exercises involve reading and writing data to/from a file, serialization provides some distinct advantages over plain file I/O when it comes to handling complex object structures. Here's an example that illustrates the advantages of serialization:

Consider a scenario where you have a system that manages a collection of objects representing different shapes, such as circles, rectangles, and triangles. Each shape object has its own set of properties like dimensions, color, and position.

Without serialization: If you were to store the shape objects in a file using plain file I/O, you would need to come up with a custom file format and define your own rules for reading and writing each shape's properties. For example, you might choose a CSV (Comma-Separated Values) format where each line represents a shape and each value represents a property. While this approach is feasible, it requires significant manual effort to correctly parse and format the data for each shape type.

With serialization: By using serialization, you can simply serialize the entire collection of shape objects and write them to a file as a single unit. The serialization process automatically handles the conversion of the complex object structure into a byte stream, which can be directly written to a file. Similarly, during deserialization, the byte stream is read from the file and reconstructed back into the original object structure.


Exercise 3

Problem Statement: You are building a Shape Collection application that manages a collection of various shapes, including circles, rectangles, and triangles. Implement serialization and deserialization to store and retrieve the shape objects. Your task is to complete the program by adding serialization support to the Shape class hierarchy.

Instructions:

  • Implement the Serializable interface in each shape class (Circle, Rectangle, and Triangle) to enable serialization.

  • Ensure that all necessary properties and methods required to represent each shape are present in their respective classes.

  • Implement the ShapeCollection class, which will serve as the container for the shape objects. This class should have methods to add shapes, serialize the shape collection to a file (use FileReader and FileWriter to do so), and deserialize the shape collection from a file.

  • Test your implementation by adding multiple shapes to the collection, serializing the collection to a file, and then deserializing it to retrieve and display the shapes.

Sample I/O

brownie@Cranberry:~/ubuntu$ javac Ex3.java
brownie@Cranberry:~/ubuntu$ java Ex3
1. Add a circle
2. Add a rectangle
3. Add a triangle
4. Serialize shape collection
5. Deserialize shape collection
6. Exit
Enter your choice: 1
Enter the radius of the circle: 5
Circle added successfully.

1. Add a circle
2. Add a rectangle
3. Add a triangle
4. Serialize shape collection
5. Deserialize shape collection
6. Exit
Enter your choice: 1
Enter the radius of the circle: 3
Circle added successfully.

1. Add a circle
2. Add a rectangle
3. Add a triangle
4. Serialize shape collection
5. Deserialize shape collection
6. Exit
Enter your choice: 3
Enter the base of the triangle: 4
Enter the height of the triangle: 5
Triangle added successfully.

1. Add a circle
2. Add a rectangle
3. Add a triangle
4. Serialize shape collection
5. Deserialize shape collection
6. Exit
Enter your choice: 4
Shape collection serialized successfully.

1. Add a circle
2. Add a rectangle
3. Add a triangle
4. Serialize shape collection
5. Deserialize shape collection
6. Exit
Enter your choice: 5
Shape collection deserialized successfully.
Shapes in the collection:
1. Circle: Radius = 5.0
2. Circle: Radius = 3.0
3. Triangle: Base = 4.0, Height = 5.0

1. Add a circle
2. Add a rectangle
3. Add a triangle
4. Serialize shape collection
5. Deserialize shape collection
6. Exit
Enter your choice: 6
Exiting the program...

Last updated