“嗨!在今天的课程中,我们将继续讨论 Java 中的输入和输出流 ( Java I/O )。这不是关于该主题的第一课,当然也不会是最后一课 :)
因为它碰巧,Java 语言提供了很多处理 I/O 的方法。有相当多的类实现了这个功能,所以我们把它们分成几节课——所以你不会从一开始就感到困惑 :) 过去课程,我们谈到了
这是使用以下方法从文件中读取数据的样子

BufferedReader
,以及抽象类InputStream
和OutputStream
几个后代。今天我们将考虑 3 个新类:FileInputStream
、 FileOutputStream
和 BufferedInputStream
。
文件输出流类
该类的主要目的FileOutputStream
是将字节写入文件。 没什么复杂的:)FileOutputStream
是抽象类的实现之一OutputStream
。在构造函数中,此类的对象采用目标文件(应写入字节的位置)的路径或对象File
。我们将检查每个示例:
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 CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
创建File
对象时,我们将所需的路径传递给构造函数。我们不需要提前创建它:如果它不存在,程序会创建它。您也可以在不创建额外对象的情况下通过,只需传递一个带有路径的字符串:
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 CodeGym — The best site for would-be programmers!";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
两种情况下的结果都是一样的。 我们可以打开我们的文件并在那里看到以下内容:
Hi! Welcome to CodeGym — The best site for would-be programmers!
但这里有一个细微差别。尝试连续多次运行上述示例中的代码。然后查看文件并回答这个问题:它有多少行?只有一个。但是你多次运行代码。事实证明,数据每次都被覆盖——旧的被新的取代。如果这不适合我们并且我们需要按顺序写入文件,我们该怎么办?如果我们想连续三次将问候语写入文件怎么办?这一切都非常简单。由于语言无法知道我们在每种情况下需要什么行为,FileOutputStream
构造函数可以采用一个额外的参数——boolean append
. 如果它的值为真,数据将被写入文件的末尾。如果为 false(默认情况下为 false),任何旧数据都将被删除并由新数据替换。让我们通过运行修改后的代码三次来检查这一点:
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 CodeGym — The best site for would-be programmers!\r\n";
fileOutputStream.write(greetings.getBytes());
fileOutputStream.close();
}
}
文件内容:
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
Hi! Welcome to CodeGym — The best site for would-be programmers!
现在不一样了!在使用 I/O 类时不要忘记这个特性。曾经有一段时间,我在任务上花费了数小时,绞尽脑汁数小时,试图了解我的数据是如何从文件中消失的:)当然,就像其他 I/O 类一样,不要忘记使用该close()
方法释放资源。
文件输入流类
有FileInputStream
相反的目的——从文件中读取字节。正如FileOutputStream
inherits一样OutputStream
,这个类派生自InputStream
抽象类。我们将在我们的“ 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"

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);
}
}
}
我们从文件中读取一个字节,将读取的字节转换为字符并显示在控制台上。这是控制台输出:
So close no matter how far
Couldn't be much more from the heart
Forever trusting who we are
And nothing else matters
缓冲输入流类
我认为,鉴于过去课程中的知识,您可以很容易地说出我们为什么需要这个BufferedInputStream
类以及它与FileInputStream
:) 相比有什么优势:) 我们已经遇到过缓冲流,所以在继续阅读之前尝试猜测(或记住):) 缓冲流主要用于优化 I/O。 访问数据源,例如从文件中读取,在性能方面是一项昂贵的操作,而访问文件以读取每个字节是一种浪费。这就是为什么BufferedInputStream
不是一次一个字节地读取数据,而是以块为单位读取数据,并将它们临时存储在一个特殊的缓冲区中。这让我们可以通过减少访问文件的次数来优化程序。让我们看看这是什么样子的:
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);
}
}
}
这里我们创建了一个BufferedInputStream
对象。InputStream
它的构造函数采用该类或其任何后代的实例,所以FileInputStream
会这样做。 作为附加参数,它采用以字节为单位的缓冲区大小。由于这个参数,现在将从文件中读取的数据不是一次一个字节,而是一次 200 个字节!想象一下我们减少了多少文件访问次数。FileInputStream
要比较性能,您可以获取一个大文本文件(几兆字节的文本)并使用和比较读取和输出到控制台所花费的时间(以毫秒为单位)BufferedInputStream
。下面是演示这两个选项的代码:
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()));
}
}
在我的计算机上读取一个 1.5 MB 的文件时,FileInputStream
在 ~3500 毫秒内完成了工作,但BufferedInputStream
在 ~1700 毫秒内完成了它。如您所见,缓冲流优化了工作,将其减半!:) 我们将继续研究 I/O 课程——待会见!
GO TO FULL VERSION