OutputStream是Java.io中的一个抽象类,它是所有向字节流写入数据的输出流的超类。OutputStream提供了一些基本的方法,如write(byte b)方法用于写入单个字节等,基于此可以构建出很多高级的输出流类,如BufferedOutputStream、DataOutputStream等。在本文中,我们将探讨OutputStream的基本使用方法以及实现原理。
OutputStream的基本使用方法
在Java编程中,我们经常需要将数据写入到文件、网络流甚至是内存缓冲区。OutputStream提供了以下方法以实现常见的写操作:
write(int b)
: 写入一个字节write(byte[] b)
: 写入一个字节数组write(byte[] b, int off, int len)
: 写入字节数组的一部分flush()
: 将缓冲区的内容强制输出close()
: 关闭输出流
以写入文件为例,我们可以先创建一个文件输出流,然后利用write方法将数据写入文件中:
String filePath = \"output.txt\"; // 定义文件路径
String content = \"Hello, World!\"; // 定义要写入的内容
OutputStream outputStream = new FileOutputStream(filePath);
outputStream.write(content.getBytes()); // 写入数据
outputStream.close(); // 关闭输出流
这段代码会将字符串\"Hello, World!\"写入名为\"output.txt\"的文件中。需要注意的是,write方法写入的是字节数组,所以需要将字符串转换为字节数组。
OutputStream的实现原理
OutputStream是一个抽象类,它的实现类包括了FileOutputStream、ByteArrayOutputStream、PipedOutputStream等。这些类都提供了OutputStream的基本方法,并通过不同的方式实现了向不同的目的地写入数据。这些实现的原理大同小异,下面以FileOutputStream为例进行讲解。
FileOutputStream实现了OutputStream的所有方法,它的构造方法如下:
public FileOutputStream(File file) throws FileNotFoundException {
this(file.getPath(), false);
}
public FileOutputStream(String name, boolean append) throws FileNotFoundException {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(name);
}
if (name == null) {
throw new NullPointerException();
}
if (name.isEmpty()) {
throw new FileNotFoundException(\"Empty file name\");
}
open(name, append);
}
这里重点关注open方法的实现,该方法用于打开文件并创建FileChannel和FileDescriptor,其中FileChannel是Java NIO中的类,用于实现高性能、高效率的I/O操作:
private void open(String name, boolean append) throws FileNotFoundException {
/* Use the system property \"file.encoding\" to initialize the encoder.
* If file.encoding is not set, use iso-8859-1.
*
* The specified encoding is applied to the file name string,
* after converting it to a canonical form,
* and to the content of the file, on the way out.
*/
String csn = Charset.defaultCharset().name();
try {
csn = System.getProperty(\"file.encoding\", csn);
} catch (SecurityException se) {
}
Charset cs = Charset.forName(csn);
if (name == null) {
throw new NullPointerException();
}
if (name.isEmpty()) {
throw new FileNotFoundException(\"Empty file name\");
}
// 根据append参数创建FileOutputStream
if (append) {
openAppend(name);
} else {
boolean exclusive = false;
SecurityManager security = System.getSecurityManager();
if (security != null) {
/* Checking read access doesn't actually help here,
* because the file can be opened for writing
* without being opened for reading.
*/
security.checkWrite(name);
exclusive = true;
}
open0(name, exclusive);
}
// 创建FileDescriptor和FileChannel
fd = fdVal;
this.cs = cs;
this.path = name;
}
至此,FileOutputStream的open方法打开了文件,并通过fdVal获取了一个底层操作系统的文件句柄(handle,也称为文件描述符)。有了底层句柄,我们就可以用Java NIO中的ByteBuffer来向文件流中写数据了。
当我们调用write方法向文件流中写入数据时,实际上是将数据写入到一个内存缓存区中,这个缓存区一般是一个ByteBuffer,Java NIO中的直接缓冲区(也就是将缓冲区分配在Java Heap以外的缓冲区)。当缓存区填满或者我们显式调用flush方法时,数据才会真正被写入文件。需要注意的是,关闭输出流时也会显式调用flush方法,所以我们不需要在close方法中再次调用flush。
总结
OutputStream是Java中所有输出流的超类,它提供了基本的写操作方法,如write和flush等。OutputStream的实现类负责具体向目的地写入数据,比如FileOutputStream将数据写入到文件中,ByteArrayOutputStream将数据写入到内存中的缓冲区中。OutputStream的实现原理与具体实现类有关,我们以FileOutputStream为例讲解了其打开文件和写入文件的实现过程。