OutputStream也是抽象类,它是所有输出流的超类,这个抽象类定义的一个最重要的方法就是void write(int b)
public abstract void write(int b) throws IOException;
这个方法会写入一个字节到输出流,虽然传入的参数是int参数,但是只会写入一个字节,即只写入int最低8位表示字节的部分(相当于 b & 0xff)
和InputStream类似,OutputStream也提供了close()方法关闭输出流,以便释放系统资源。
OutpuStream还提供了一个flush()方法,它的目的是将缓冲区的内容真正输出到目的地。
向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个
bytep[]数组),等到缓冲区写满了,再一次性写入文件或者网络。对于很多IO设备来说,一次写一个字节和一次写1000个字节,花费的时间几乎是完全一样的,所以OutputStream有个flush()方法,能强制把缓冲区内容输出。
通常情况下,我们不需要调用这个flush()方法,因为缓冲区写满了OutputStream会自动调用它,并且,在调用close()方法关闭OutpuStream之前,也会自动调用flush()方法。
但是某些情况下,我们必须手动调用flush()方法。
小明正在开发一款在线聊天软件,当用户输入一句话后,就通过
OutpuStream的write()方法写入网络流。 小明测试的时候发现,发送方输入后,接收方根本收不到任何信息,怎么肥四?原因就在于写入网络流是先写入内存缓冲区,等缓冲区满江了才会一次性发送到网络。如果缓冲区大小是4K,则发送方要敲几千个字符后,操作系统才会把缓冲区的内容发送出去,这个时候,接收方会一次性收到大量消息。 解决办法就是每输入一句话后,立刻调用flush(),不管当前缓冲区是否已满,强迫操作系统把缓冲区的内容立刻发送出去。
实际上,InputStream也有缓冲区。
例如从FileInputStream读取一个字节时,操作系统往往会一次性读取若干字节到缓冲区,并维护一个指针指向未读的缓冲区。然后,每次我们调用int read()读取下一个字节时,可以直接返回缓冲区的下一个字节,避免每次读一个字节都导致IO操作。当缓冲区全部读完后,继续调用read(),则会触发操作系统的下一次读取并再次填满缓冲区。
FileOutputStream
写入文件流
public void writeFile() throws IOException {OutputStream output = new FileOutputStream("src/readme.txt");output.write(72);output.write(101);output.write(108);output.write(108);output.write(111);output.close();}
每次写入一个字节非常麻烦,更常见的是一次性写入若干字节。这里,可以用OutpuStream提供的重载方法void wrte(bytep[])来实现
public void wrteFile() throws IOException {OutputStream output = new FileOutputStream("src/readme.txt");output.write("Hello".getBytes("UTF-8"));output.close();}
和InputStream一样,我们需要用try(resource)来保证OutputStream在无论是否发生IO错误的时候都能够正确地关闭。
public void writeFile() throws IOException{try(OutputStream output = new FileOutputStream("src\\readme.txt")){output.write("Hello".getBytes("UTF-8"));}}
阻塞
和InputStream一样,OutputStream的write()方法也是阻塞的。
OuputStream实现类
用FileOutputStream可以从文件获取输出流,这是OutpuStream常用的一个实现类。此外,`ByteArrayOutpuStream 可以在内存中模拟一个OutputStream。
public class Main {public static void main(String[] args) throws IOException{byte[] data;try(ByteArrayOutputStream output = new ByteArrayOutputStream()){output.wirte("Hello".getBytes("UTF-8"));output.write("world!".getBytes("UTF-8"));data = output.toByteArray();}Sytem.out.println(new String(data, "UTF-8"));}}
ByteArrayOutputStream实际上是把一个byte[]数组在内存中变成一个OutputStream,虽然实际应用不多,但测试的时候,可以用它来构造一个OutputStream。
同时操作多个AutoCloseable资源时, 在try(resource) {}语句中可以同时写出多个资源,用;分隔。
try(InputStream input = new FileInputStream();OutputStream output = new FileOutputStream()){input.transferTo(output);}
练习
利用InputStream和OutputStream,编写一个复制文件的程序,它可以带参数运行
public class Main {public static void main(String[] args){if(args.length != 2){System.exit(1);}copyFileTo(args[0],args[1]);}public static void copyFileTo(String source,String target){try(InputStream input = new FileInputStream(source);OutputStream output = new FIleOutputStream(target)){int n;byte[] bytes = new byte[1000];while ( (n = input.read(bytes)) != -1) {output.write(bytes,0,n);}}}}
小结
Java标准库的java.io.OutputStream定义了所有输出流的超类
FileOutputStream实现了文件流输出ByteArrayOutpuStream在内存中模拟一个字节流输出
某些情况下需要手动调用 OutputStream的flush()方法强制输出缓冲区
总是使用try(resource)来保证OutputStream正确关闭
