Input/output in Java. FileInputStream, FileOutputStream, and BufferedInputStream classes

Published in the Java Developer group
"Hi! In today's lesson, we'll continue our conversation about input and output streams in Java (Java I/O). This is not the first lesson on this topic, and it certainly won't be the last :) Input/output in Java. FileInputStream, FileOutputStream, and BufferedInputStream classes - 1As it happens, the Java language provides many ways to work with I/O. There are quite a few classes that implement this functionality, so we've divided them into several lessons — so you won't get confused from the start :) In past lessons, we touched on BufferedReader, as well as the InputStream and OutputStream abstract classes and several descendants. Today we'll consider 3 new classes: FileInputStream,  FileOutputStream, and BufferedInputStream.

The FileOutputStream class

The main purpose of the FileOutputStream class is to write bytes to a file. Nothing complicated :) FileOutputStream is one of the implementations of the OutputStream abstract class. In the constructor, objects of this class take either the path to the target file (where the bytes should be written) or a File object. We'll examine examples of each:

public class Main { 

   public static void main(String[] args) throws IOException { 

       File file = new File("C:\\Users\\Username\\Desktop\\test.txt"); 
       FileOutputStream fileOutputStream = new FileOutputStream(file); 

       String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!"; 

       fileOutputStream.write(greetings.getBytes()); 
       fileOutputStream.close(); 
   } 
}
When creating the File object, we passed the desired path to the constructor. We don't need to create it in advance: if it doesn't exist, the program will create it. You can also get by without creating an extra object, simply passing a string with the path:

public class Main { 

    public static void main(String[] args) throws IOException { 

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt"); 
       String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!"; 

       fileOutputStream.write(greetings.getBytes()); 
       fileOutputStream.close(); 
   } 
} 
The result in both cases will be the same. We can open our file and see the following there:

Hi! Welcome to CodeGym — The best site for would-be programmers!
But there's one nuance here. Try running the code from the example above several times in a row. Then look in the file and answer this question: how many lines does it have? Just one. But you ran the code several times. It turns out that the data is overwritten every time — the old is replaced by the new. What do we do if that doesn't suit us and we need to write sequentially to the file? What if we want to write our greeting to a file three times in a row? It's all very simple. Since the language can't know what behavior we need in each case, the FileOutputStream contrucutor can take an additional parameter — boolean append. If its value is true, the data will be written to the end of the file. If it is false (and by default it is false), any old data will be erased and replaced by new data. Let's check this by running our modified code three times:

public class Main { 

   public static void main(String[] args) throws IOException { 

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt", true); 
       String greetings = "Hi! Welcome to CodeGym — The best site for would-be programmers!\r\n"; 

       fileOutputStream.write(greetings.getBytes()); 
       fileOutputStream.close(); 
   } 
} 
File contents:

Hi! Welcome to CodeGym — The best site for would-be programmers! 
Hi! Welcome to CodeGym — The best site for would-be programmers! 
Hi! Welcome to CodeGym — The best site for would-be programmers!
Now that's different! Don't forget about this feature when using I/O classes. There was a time when I spent hours on tasks, racking my brains for hours, trying to understand how my data was disappearing from files :) And of course, just as with other I/O classes, don't forget to use the close() method to free resources.

The FileInputStream class

The FileInputStream has the opposite purpose — reading bytes from a file. Just as FileOutputStream inherits OutputStream, this class derives from the InputStream abstract class. We'll write a few lines of text in our "test.txt" file:

"So close no matter how far 
Couldn't be much more from the heart 
Forever trusting who we are 
And nothing else matters"
Input/output in Java. FileInputStream, FileOutputStream, and BufferedInputStream classes - 2Here what it looks like to read data from a file using FileInputStream:

public class Main { 

   public static void main(String[] args) throws IOException { 

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt"); 

       int i; 

       while((i=fileInputStream.read())!= -1){ 

           System.out.print((char)i); 

       } 
   } 
}
We read one byte from the file, convert the read bytes into characters and display them on the console. And here's the console output:

So close no matter how far 
Couldn't be much more from the heart 
Forever trusting who we are 
And nothing else matters

The BufferedInputStream class

I think, given the knowledge from past lessons, you can easily say why we need the BufferedInputStream class and what advantages it has compared to FileInputStream :) We've already encountered buffered streams, so try to guess (or remember) before you continue reading :) Buffered streams are needed mainly to optimize I/O. Accessing a data source, such as reading from a file, is an expensive operation in terms of performance And to access a file to read each byte is wasteful. That's why BufferedInputStream reads data not one byte at a time, but in blocks, and temporarily stores them in a special buffer. This lets us optimize the program by reducing the number of times we access the file. Let's see what this looks like:

public class Main { 

   public static void main(String[] args) throws IOException { 

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\test.txt"); 

       BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream, 200); 

       int i; 

       while((i = bufferedInputStream.read())!= -1){ 

           System.out.print((char)i); 
       } 
   } 
} 
Here we created a BufferedInputStream object. Its constructor takes an instance of the InputStream class or any of its descendants, so FileInputStream will do. As an additional argument, it takes the buffer size in bytes. Thanks to this argument, the data will now be read from the file not one byte at a time, but 200 bytes at a time! Imagine how much we've reduced the number of file accesses. To compare performance, you can take a large text file (several megabytes of text) and compare how long it takes in milliseconds to read and output to the console using FileInputStream and BufferedInputStream. Here's code that demonstrates both options:

public class Main { 

   public static void main(String[] args) throws IOException { 

       Date date = new Date(); 

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\textBook.rtf"); 
       BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); 

       int i; 
       while((i = bufferedInputStream.read())!= -1){ 

           System.out.print((char)i); 
       } 

       Date date1 = new Date(); 
       System.out.println((date1.getTime() - date.getTime())); 
   } 
} 

 
public class Main { 

   public static void main(String[] args) throws IOException { 

       Date date = new Date(); 
       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\26951280.rtf"); 

       int i; 
       while((i = fileInputStream.read())!= -1){ 

           System.out.print((char)i); 
       } 


       Date date1 = new Date(); 
       System.out.println((date1.getTime() - date.getTime())); 
   }
}
When reading a 1.5 MB file on my computer, FileInputStream completed the work in ~3500 milliseconds, but BufferedInputStream managed it in ~1700 milliseconds. As you can see, the buffered stream optimized the work, cutting it in half! :) We will continue to study I/O classes — see you soon!
Comments (14)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Hoa Nguyen Level 28, Australia
17 February 2022
Excellent lesson! Very detailed! Very clear! Thank you! P/S: the more I learn from you, the more I find my university course on structured programming (using Java) a waste of money. But more importantly, the lack of clear explanation has made many students think that they are not capable of programming which is not true. The bad teaching cuts the dreams of many students short. And that cannot be measured by money.
Gellert Varga Level 23, Szekesfehervar, Hungary
2 July 2021
To use BufferedInputStream does not only halve the time, but reduces it by a much larger amount. The two programs above that compare the speed of the two streams unfortunately add the slowness of the print() method to the measurement, and thus greatly distorts the final result. Let's remove the print((char)i) method from the loop so that we can really only measure the speed of the read() methods. I read a 2.5MB (2.5 million bytes) file this way, and the result is: With FileInputStream: ~3100 ms, With BufferedInputStream: 110 ms. 30 times difference!
Aakankasha Sharma Level 18, Chandigarh, India
15 May 2021
while((i=fileInputStream.read())!= -1){ System.out.print((char)i); } read() returns only one byte, and we are trying to print a char. How is it printing the char properly, shouldn't it print a byte?
Karas Level 20, Tampa, United States
6 April 2021
"So close no matter how far Couldn't be much more from the heart Forever trusting who we are And nothing else matters" Sounds familiar somehow.... is it a poem? LOL!
Bodea Tudor Andrei Level 25, Cluj-Napoca, Romania
15 January 2021
At the start of the FIleInputStream where it says "We read one byte from the file, convert the read bytes into files and display them on the console." shouldn't it be "convert the read bytes intro chars" ?
Collin M Level 22, Jane Furse, South Africa
2 October 2020
Very good article, it has cleared some confusions I had with Java I/O
Ntuthuko Xaba Level 18, Johannesburg, South Africa
20 May 2020
For some reason all these examples don't seem to work in my IDE.I tried using a different filepath but got exceptions. Anyone else experiencing the same problem?
Ahmad Level 23, Riyadh, Saudi Arabia
5 May 2020
very simple and nice article