Background
As we saw in our last post byte stream classes are derived from
InputStream and
OutputStream classes. Similarly, for character stream all classes descend from
Reader and
Writer classes.
We generally use character streams for I/O so i would recommend to understand this post very thoroughly. Byte streams are used only for low level I/O operations.
Background
Similar to
byte streams we will primarily focus on File I/O streams.Classes for the same are
FileReader and
FileWriter.We will also discuss reading from standard Input.
In
last post we wrote a code and then said that it was not the perfect way to write it.We discussed what might be a better way and why do we have to close the streams.So like the last example code we will not let our
main() function throw any exception.
We will catch it and then close the stream in finally block. Though we have not explained
Exception Handling this would be a basic code to start with.There will be an entire post on Exception Handling. So
try to understand the logic behind writing the following code than to understand Exceptions.We will write this code more systematically.
Example code
public class CharacterStreamDemonstrator {
public static void main(String[] args) {
FileReader reader = null;
FileWriter writer = null;
try {
try {
reader = new FileReader("inFile.txt");
writer = new FileWriter("outFile.txt");
int readStatus;
while ((readStatus = reader.read()) != -1) {
writer.write(readStatus);
}
} finally {
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
}
} catch (FileNotFoundException e) {
System.out.println("File could not be found \n");
e.printStackTrace();
} catch (IOException e) {
System.out.println("IO Exception occured \n");
e.printStackTrace();
}
}
}
Code Analysis and Explanation
I repeat do not get confused looking at the try-catch-finally statements.Though this is one of the proper way to write the code our aim in this post is to understand character streams though i will try my best to explain entire code in as generic was as possible.
Lets go line by line.First we define FileReader and FileWriter objects.
Why are FileReader and FileWriter objects initialized to null?
These objects are
local variables (not instance variables) and if you recall the
post which explained difference between local and instance variables, compiler will complaint if you try to use local variables without initializing.So we have initialized it to null.
Reading and writing logic is very much similar what we used in byte stream except that we read and write character at a time rather that a byte at a time.Now let me explain what are those try-catch-finally statements doing in the code.
Understanding try-catch-finally statements
If you observe the code carefully we have try-finally statement inside try-catch statement.Let see what both of them actually do.
The inner try-finally statements execute the reading.writing logic and finally close the streams.Also recall from the last post why do we check the stream for null before closing them.
The outer try-catch statement will execute everything in it's try block which is nothing but our inner try-finally block and then catch any Exceptions that may occur in the try block. e.printStackTrace() will print the cause of exception in an sequential manner like what caused the exception or what exception cause this exception to occur.
So basically what your try-catch statement does is execute something in try block and catch if any exception arises in try block with catch block.
Note : Compiler will not compile unless you either throw or catch any possible exceptions that might occur in your code (such exceptions are called checked exceptions). Java is designed that way to reduce the errors and increase security.
Understanding finally statement
The reason i am explaining finally statement separately than try-catch-finally statement is that finally block has special importance and there are various thing you need to know about it.Note that finally block cannot exist on it's own. You must have either try-catch-finally syntax or try-finally syntax.
finally block will execute regardless of whether Exception has occurred or not. So we use it to close our steams. So regardless of what happens our streams will be closed.Note that there is one case in which finally block will not be executed and that is if you say System.exit() but except that finally block will always be executed even if there is a return statement in try block.
However it does not make sense to read and write one character at a time. Can we read a line at a time?
Yes! of course you can. We have BufferedReader and PrintWriter/BufferedWriter for the same purpose.Think of these as wrappers around our Reader and Writer classes. As the name suggests it buffers characters ahead of time and reads/write a line at a time.
Lets see code to do so.I will not write the entire code again because the structure remains the same.All we have to change is our reading/writing logic.
BufferedReader reader = new BufferedReader(new FileReader("inFile.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("outFile.txt"));
String readLine;
while ((readLine = reader.readLine()) != null) {
writer.write(readLine);
}
One of the difference to mention here is that as we are reading line we compare it with null to check if all the data from input stream is read.You can also use PrintWriter instead of BufferedWriter. Difference is that PrintWriter flushes the data every time we write whereas in BufferedWriter data is flushed when it encounters '\n' i.e new line or we explicitly say so. Note this concept of flushing comes into picture only when streams are buffered, not otherwise.
Reading from standard input.
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String readLine = reader.readLine();
I think the code is pretty straightforward and easy to understand. Only thing worth mentioning in this is System.in which just tell your input stream must come from standard input.
Note : character stream internally uses byte stream only.
In our next post we will see another way to read data from various streams and that is by using
scanner class.This was one of the important tutorials in Java, so let me know if there is any doubt in any part explained above.
Related Links