Java IO

Yes, there are so many different Stream classes. (Now I am talking about IO Streaming)

Here are some tips to keep them apart:

  • All these classes inherit (in)directly from on of the following four abstract Classes
    • InputStream
    • OutputStream
    • Reader
    • Writer
  • ! You cannot instantiate one of the above classes since they are all abstract
  • If the classname contains InputStream or OutputStream then the class is used to read or write binary data
  • If the classname contains Reader or Writer then the class is used to handle reading and writing of String of character data directly
  • We make a difference between low-level and high-level streams:
    • low-level streams operate  directly on the source of the data
    • high-level streams take another stream as constructor argument. They are wrapping another stream
  • If the classname of a stream starts with the word  File, than it is a low-level stream eg. :
    • FileInputStream
    • FileOutputStream
    • FileReader
    • FileWriter
  • Buffered streams read and write data in larger chunks


BufferedReader and BufferedWriter

Here are two examples of how to copy a File using BufferedReader and BufferedWriter.

The first example does it all in the main method. In the second example I use an intermediary List of String data to store the content of the file. This would allow for manipulation of the data before writing it to the other file.

public class CopyFileBufferedRW {

    public static void main(String[] args) throws IOException {
        File originalFile = new File("original.txt");
        File newFile = new File("new.txt");

        try (BufferedReader br = new BufferedReader(new FileReader(originalFile));
             BufferedWriter bw = new BufferedWriter(new FileWriter(newFile))) {
            String s;
            while ((s = br.readLine()) != null) {
public class CopyFileBufferedRW2 {

    private List<String> readFile(String source) throws IOException {
        try(BufferedReader br = new BufferedReader(new FileReader(source))){
            List<String> listOfData = new ArrayList<>();
            String d;
            while((d = br.readLine()) != null){
            return listOfData;

    private void writeFile(List<String> listOfData, String target ) throws IOException{
        try(BufferedWriter bw = new BufferedWriter(new FileWriter(target))){
            for(String str: listOfData){

    public static void main(String[] args) throws IOException {
        CopyFileBufferedRW2 copyMachine = new CopyFileBufferedRW2();
        List<String> data = copyMachine.readFile("original.txt");
        copyMachine.writeFile(data, "new.txt");



In this example I serialize a list of people. Watch how the transient field is gone after deserialization. The same goes for the static field race since its a class member and not an instance member.

public class UseObjectStreams {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person p1 = new Person("Nick", "nxtra", 18);
        Person p2 = new Person("Phil", "fictio", 19);
        Person p3 = new Person("Phoe", "fecteo", 20);
        List<Person> people = new ArrayList<>(Arrays.asList(p1, p2, p3));

        File dataFile = new File("./8_IO/src/main/resources/objects/people.data");

        writePeople(people, dataFile);
        List<Person> peopleAgain = getPeople(dataFile);

    static void writePeople(List<Person> people, File dataFile) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(
                new BufferedOutputStream(new FileOutputStream(dataFile)))) {
            for (Person person : people) {

    static List<Person> getPeople(File dataFile) throws IOException, ClassNotFoundException {
        List<Person> people = new ArrayList<>();
        try (ObjectInputStream in = new ObjectInputStream(
                new BufferedInputStream(new FileInputStream(dataFile)))) {
            while (true) {
                Object object = in.readObject();
                if (object instanceof Person) {
                    people.add((Person) object);
        } catch (EOFException e){}
        return people;

class Person implements Serializable {

    String name;
    transient String nickName; // will be null after deserialization
    int age;
    static String race = "Human";

    public Person(String name, String nickName, int age) {
        this.name = name;
        this.nickName = nickName;
        this.age = age;

    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                ", age=" + age +

! It is not necessary to wrap the FileInputStream in a BufferedInputStream but it will be more efficient.

! When deserializing the constructor and all default initializations are neglected.


Leave a Reply

Your email address will not be published.