Java中的Stream


流是一种通用工具,它们允许程序从任何地方接收数据(输入流),并将其发送到任何地方(输出流)。它们的任务是一样的:
从一个地方接收数据,将其发送到另一个地方


有两种类型的流:
输入流用于接收数据(InputStream)
输出流用于发送数据(OutputStream)
这些流由 InputStreamOutputStream类实现。除了输入流和输出流,还有字节流和字符流。字节流将信息作为一组字节发送,而字符流作为一组字符发送信息。


BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
上述代码中
System.in是一个InputStream对象,它是连接到系统输入设备的(键盘)的输入流。相对应的System.out是一个OutputStream对象,它可以通过System.out.println()将数据输出到控制台。
其中System.in可以很简单的从键盘获取输入的数据,而无需复杂的代码,甚至可以简写为System.in.read();

点击查看代码

public class TestCode{
	public static void main(String[] args) throws IOException {
		while(true){
			int x = System.in.read();
			System.out.println(x);
		}
	}
}

InputStream类(System.in是一个InputStream对象)有一个read()用来读取数据。它读取的是bytes,而不是characters
例如,我们输入汉字“鱼”,控制台会输出


233 173 154 10

汉字在计算机内存中占用3个字节(拉丁字母占用1个字节)。在这种情况下,从流中读取4个字节: 钱三个字节代表汉字“鱼”,其他字节代表换行(回车)。System.in简单的读取字节导致我们无法知道其真实的含义。但是InputStreamReader类可以解决这个问题
BufferedReader buffered = new BufferedReader(new InputStreamReader(System.in));
以上创建了一个InputStreamReader对象,并传递给它一个输入流(System.in),它将从中读取数据。在这种情况下new InputStreamReader(System.in)这段代码的含义是: 你将从系统输入流(键盘)中读取数据。但是这不是它唯一的功能,InputStreamReader不仅从流接收数据,它还将字节流转换为字符流。这时,我们不再需要将数据从0和1转换为人类可以读懂的语言,InputStreamReader可以帮我们做完这些。InputStreamReader不限于从控制台读取数据,它也可以从其他地方读取数据,例如从一个文件:

点击查看代码
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

   public static void main(String[] args) throws IOException {
       InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\username\\Desktop\\testFile.txt"));
   }
}

上面代码中,创建了一个FileInputStream (InputStream的子类),传入文件路径,并将流本身传递给InputStreamReader,现在它将能够从文件中读取数据(前提是指定的文件存在)。可以使用InputStreamReader类的read()方法来读取数据(不论数据来源是哪里)。


System.in.read()InputStreamReader.read()的区别:

方法 结果
System.in.read(); 233 177 188 10
new InputStreamReader(System.in).read() 40060 10

区别是很明显的。最后一个字节(代表新行),保持不变,但是字符“鱼”被转换为单个代码“40060”。这就是读取字符的含义。


为什么要使用BufferedReader?
InputStreamReader虽然可以读取数据并将字节转换为字符,但是为了性能方便,使用BufferedReader读取数据时,它使用一个被称为缓冲区的特殊区域,在其中“存储”读取的字符。最终,当程序需要使用这些字符时,它们将从缓冲区中获取,而不是直接从数据源(键盘,文件等)中获取。这样可以节省很多资源。
要了解这是如何工作的,请想象一下一家大公司的快递员。快递员在快递站点,等着有人来寄包裹。每次收到新包裹,他都立即上路。但是白天可能会有很多包裹。他将不得不在办公室和送货地址之间往返多次。
相反,快递员在他的办公室里放了一个箱子。每个人都把他们要寄的包裹放进箱子里。现在快递员可以一次从箱子里拿出需要送出的包裹,从一个地址送到另一个地址。这样可以节省很多时间,因为他不必每次都回到办公室。
在这个例子中,箱子只是一个缓冲区,快递站是一个数据源。快递员在送货时从一个箱子里取包裹比每次都回到快递站要容易得多。他也会省时间。类似地,在程序中,从缓冲区获取数据比每次都引用数据源所需的资源少得多。
结论: 与每次引用数据源相比,从缓冲区获取数据所需的资源要少得多。与每次引用数据源相比,从缓冲区获取数据所需的资源要少得多。所以,BufferedReader+InputStreamReaderInputStreamReader单独更快。

上面已经说了性能,那么便利性怎么样?
BufferedReader不仅可以读取一个字符(read()),还可以一次读取一整行数据,这是使用readLine()方法完成的。

点击查看代码
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("我们从键盘上读取到这句话:");
       System.out.println(s);
   }
}

输出结果:

我们从键盘上读取到这句话
我爱java