Hi! Today's lesson will be divided into two parts for convenience. We'll repeat some old topics that we touched on previously, and we'll consider some new features :)
Let's start with the first one.
You've already a class like
BufferedReader
many times. I hope you haven't had time to forget this statement:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Before reading further, try to remember what each component — System.in
, InputStreamReader
, BufferedReader
— is responsible for and why it is needed.
Did you remember?
If not, no worries. :) If you've forgotten something, reread this lesson, which is dedicated to reader classes.
We'll briefly recall what each of them can do.
System.in
— this is a stream for receiving data from the keyboard. In principle, it alone would be enough to implement the logic required to read text. But, as you will recall, System.in
can only read bytes, not characters:
public class Main {
public static void main(String[] args) throws IOException {
while (true) {
int x = System.in.read();
System.out.println(x);
}
}
}
If we execute this code and enter the Cyrillic letter "Й", the output will be:
Й
208
153
10
Cyrillic characters occupy 2 bytes in memory, and they are displayed on the screen. The number 10 is the decimal representation of a line feed character, i.e. from pressing Enter.
Reading bytes is such a pleasure, so using System.in
isn't very convenient. In order to correctly read Cyrillic (and other) letters, we use InputStreamReader
as a wrapper:
public class Main {
public static void main(String[] args) throws IOException {
InputStreamReader reader = new InputStreamReader(System.in);
while (true) {
int x = reader.read();
System.out.println(x);
}
}
}
We enter the same letter "Й", but the result is different this time:
Й
1049
10
InputStreamReader
converted two bytes (208 and 153) to the single number 1049. This is what it means to read characters. 1049 corresponds to the Cyrillic letter "Й". We can easily convince ourselves that this is true:
public class Main {
public static void main(String[] args) throws IOException {
char x = 1049;
System.out.println(x);
}
}
Console output:
Й
And as forBufferedReader
(and in general, BufferedAnythingYouWant
), buffered classes are used to optimize performance.
Accessing a data source (file, console, web resource) is quite expensive in terms of performance. Therefore, in order to reduce the number of accesses, BufferedReader
reads and accumulates data in a special buffer, and we get it from there.
As a result, the number of times the data source is accessed is slashed — possibly by several orders of magnitude!
Another one of BufferedReader
's features and its advantage over the ordinary InputStreamReader
, is the extremely helpful readLine()
method, which reads entire lines of data, not individual numbers. This, of course, is super convenient when dealing with large texts.
Here's what reading lines looks like:
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = reader.readLine();
System.out.println ("The user entered the following text:");
System.out.println(s);
reader.close();
}
}
BufferedReader+InputStreamReader is faster than InputStreamReader alone
The user entered the following text:
BufferedReader+InputStreamReader is faster than InputStreamReader alone
Of course, BufferedReader
is very flexible. You're not limited to working with the keyboard.
For example, you can read data directly from the web, simply by passing the required URL to a reader:
public class URLReader {
public static void main(String[] args) throws Exception {
URL oracle = new URL("https://www.oracle.com/index.html");
BufferedReader in = new BufferedReader(
new InputStreamReader(oracle.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
You can read data from a file by passing the file path:
public class Main {
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream = new FileInputStream("testFile.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream));
String str;
while ((str = reader.readLine()) != null) {
System.out.println (str);
}
reader.close();
}
}
Replacing System.out
Now let's take a look at an interesting capability that we haven't touched on before. As you surely remember, theSystem
class has two static fields — System.in
and System.out
.
These twin brothers are stream objects. System.in
is an InputStream
. And System.out
is a PrintStream
.
Right now, we'll talk about System.out
. If we drop into the System
class's source code, we see this:
public final class System {
……………...
public final static PrintStream out = null;
…………
}
Thus, System.out
is simply an ordinary static variable of the System
class. There's nothing magic about it :)
The out
variable is a PrintStream
reference.
Here's an interesting question: When System.out.println()
is executed, why exactly does the output go to the console and not somewhere else? And can this be changed somehow?
For example, suppose we want to read data from the console and write it to a text file. Is it possible to somehow implement this simply using System.out
rather than additional reader and writer classes?
Indeed, it is :)
And we can do it even though the System.out
variable is marked with the final
modifier!
So what do we need to make this happen?
First of all, we need a new PrintStream
object to replace the current one. The current object, set in the System
class by default, doesn't serve our purposes: it points to the console. You need to create a new one that points to a text file — the "destination" for our data.
Second, we need to understand how to assign a new value to the System.out
variable. You can't use a simple assignment operator, because the variable is marked final
.
Let's work backwards from the end.
As it happens, the System
class has the method we need: setOut()
. It takes a PrintStream
object and sets it as the destination for output. That's just what we need!
All that remains is to create a PrintStream
object.
This is also easy:
PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
The full code will look like this:
public class SystemRedirectService {
public static void main(String arr[]) throws FileNotFoundException
{
PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
/* Save the current value of System.out in a separate variable so that later
we can switch back to console output */
PrintStream console = System.out;
// Assign a new value to System.out
System.setOut(filePrintStream);
System.out.println("This line will be written to the text file");
// Restore the old value of System.out
System.setOut(console);
System.out.println("But this line will be output to the console!");
}
}
As a result, the first string is written to the text file, and the second is displayed in the console :)
You can copy this code into your IDE and run it. Open the text file and you'll see that the string has been successfully written there :)
With this, our lesson has come to the end. Today we recalled how to work with streams and readers. We recollected how they differ from one another and learned about some new capabilities of System.out
, which we've used in almost every lesson :)
Until the next lessons!
More reading: |
---|
GO TO FULL VERSION