Java I/O

The Java API designer considered input and output as streams. If the stream comes into the Java program, it is an input stream. If the stream goes out of the Java program, it is an output stream. When creating a stream object, the critical information is the source for the input stream and the destination for the output stream. You need this information as an argument for stream constructors. The shape of sources or destinations varies. It may be a file, a console screen, or a socket.

Streams are classified as follows:

  • Is it a byte stream? Is it a character stream?
  • Is it an input stream? Is it an output stream?
  • Does it do I/O? Does it help with I/O performance?

Byte Stream

The byte stream is input and output in units of 1 byte for generally I/O to binary files such as videos or images. InputStream and OutputStream are abstract and top-level classes of all byte stream classes.

InputStream
int read()
It is an abstract method. It reads one byte from the input stream and returns a value between 0 and 255. It returns -1 if the end of the input stream has been reached and can no longer be read.
int read(byte[] b)
In most cases, it reads up to the size of b, stores it in b, and returns the number of bytes read. It returns -1 if the end of the input stream has been reached and can no longer be read.
int read(byte[] b, int off, int len)
It reads up to the size of the len, stores it in the off position of b, and returns the number of bytes read. It returns -1 if the end of the input stream has been reached and can no longer be read.
int available()
It returns the number of bytes that JVM can read.
void close()
It closes the input stream and returns the system resources associated with the stream.
OutputStream
void write(int b)
It outputs the low 8 bits of b.
void write(byte[] b)
It outputs the contents of b.
void write(byte[] b, int off, int len)
It outputs len bytes from the off position of b.
void flush()
It outputs the remaining bytes in the buffer.
void close()
It closes the output stream and returns the system resources associated with it.
Test.java
package net.java_school.stream;

import java.io.FileInputStream;

public class Test {

  public static void main(String[] args) throws Exception {
    int n = 0;
    FileInputStream fis = new FileInputStream("C:/javaApp/test.txt");
    while ((n = fis.available()) > 0) {
      byte[] b = new byte[n];
      int result = fis.read(b);
      if (result == -1) {
        break;
      }
      String s = new String(b);
      System.out.println(s);
    }
    fis.close();
  }
}

Test with the following file:

test.txt
a b c d e
1 2 3 4 5

The following is an example of creating a copy file.

Test.java
package net.java_school.stream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test {

  public static void main(String[] args) throws IOException {
    int n = 0;
    FileInputStream fis = new FileInputStream("C:/javaApp/test.txt");
    FileOutputStream fos = new FileOutputStream("C:/javaApp/testCopy.txt", false);
    while ((n = fis.available()) > 0) {
      byte[] b = new byte[n];
      int result = fis.read(b);
      if (result == -1) {
        break;
      }
      fos.write(b);
    }
    fis.close();
    fos.close();
  }
}

FileInputStream/FileOutputStream is a class for file I/O in bytes.

FileInputStream's Constructors
FileInputStream(File file)
It creates an input stream for the file specified by the file argument.
FileInputStream(String name)
It creates an input stream for the file specified by name.
FileOutputStream's Constructors
FileOutputStream(File file)
It creates an output stream for the file specified by the file argument.
FileOutputStream(File file, boolean append)
It creates an output stream for the file specified by the file argument. If append flag is true, the output stream appends the contents to the end of the file's contents.
FileOutputStream(String name)
It creates an output stream for the file specified by name.
FileOutputStream(String name, boolean append)
It creates an output stream for the file specified by name. If the append flag is true, the output stream appends the contents to the end of the file's contents. If false, the output stream overwrites the existing contents.

The methods of FileInputStream and FileOutputStream used in the example are those of InputStream and OutputStream. FileInputStream extends OutputStream and FileOutputStream extends OutputStream.

Character Stream

Reader and Writer are the character input and output classes' top-level classes. Reader and Writer are all abstract classes, and the unit of input and output data in the method is a character.

Reader
int read()
It reads one single character and returns the Unicode value of the character.
int read(char[] b)
It reads a character as many as the size of array b, stores it in b, and returns the number of characters read.
abstract int read(char[] b, int off, int len)
It reads a character as many as the size of len and returns the number of characters read.
Writer
void write(String s)
It outputs s.
void write(char[] b)
It outputs b.
void write(char[] b, int off, int len)
It outputs characters as many as the size of len from the off index of array b.
void write(String s, int off, int len)
It outputs characters as many as size of len from the off index of String s.
Test2.java
package net.java_school.stream;

import java.io.FileReader;
import java.io.FileWriter;

public class Test2 {

  public static void main(String[] args) throws Exception {
    int n = 0;
    FileReader fr = new FileReader("C:/javaApp/test.txt");
    FileWriter fw = new FileWriter("C:/javaApp/testCopy.txt", false);
    while ((n = fr.read()) != -1) {
      fw.write(n);
    }
    fr.close();
    fw.close();
  }
}

BufferedReader/BufferedWriter

BufferedReader/BufferedWriter provides a buffering function for character I/O.

Test2.java
package net.java_school.stream;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

public class Test {

  public static void main(String[] args) throws Exception {
    FileReader fr = new FileReader("C:/javaApp/test.txt");
    BufferedReader br = new BufferedReader(fr);
    String s = null;
    FileWriter fw = new FileWriter("C:/javaApp/testCopy2.txt", false);
    BufferedWriter bw = new BufferedWriter(fw);
    while ((s = br.readLine()) != null) {
      bw.write(s);
      bw.newLine();
    }
    fr.close();
    br.close();
    bw.close();
    fw.close();
  }
}

InputStreamReader/OutputStreamWriter

An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified charset.
For more information, see this.
An OutputStreamWriter is a bridge from character streams to byte streams: Characters written to it are encoded into bytes using a specified charset.
For more information, see this.

InputStreamReader's Constructor
InputStreamReader(InputStream in)
InputStreamReader(InputStream in, String enc)
OutputStreamWriter's Constructor
OutputStreamWriter(OutputStream out)
OutputStreamWriter(OutputStream out, String enc)

The following example uses InputStreamReader to convert bytes input from the keyboard to characters.

InputTest.java
package net.java_school.stream;

import java.io.*;

public class InputTest {
  public static void main(String[] args) throws IOException {
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);
    String input = br.readLine();
    System.out.println("Input: " + input); 
  }
}

The example creates an InputStreamReader with the keyboard as the source. -- In Java, the keyboard is represented by System.in -- Then create a BufferedReader object for the buffer function. At this time, the example code passes the InputStreamReader's reference to the BufferedReader's constructor.

String input = br.readLine();

This code waits for user input.

BufferedReader's readLine() method returns the input string excluding the Enter key when the user presses the Enter key.

For more information about the BufferedReader, see this.

The following example prints character data to a file.

OutputTest.java
package net.java_school.stream;

import java.io.*;

public class OutputTest {
  public static void main(String[] args) {
    FileWriter fw = null;
    try {
      fw = new FileWriter("C:/output.txt", true);
      fw.write("TEST");
      fw.flush();
    } catch(IOException e) {
      e.printStackTrace();
    } finally {
      try {
        fw.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

You can use the FileWriter to output character data to a file. In the FileWriter's constructor, the first argument is the destination, and the second argument is a flag that determines whether to keep the existing contents in the file. If true, leave the current contents intact.

It is important to close the output stream.

fw.close();

This code closes the output stream.

SubtitleToText.java
package net.java_school.stream;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class SubtitleToText {
	
  public static void main(String[] args) throws IOException {
    FileReader fr = null;
    FileWriter fw = null;
    BufferedReader br = null;
    BufferedWriter bw = null;
		
    if (args.length >= 2) {
      try {
        fr = new FileReader(args[0]);
        br = new BufferedReader(fr);
        fw = new FileWriter(args[1], false);
        bw = new BufferedWriter(fw);
        String s = null;
        while ((s = br.readLine()) != null) {
          try {
            Integer.parseInt(s);
          } catch (NumberFormatException e) {
            if (!s.contains("->")) {
              bw.write(s);
              bw.newLine();
            }
          }
        }
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        try {
          if (fr != null) {
            fr.close();
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
        try {
          if (br != null) {
            br.close();
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
        try {
          if (bw != null) {
            bw.close();
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
        try {
          if (fw != null) {
            fw.close();
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  } 
}
URLTest.java
package net.java_school.stream;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;

public class URLTest {

  public static void main(String[] args) {
    String url = "http://www.java-school.net";
    String file = "index.html";
    InputStream in = null;
    BufferedReader br = null;
    FileOutputStream fos = null;
    BufferedWriter bw = null;
    try {
      in = (new URL(url)).openStream();
      br = new BufferedReader(new InputStreamReader(in));
      fos = new FileOutputStream(file);
      bw = new BufferedWriter(new OutputStreamWriter(fos));
      String str = null;
      while ((str = br.readLine()) != null) {
        bw.write(str);
        bw.newLine();
      }
    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (br != null) {
        try {
          br.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (bw != null) {
        try {
          bw.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }
}

Object Stream

Passing an object through a file or network needs serialization. Also, reconstructing an object from a file or an object stream passed over the network requires deserialization. The below example stores object information in a file and create an object from the file where object information is stored.

Address.java
package net.java_school.serial;

import java.io.Serializable;

public class Address implements Serializable {

  private String mobile;
  private String address;
	
  public String getMobile() {
    return mobile;
  }
  public void setMobile(String mobile) {
    this.mobile = mobile;
  }
  public String getAddress() {
    return address;
  }
  public void setAddress(String address) {
    this.address = address;
  }
}

If the Address object is serialized and stored in a file through a stream, the Address class must declare that it implements the Serializable interface.

public class Address implements Serializable

The Serializable interface does not have any method to implement.
The Serializable interface only tells the virtual machine that it may have to serialize objects created from the class. Such an interface is called a declarative interface.

Primitive data types in Java do not need serialization. Many popular classes, such as String and Collection, are serializable. These classes declare that they implement Serializable in their class declaration. Similarly, if you create a class that JVM may have to serialize, you must declare that it implements Serializable in the class declaration.

Backup.java
package net.java_school.serial;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Backup {

  public static void main(String[] args) {
    ObjectOutputStream out = null;
    try {
      out = new ObjectOutputStream(new FileOutputStream("address.txt"));
      Address addr = new Address();
      addr.setMobile("212-963-4475");
      addr.setAddress("760 United Nations Plaza, Manhattan, New York City");
      out.writeObject(addr);
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        out.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

The Backup class creates an Address object and stores it in a file. -- It used ObjectOutputStream to generate the object stream and FileOutputStream to store the Address object in the file --

The following example creates an object from the object information stored in a file.

Recovery.java
package net.java_school.serial;

import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Recovery {

  public static void main(String[] args) {
    ObjectInputStream in = null;
    try {
      in = new ObjectInputStream(new FileInputStream("address.txt"));
      while(true) {
        Address addr = (Address) in.readObject();
        System.out.println(addr.getMobile());
        System.out.println(addr.getAddress());
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (EOFException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } finally {
      try {
        in.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

The Recovery class creates an object of the same content from the object information stored in the address.txt file and puts it in the heap memory. -- It uses a FileInputStream to read bytes from a file. This example uses an ObjectInputStream for an object stream coming into a Java program --

Edit Javabank Example to use Java I/O

Create and test a new class called BankUi, as shown below.

package net.java_school.bank;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class BankUi{
	
  public static void main(String[] args) throws IOException {
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);
    String input = br.readLine();
    System.out.println("Input: " + input);
  }
}

TODO

Change the BankUi as shown below and implement the TODO part.

package net.java_school.bank;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class BankUi {

  private Bank bank = new MyBank();

  private String readCommandLine() throws IOException {
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);
    String input = br.readLine();
    return input;
  }

  public void startWork() {
    String menu = null;

    do {
      System.out.println(" ** Menu ** ");
      System.out.println(" 1 ** Bank account registration    ");
      System.out.println(" 2 ** List of bank accounts    ");
      System.out.println(" 3 ** Deposit    ");
      System.out.println(" 4 ** Withdrawal    ");
      System.out.println(" 5 ** Transfer    ");
      System.out.println(" 6 ** Transaction history    ");
      System.out.println(" q ** Exit    ");
      System.out.println(" ********************** ");
      System.out.print(">>");
            
      try {
        menu = readCommandLine();
        if (menu.equals("1")) {
          //TODO Bank account registration
        } else if (menu.equals("2")) {
          //TODO List of bank accounts
        } else if (menu.equals("3")) {
          //TODO Deposit
        } else if (menu.equals("4")) {
          //TODO Withdrawal
        } else if (menu.equals("5")) {
          //TODO Transfer
        } else if (menu.equals("6")) {
          //TODO Transaction history
        }
      } catch (Exception e) {
          e.printStackTrace();
      }
      System.out.println();
    } while (!menu.equals("q"));
  }

  public static void main(String[] args) {
    BankUi ui = new BankUi();
    ui.startWork();
  }
}

Implement all //TODO parts and test them thoroughly. The Java bank program has evolved into an interactive program, but accounts and transaction history disappear once the program ends. You can save the account information and transaction history in the following ways:

  1. Save the bank object to a file
  2. Save the account info and transaction history to a text file
  3. Save the account info and transaction history to a database

1. Save a bank object to a file

JVM can save only serializable objects to a file.
Modify the Bank, Account, and Transaction classes, as shown below.

import java.io.Serializable;
public interface Bank extends Serializable
public abstract class Account implements Serializable
public class Transaction implements Serializable

The scenario is as follows. When the program starts, it de-serializes the bank object information stored in the file named bank.ser and loads the bank object into the heap memory. When you run the program for the first time, FileNotFoundException occurs because of the bank.ser file does not exist. When a FileNotFoundException occurs, call the bank class's constructor to create the bank object and assign the bank reference to the variable named bank. At the end of the program, JVM saves the bank object information in the bank.ser file.

Add the following method to the BankUi class.

public void loadBank() throws Exception {
  try {
    ObjectInputStream in = null;
    in = new ObjectInputStream(new FileInputStream("bank.ser"));
    bank = (Bank) in.readObject();
    in.close();
  } catch (FileNotFoundException e) {
    bank = new MyBank();
  } 
}
public void saveBank() throws Exception {
  ObjectOutputStream out = null;
  out = new ObjectOutputStream(new FileOutputStream("bank.ser"));
  out.writeObject(bank);
  out.close();
}
BankUi.java
private Bank bank;

public BankUi() throws Exception {
  loadBank();
}

public static void main(String[] args) throws Exception {
  BankUi ui = new BankUi();
  ui.startWork();
  ui.saveBank();
}

2. Save the account info and transaction history to a text file.

Before looking at the code hints for saving to a text file, let's first look at the File class. What you can do with the File class is as follows.

  • See the contents of the directory.
  • Get the attributes of the file.
  • Rename or delete the file.

Note that the File class does not have I/O capabilities for files.

File dir = new File(path);

The above code snippet creates a file object. The file or directory corresponding to the path must be the full path of the system.

The following are the essential methods of the File class.

File
isDirectory():boolean
dir.isDirectory(); //Returns true if dir is a directory.
isFile():boolean
dir.isFile(); //Returns true if dir is a file.
list():String[]
dir.list(); //If dir is a directory, return the file names in the directory as String[].
listFiles():File[]
dir.listFiles(); //Returns an array of file objects for files in the directory.
mkdir():boolean
dir.mkdir(); //Create a directory with the name of the File object.
getName():String
Return file name.
getPath():String
Return path.
delete():boolean
Delete file.
exists():boolean
Returns true if the file or directory exists, false if not.

For more information, see https://docs.oracle.com/javase/8/docs/api/java/io/File.html

JVM will store the account information in a file named accounts.txt. The format of the stored account information is as follows.

101|Alison|10000|Normal
202|Bill|5000|Normal
303|Carol|0|Normal
404|Alison|0|Minus

Open the Account.java file and modify the toString() method as follows:

@Override
public String toString() {
  StringBuffer sb = new StringBuffer();
  sb.append(accountNo);
  sb.append("|");		
  sb.append(name);
  sb.append("|");
  sb.append(balance);
  sb.append("|");
  sb.append(getKind());

  return sb.toString();
}

JVM will store the transaction history in a file with the same name as the account number. The format of the transaction history information stored is as follows.

2014/5/1|09:33:30|Deposit|10|10
2014/5/3|09:33:30|Withdrawal|6|4

Open the Transaction.java file and modify the toString() method as follows:

@Override
public String toString() {
  StringBuilder sb = new StringBuilder();
  sb.append(transactionDate);
  sb.append("|");
  sb.append(transactionTime);
  sb.append("|");
  sb.append(kind);
  sb.append("|");
  sb.append(amount);
  sb.append("|");
  sb.append(balance);

  return sb.toString();
}

Open BankUi.java and modify the code referring to the following.

static final String ACCOUNT_FILE = "accounts.txt";
static final String DATA_DIR = "./data/";

private Bank bank = new MyBank();
/*	
public BankUi() throws Exception {
  loadBank();
}
*/	

public static void main(String[] args) throws Exception {
  BankUi ui = new BankUi();
  ui.startWork();
  //ui.saveBank();
}

When the program starts, it reads the account information from the accounts.txt file and loads the account object. If you work in Eclipse, you need to create a data folder in the project root directory and create an empty file named accounts.txt in the folder.

FileReader fr = null;
fr = new FileReader(DATA_DIR + BankUi.ACCOUNT_FILE);
BufferedReader br = new BufferedReader(fr);
String str = null;

while ((str = br.readLine()) != null) {
  StringTokenizer st = new StringTokenizer(str, "|");
  String accountNo = st.nextToken();
  String name = st.nextToken();
  double balance = Double.parseDouble(st.nextToken());
  String kind = st.nextToken();

  if (kind.equals(NormalAccount.KIND)) {
    bank.addAccount(accountNo, name, balance, NormalAccount.KIND);
  } else if (kind.equals(MinusAccount.KIND)) {
    bank.addAccount(accountNo, name, balance, MinusAccount.KIND);
  }
}

br.close();

The program loads the transaction history object from each transaction history file and binds it with the account object when it starts.

File dir = new File(BankUi.DATA_DIR);
File[] files = dir.listFiles();
FileReader fr = null;
BufferedReader br = null;

for (File file : files) {
  if (file.isFile()) {

    String fileName = file.getName();

    if (!fileName.equals(BankUi.ACCOUNT_FILE)) {
      fr = new FileReader(BankUi.DATA_DIR + fileName);
      br = new BufferedReader(fr);
      List<Transaction> transactions = new ArrayList<Transaction>();
      String str = null;

      while ( (str = br.readLine()) != null) {
        StringTokenizer st = new StringTokenizer(str, "|");
        String day = st.nextToken();
        String time = st.nextToken();
        String kind = st.nextToken();
        double amount = Double.parseDouble(st.nextToken());
        double balance = Double.parseDouble(st.nextToken());
        transactions.add(new Transaction(day,time,kind,amount,balance));
      }

      Account account = bank.getAccount(fileName);

      if (account != null) {
        account.setTransactions(transactions);
      }
    }
  }
} 		

The program saves the account and transaction details when it ends.

StringBuilder sbForAccounts = new StringBuilder();
List<Account> accounts = bank.getAccounts();

for (Account account : accounts) {
  sbForAccounts.append(account);
  sbForAccounts.append(System.getProperty("line.separator"));
  StringBuilder sbForTransactions = new StringBuilder();
  List<Transaction> transactions = account.getTransactions();

  for (Transaction transaction : transactions) {
    sbForTransactions.append(transaction);
    sbForTransactions.append(System.getProperty("line.separator"));
  }

  FileWriter fw = new FileWriter(BankUi.DATA_DIR + account.getAccountNo(), false);
  BufferedWriter bw = new BufferedWriter(fw);
  bw.write(sbForTransactions.toString());
  bw.close();
}

FileWriter fw = null;
BufferedWriter bw = null;
fw = new FileWriter(DATA_DIR + BankUi.ACCOUNT_FILE, false);
bw = new BufferedWriter(fw);
bw.write(sbForAccounts.toString());
bw.close();		

Saving to the database is described in the JDBC chapter.

BankUi.java
package net.java_school.bank;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class BankUi {

  static final String ACCOUNT_FILE = "accounts.txt";
  static final String DATA_DIR = "./data/";
	
  private Bank bank = new MyBank();
  /*	
  public BankUi() throws Exception {
    loadBank();
  }
  */	
  private String readCommandLine() throws IOException {
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);
    String input = br.readLine();
    return input;
  }
	
  public void startWork() {
    String menu = null;
      do {
        System.out.println(" ** Menu ** ");
        System.out.println(" 1 ** Bank account registration    ");
        System.out.println(" 2 ** List of bank accounts    ");
        System.out.println(" 3 ** Deposit    ");
        System.out.println(" 4 ** Withdrawal    ");
        System.out.println(" 5 ** Transfer    ");
        System.out.println(" 6 ** Transaction history    ");
        System.out.println(" q ** Exit    ");
        System.out.println(" ********************** ");
        System.out.print(">>");
        try {
          menu = readCommandLine();
          String accountNo = null;
          String name = null;
          String kind = null;
          double amount = 0;
          if (menu.equals("1")) {
            //TODO Bank account registration
            System.out.print("Please enter the number of the account to be created: ");
            accountNo = this.readCommandLine();
            System.out.print("Please enter the owner name of the account to be created: ");
            name = this.readCommandLine();
            System.out.print("Please select the account type to be created. Normal(just Enter), Minus(2): ");
            kind = this.readCommandLine();
            if (kind != null && kind.equals("2")) {
              bank.addAccount(accountNo, name, MinusAccount.KIND);
            } else {
              bank.addAccount(accountNo, name, NormalAccount.KIND);
            }
          } else if (menu.equals("2")) {
            //TODO List of bank accounts
            List<Account> accounts = bank.getAccounts();
            for (Account account : accounts) {
              System.out.println(account);
            }
          } else if (menu.equals("3")) {
            //TODO Deposit
            System.out.print("Please enter your account number: ");
            accountNo = this.readCommandLine();
            System.out.print("Please enter deposit amount: ");
            amount = Integer.parseInt(this.readCommandLine());
            Account account = bank.getAccount(accountNo);
            account.deposit(amount);
          } else if (menu.equals("4")) {
            //TODO Withdrawal
            System.out.print("Please enter your account number: ");
            accountNo = this.readCommandLine();
            System.out.print("Please enter deposit amount: ");
            amount = Integer.parseInt(this.readCommandLine());
            Account account = bank.getAccount(accountNo);
            account.withdraw(amount);
          } else if (menu.equals("5")) {
            //TODO Transfer
            System.out.print("Please enter your account number: ");
            String from = this.readCommandLine();
            System.out.print("Please enter the account number you wish to transfer: ");
            String to = this.readCommandLine();
            System.out.print("Enter transfer amount: ");
            amount = Integer.parseInt(this.readCommandLine());
            Account fromAccount = bank.getAccount(from);
            Account toAccount = bank.getAccount(to);
            fromAccount.withdraw(amount);
            toAccount.deposit(amount);	
          } else if (menu.equals("6")) {
            //TODO Transaction history
            System.out.print("Please enter your account number: ");
            accountNo = this.readCommandLine();
            Account account = bank.getAccount(accountNo);
            List<Transaction> transactions = account.getTransactions();
            for (Transaction transaction : transactions) {
              System.out.println(transaction);
            }
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
        System.out.println();
      } while (!menu.equals("q"));
  }
	
  public void loadBank() throws Exception {
    try {
      ObjectInputStream in = null;
      in = new ObjectInputStream(new FileInputStream("bank.ser"));
      bank = (Bank) in.readObject();
      in.close();
    } catch (FileNotFoundException e) {
      bank = new MyBank();
    }
  }
	
  public void saveBank() throws Exception {
    ObjectOutputStream out = null;
    out = new ObjectOutputStream(new FileOutputStream("bank.ser"));
    out.writeObject(bank);
    out.close();
  }
	
  public void readAccounts() throws Exception {
    FileReader fr = null;
    fr = new FileReader(DATA_DIR + BankUi.ACCOUNT_FILE);
    BufferedReader br = new BufferedReader(fr);
    String str = null;
		
    while ( (str = br.readLine()) != null) {
      StringTokenizer st = new StringTokenizer(str, "|");
      String accountNo = st.nextToken();
      String name = st.nextToken();
      double balance = Double.parseDouble(st.nextToken());
      String kind = st.nextToken();
			
      if (kind.equals(NormalAccount.KIND)) {
        bank.addAccount(accountNo, name, balance, NormalAccount.KIND);
      } else if (kind.equals(MinusAccount.KIND)) {
        bank.addAccount(accountNo, name, balance, MinusAccount.KIND);
      }
    }
    br.close();
  }
	
  public void readTransactions() throws Exception {
    File dir = new File(BankUi.DATA_DIR);
    File[] files = dir.listFiles();
    FileReader fr = null;
    BufferedReader br = null;
		
    for (File file : files) {
      if (file.isFile()) {
        String fileName = file.getName();
        if (!fileName.equals(BankUi.ACCOUNT_FILE)) {
          fr = new FileReader(BankUi.DATA_DIR + fileName);
          br = new BufferedReader(fr);
          List<Transaction> transactions = new ArrayList<Transaction>();
          String str = null;
          while ( (str = br.readLine()) != null) {
            StringTokenizer st = new StringTokenizer(str, "|");
            String day = st.nextToken();
            String time = st.nextToken();
            String kind = st.nextToken();
            double amount = Double.parseDouble(st.nextToken());
            double balance = Double.parseDouble(st.nextToken());
            transactions.add(new Transaction(day,time,kind,amount,balance));
          }
          Account account = bank.getAccount(fileName);
          if (account != null) {
            account.setTransactions(transactions);
          }
        }
      }
    } 		
  }
	
  public void readAll() throws Exception {
    readAccounts();
    readTransactions();
  }
	
  public void writeAll() throws Exception {
    StringBuilder sbForAccounts = new StringBuilder();
    List<Account> accounts = bank.getAccounts();
		
    for (Account account : accounts) {
      sbForAccounts.append(account);
      sbForAccounts.append(System.getProperty("line.separator"));
      StringBuilder sbForTransactions = new StringBuilder();
      List<Transaction> transactions = account.getTransactions();
			
      for (Transaction transaction : transactions) {
        sbForTransactions.append(transaction);
        sbForTransactions.append(System.getProperty("line.separator"));
      }
			
      FileWriter fw = new FileWriter(BankUi.DATA_DIR + account.getAccountNo(), false);
      BufferedWriter bw = new BufferedWriter(fw);
      bw.write(sbForTransactions.toString());
      bw.close();
    }
		
    FileWriter fw = null;
    BufferedWriter bw = null;
    fw = new FileWriter(DATA_DIR + BankUi.ACCOUNT_FILE, false);
    bw = new BufferedWriter(fw);
    bw.write(sbForAccounts.toString());
    bw.close();		
  }
	
  public static void main(String[] args) throws Exception {
    BankUi ui = new BankUi();
    ui.readAll();
    ui.startWork();
    ui.writeAll();
    //ui.saveBank();
  }
}