Hello! In today's lecture we will continue the conversation about input and output streams in Java, or Java I/O (“input-output”) for short. This is not the first lecture on this topic, and it will not be the last :) It just so happens that Java as a language provides many opportunities for working with input/output. There are quite a lot of classes that implement this functionality, so we divided them into several lectures so that you don’t get confused at first :) In previous lectures we touched on BufferedReader , as well as the abstract classes InputStream & OutputStream and several descendants. Today we will look at 3 new classes: FileInputStream , FileOutputStream and BufferedInputStream .
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 abstract OutputStream class . In the constructor, objects of this class take either the path to the target file (to which the bytes need to be written) or an object of the classFile
. Let's look at both examples:
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 JavaRush - the best site for those who want to become a programmer!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
When creating an object, File
we specified in the constructor the path where it should be located. There is no need to create it in advance: if it does not exist, the program will create it itself. You can do without creating an extra object and just pass a string with the address:
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 JavaRush - the best site for those who want to become a programmer!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
The result in both cases will be the same. We can open our file and see there:
Hello! Добро пожаловать на JavaRush — лучший сайт для тех, кто хочет стать программистом!
However, there is one caveat here. Try running the code from the example above several times in a row, and then look at the file and answer the question: how many lines do you see written in it? Just one. But you ran the code several times. However, it turns out that the data was overwritten each time, replacing the old ones. What if we are not satisfied with this and need sequential recording? What if we want to write our greeting to a file three times in a row? Everything is simple here. Since the language itself cannot know what kind of behavior we need in each case, FileOutputStream
you can pass an additional parameter to the constructor - boolean append
. If its value is true , the data will be written to the end of the file. If false (and the default value is false ), old data will be erased and new data will be written. Let's test and run 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 JavaRush - the best site for those who want to become a programmer!\r\n";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
Result in file:
Hello! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!
Hello! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!
Hello! Добро пожаловать на JavaRush - лучший сайт для тех, кто хочет стать программистом!
Another thing! Keep this feature in mind when using I/O classes. At one time, I had to sit for hours on tasks to understand where my old data went from the files :) And of course, as in the case of other I/O classes, do not forget about releasing resources through the close()
.
FileInputStream class
The class hasFileInputStream
the opposite purpose - reading bytes from a file. Just like FileOutputStream
inherits OutputStream
, this class derives from the abstract class InputStream
. Let's write several lines of text into our text “ test.txt ”:
«So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters»
This is what the implementation of reading 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 output them to the console. And here is the result in the console:
So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters
BufferedInputStream class
I think, given the knowledge from previous lectures, you can easily tell why the class is neededBufferedInputStream
and what advantages it has over FileInputStream
:) We have already met with buffered streams, so try to guess (or remember) before continuing reading :) Buffered streams are needed primarily to optimize I/O. Accessing a data source, such as reading from a file, is a performance-intensive operation. And accessing the file to read one byte each time is wasteful. Therefore, BufferedInputStream
it reads data not one byte at a time, but in blocks and temporarily stores them in a special buffer. This allows us to optimize the operation of the program by reducing the number of accesses to the file. Let's see what it 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 have created an object BufferedInputStream
. It accepts an object or any of its successors as input InputStream
, so the previous one FileInputStream
will do. It takes the buffer size in bytes as an additional parameter. Now, thanks to this, data will be read from the file not one byte at a time, but 200 at a time! Imagine how much we have reduced the number of file accesses. To compare performance, you can take some large text file several megabytes in size and compare how long it takes to read it and output it to the console in milliseconds using FileInputStream
and BufferedInputStream
. Here are both examples of code:
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
it did the job in ~3500 milliseconds, but here BufferedInputStream
it did the job in ~1700 milliseconds. As you can see, the buffered stream optimized the program's performance by 2 times! :) We will continue to study I/O classes - see you soon!
GO TO FULL VERSION