1.数据流
程序很少作为一个孤岛存在。程序通常以某种方式与“外部世界”交互。这可以通过从键盘读取数据、发送消息、从 Internet 下载页面,或者相反,将文件上传到远程服务器来实现。
我们可以用一个词来指代所有这些行为:程序与外界之间的数据交换。等等,这不仅仅是一个词。
当然,数据交换本身可以分为接收数据和发送数据两部分。例如,您使用Scanner
对象从键盘读取数据——这就是接收数据。然后您使用命令在屏幕上显示数据System.out.println()
——这就是发送数据。
在编程中,术语“流”用于描述数据交换。这个词是从哪里来的?
在现实生活中,你可以有一股水流,也可以有一股意识流。在编程中,我们有数据流。
流是一种多功能工具。它们允许程序从任何地方接收数据(输入流)并向任何地方发送数据(输出流)。因此,有两种类型:
- 输入流用于接收数据
- 输出流用于发送数据
为了使流“有形”,Java 的创建者编写了两个类:InputStream
和OutputStream
.
该类InputStream
有一个read()
方法可以让您从中读取数据。该类OutputStream
有一个write()
方法可以让你向它写入数据。他们还有其他方法,但稍后会详细介绍。
字节流
我们在谈论什么样的数据?它采用什么格式?换句话说,这些类支持哪些数据类型?
这些是通用类,因此它们支持最常见的数据类型 — byte
. AnOutputStream
可以写入字节(和字节数组),而InputStream
对象可以读取字节(或字节数组)。就是这样——它们不支持任何其他数据类型。
因此,这些流也被称为字节流。
流的一个特点是它们的数据只能按顺序读取(或写入)。如果不读取流之前的所有数据,就无法从流的中间读取数据。
这就是通过类从键盘读取数据的方式Scanner
:您逐行顺序地从键盘读取数据。我们读一行,然后读下一行,再读下一行,依此类推。恰当地,读取行的方法称为nextLine()
。
将数据写入 anOutputStream
也是顺序发生的。一个很好的例子是控制台输出。您输出一行,然后是另一行。这是顺序输出。你不能输出第一行,然后是第十行,然后是第二行。所有数据仅按顺序写入输出流。
字符流
您最近了解到字符串是第二流行的数据类型,而且确实如此。许多信息以字符和整个字符串的形式传递。计算机擅长以字节形式发送和接收所有内容,但人类并不是那么完美。
考虑到这一事实,Java 程序员编写了另外两个类:Reader
和Writer
. 该类Reader
与类类似InputStream
,但其read()
方法读取的不是字节,而是字符 ( char
)。班级Writer
对应班级OutputStream
。就像Reader
类一样,它适用于字符 ( char
),而不是字节。
如果我们比较这四个类,我们会得到下图:
字节(字节) | 字符(字符) | |
---|---|---|
读取数据 |
|
|
写入数据 |
|
|
实际应用
、和类本身不被任何人直接使用,因为InputStream
它们不与任何可从中读取数据(或可写入数据)的具体对象相关联。但是这四个类有很多可以做很多事情的后代类。OutputStream
Reader
Writer
2.InputStream
类
该类InputStream
很有趣,因为它是数百个后代类的父类。它自己没有任何数据,但它有所有派生类都继承的方法。
一般情况下,流对象内部存储数据的情况很少见。流是读取/写入数据的工具,但不是存储。也就是说,也有例外。
InputStream
该类及其所有子类的方法:
方法 | 描述 |
---|---|
|
从流中读取一个字节 |
|
从流中读取字节数组 |
|
从流中读取所有字节 |
|
跳过n 流中的字节(读取并丢弃它们) |
|
检查流中剩余的字节数 |
|
关闭流 |
让我们简要介绍一下这些方法:
read()
方法
该方法从流中read()
读取一个字节并将其返回。您可能会对int
返回类型感到困惑。选择此类型是因为它int
是标准整数类型。的前三个字节int
将为零。
read(byte[] buffer)
方法
这是该方法的第二个变体read()
。InputStream
它允许您一次从 all 中读取一个字节数组。将存储字节的数组必须作为参数传递。该方法返回一个数字——实际读取的字节数。
假设您有一个 10 KB 的缓冲区,并且您正在使用该类从文件中读取数据FileInputStream
。如果文件仅包含 2 KB,则所有数据都将加载到缓冲区数组中,并且该方法将返回数字 2048(2 KB)。
readAllBytes()
方法
一个很好的方法。它只是从中读取所有数据,InputStream
直到用完并将其作为单字节数组返回。这对于读取小文件非常方便。大文件在物理上可能不适合内存,并且该方法将抛出异常。
skip(long n)
方法
此方法允许您跳过对象的前 n 个字节InputStream
。因为数据是严格按顺序读取的,所以此方法只是从流中读取前 n 个字节并将其丢弃。
返回实际跳过的字节数(如果流在n
跳过字节之前结束)。
int available()
方法
该方法返回流中剩余的字节数
void close()
方法
该close()
方法关闭数据流并释放与之关联的外部资源。一旦流关闭,就不能再从中读取数据。
让我们编写一个示例程序来复制一个非常大的文件。我们不能使用该readAllBytes()
方法将整个文件读入内存。例子:
代码 | 笔记 |
---|---|
|
InputStream 用于从文件中OutputStream 读取 用于写入文件 将数据读入的缓冲区 只要流中有数据 将数据读入缓冲区 将缓冲区中的数据写入第二个流 |
在此示例中,我们使用了两个类:是用于从文件读取数据FileInputStream
的后代,以及用于将数据写入文件的后代。稍后我们将讨论第二类。InputStream
FileOutputStream
OutputStream
这里另一个有趣的点是real
变量。当从文件中读取最后一个数据块时,它的数据量很可能少于 64KB。因此,我们需要输出的不是整个缓冲区,而是其中的一部分——第一个real
字节。这正是write()
方法中发生的事情。
3.Reader
类
该类Reader
是该类的完整类比InputStream
。唯一的区别是它使用字符 ( char
),而不是字节。就像InputStream
类一样,Reader
类本身不会在任何地方使用:它是数百个子类的父类,并为所有子类定义通用方法。
Reader
该类(及其所有后代类)的方法:
方法 | 描述 |
---|---|
|
char 从流中读取一个 |
|
char 从流中读取数组 |
|
跳过n chars 流(读取并丢弃它们) |
|
检查流中是否还有剩余的东西 |
|
关闭流 |
这些方法与类中的方法非常相似InputStream
,尽管存在细微差别。
int read()
方法
char
此方法从流中读取一个并返回它。类型char
扩展为int
,但结果的前两个字节始终为零。
int read(char[] buffer)
方法
这是该方法的第二个变体read()
。Reader
它使您可以一次从 a 中读取一个 char 数组。将存储字符的数组必须作为参数传递。该方法返回一个数字——实际读取的字符数。
skip(long n)
方法
此方法允许您跳过对象的前 n 个字符Reader
。它的工作原理与类的类似方法完全相同InputStream
。返回实际跳过的字符数。
boolean ready()
方法
true
如果流中有未读字节则返回。
void close()
方法
该close()
方法关闭数据流并释放与之关联的外部资源。一旦流关闭,就不能再从中读取数据。
为了比较,让我们编写一个复制文本文件的程序:
代码 | 笔记 |
---|---|
|
Reader 用于从文件中Writer 读取 用于写入文件我们将读取数据的缓冲区 只要流中有数据 将数据读入缓冲区 将缓冲区中的数据写入第二个流 |
GO TO FULL VERSION