java IO之输出流——OutputStream
Posted Judy518
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java IO之输出流——OutputStream相关的知识,希望对你有一定的参考价值。
OutputStream抽象类是所有输出字节流的超类,输出流接收输出字节,并将这些字节发送到某个接收器。这个接收器可以是字节数组、文件、管道。该类的定义如下:
1 public abstract class OutputStream implements Closeable, Flushable { 2 //将指定的字节写到这个输出流中 3 public abstract void write(int b) throws IOException; 4 //将指定字节数组中的内容写到输出流中 5 public void write(byte b[]) throws IOException { 6 write(b, 0, b.length); 7 } 8 public void write(byte b[], int off, int len) throws IOException { 9 if (b == null) { 10 throw new NullPointerException(); 11 } else if ((off < 0) || (off > b.length) || (len < 0) || 12 ((off + len) > b.length) || ((off + len) < 0)) { 13 throw new IndexOutOfBoundsException(); 14 } else if (len == 0) { 15 return; 16 } 17 for (int i = 0 ; i < len ; i++) { 18 write(b[off + i]); 19 } 20 } 21 //清理输出流中的数据,迫使缓冲的字节写出去 22 public void flush() throws IOException { 23 } 24 //关闭流 25 public void close() throws IOException { 26 } 27 }
输出字节流的类结构图如下,同样,这里只列举常用的几个类,还有很多未被列出。
下面对不同的输出流进行简单的分析,会给出相应的类源码和示例。
1、ByteArrayOutputStream,字节数组输出流,此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray()
和 toString()
获取数据。 源代码如下:
1 import java.util.Arrays; 2 public class ByteArrayOutputStream extends OutputStream { 3 //定义一个用于存储输出数据的缓冲数组 4 protected byte buf[]; 5 protected int count; 6 public ByteArrayOutputStream() { 7 this(32); 8 } 9 public ByteArrayOutputStream(int size) { 10 if (size < 0) { 11 throw new IllegalArgumentException("Negative initial size: "+ size); 12 } 13 buf = new byte[size]; 14 } 15 private void ensureCapacity(int minCapacity) { 16 // overflow-conscious code 17 if (minCapacity - buf.length > 0) 18 grow(minCapacity); 19 } 20 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 21 //扩展缓冲数组大小 22 private void grow(int minCapacity) { 23 // overflow-conscious code 24 int oldCapacity = buf.length; 25 int newCapacity = oldCapacity << 1; 26 if (newCapacity - minCapacity < 0) 27 newCapacity = minCapacity; 28 if (newCapacity - MAX_ARRAY_SIZE > 0) 29 newCapacity = hugeCapacity(minCapacity); 30 buf = Arrays.copyOf(buf, newCapacity); 31 } 32 private static int hugeCapacity(int minCapacity) { 33 if (minCapacity < 0) // overflow 34 throw new OutOfMemoryError(); 35 return (minCapacity > MAX_ARRAY_SIZE) ? 36 Integer.MAX_VALUE : 37 MAX_ARRAY_SIZE; 38 } 39 //将数字b写到缓冲数组中 40 public synchronized void write(int b) { 41 ensureCapacity(count + 1); 42 buf[count] = (byte) b; 43 count += 1; 44 } 45 public synchronized void write(byte b[], int off, int len) { 46 if ((off < 0) || (off > b.length) || (len < 0) || 47 ((off + len) - b.length > 0)) { 48 throw new IndexOutOfBoundsException(); 49 } 50 ensureCapacity(count + len); 51 System.arraycopy(b, off, buf, count, len); 52 count += len; 53 } 54 //将此 byte 数组输出流的全部内容写入到指定的输出流参数中, 55 //这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。 56 public synchronized void writeTo(OutputStream out) throws IOException { 57 out.write(buf, 0, count); 58 } 59 public synchronized void reset() { 60 count = 0; 61 } 62 //将输出的内容以字符数组的形式返给用户 63 public synchronized byte toByteArray()[] { 64 return Arrays.copyOf(buf, count); 65 } 66 public synchronized int size() { 67 return count; 68 } 69 //将输出的内容以字符串的形式返给用户 70 public synchronized String toString() { 71 return new String(buf, 0, count); 72 } 73 //将输出的内容以以指定字符编码的字符串形式返给用户 74 public synchronized String toString(String charsetName) 75 throws UnsupportedEncodingException 76 { 77 return new String(buf, 0, count, charsetName); 78 } 79 @Deprecated 80 public synchronized String toString(int hibyte) { 81 return new String(buf, hibyte, 0, count); 82 } 83 public void close() throws IOException { 84 } 85 }
从源码可以看出,涉及到数据操作的方法都加了synchronized关键字,所以该类是安全同步的类。使用方法如下:
1 static void byteArrayOutputTest(){ 2 ByteArrayOutputStream out=new ByteArrayOutputStream(8); 3 try{ 4 while(out.size()!=8){ 5 out.write(System.in.read()); 6 } 7 for(byte by:out.toByteArray()){ 8 System.out.print((char)by+" "); 9 } 10 System.out.println(); 11 }catch(Exception e){ 12 e.printStackTrace(); 13 } 14 }
我们创建了一个字节数组输出流,它的缓冲容量大小为8,然后我们从控制台进行输入,输入的时候可以不加空格,如果添加空格,空格也计数在内,可以输入多个字符,但最后输出的字符数只有8个,因为我们已经指定了缓冲容量的大小,当用toByteArray()方法取出数据时,它返回的字符数组长度为8.
2、FileOutputStream,文件输出流,它是将数据输出到文件中,注意这里操作对象——文件的可用与否与平台有关,某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
1 import java.nio.channels.FileChannel; 2 import sun.nio.ch.FileChannelImpl; 3 public class FileOutputStream extends OutputStream 4 { 5 private final FileDescriptor fd; 6 private final boolean append; 7 private FileChannel channel; 8 private final String path; 9 private final Object closeLock = new Object(); 10 private volatile boolean closed = false; 11 //创建文件输出流,如果文件不存在,则自动创建文件 12 public FileOutputStream(String name) throws FileNotFoundException { 13 this(name != null ? new File(name) : null, false); 14 } 15 //创建文件输出流,如果文件不存在,则自动创建文件 16 //同时指定文件是否具有追加内容的功能,如果有,则新添内容放到文件后面,而不覆盖源文件内容 17 public FileOutputStream(String name, boolean append) 18 throws FileNotFoundException 19 { 20 this(name != null ? new File(name) : null, append); 21 } 22 public FileOutputStream(File file) throws FileNotFoundException { 23 this(file, false); 24 } 25 //创建文件输出流,并指定可以在文件最后追加内容,如果没有指定,则将原来内容覆盖 26 public FileOutputStream(File file, boolean append) 27 throws FileNotFoundException 28 { 29 String name = (file != null ? file.getPath() : null); 30 SecurityManager security = System.getSecurityManager(); 31 if (security != null) { 32 security.checkWrite(name); 33 } 34 if (name == null) { 35 throw new NullPointerException(); 36 } 37 if (file.isInvalid()) { 38 throw new FileNotFoundException("Invalid file path"); 39 } 40 this.fd = new FileDescriptor(); 41 fd.attach(this); 42 this.append = append; 43 this.path = name; 44 open(name, append); 45 } 46 public FileOutputStream(FileDescriptor fdObj) { 47 SecurityManager security = System.getSecurityManager(); 48 if (fdObj == null) { 49 throw new NullPointerException(); 50 } 51 if (security != null) { 52 security.checkWrite(fdObj); 53 } 54 this.fd = fdObj; 55 this.append = false; 56 this.path = null; 57 fd.attach(this); 58 } 59 private native void open0(String name, boolean append) 60 throws FileNotFoundException; 61 private void open(String name, boolean append) 62 throws FileNotFoundException { 63 open0(name, append); 64 } 65 //调用本地方法,将内容写入指定文件 66 private native void write(int b, boolean append) throws IOException; 67 public void write(int b) throws IOException { 68 write(b, append); 69 } 70 private native void writeBytes(byte b[], int off, int len, boolean append) 71 throws IOException; 72 //将字符数组内容写进文件 73 public void write(byte b[]) throws IOException { 74 writeBytes(b, 0, b.length, append); 75 } 76 public void write(byte b[], int off, int len) throws IOException { 77 writeBytes(b, off, len, append); 78 } 79 //关闭文件流 80 public void close() throws IOException { 81 synchronized (closeLock) { 82 if (closed) { 83 return; 84 } 85 closed = true; 86 } 87 if (channel != null) { 88 channel.close(); 89 } 90 91 fd.closeAll(new Closeable() { 92 public void close() throws IOException { 93 close0(); 94 } 95 }); 96 } 97 public final FileDescriptor getFD() throws IOException { 98 if (fd != null) { 99 return fd; 100 } 101 throw new IOException(); 102 } 103 public FileChannel getChannel() { 104 synchronized (this) { 105 if (channel == null) { 106 channel = FileChannelImpl.open(fd, path, false, true, append, this); 107 } 108 return channel; 109 } 110 } 111 //清除文件流缓冲内容,并关闭文件流 112 protected void finalize() throws IOException { 113 if (fd != null) { 114 if (fd == FileDescriptor.out || fd == FileDescriptor.err) { 115 flush(); 116 } else { 117 close(); 118 } 119 } 120 } 121 private native void close0() throws IOException; 122 private static native void initIDs(); 123 static { 124 initIDs(); 125 } 126 }
文件输出流操作不是线程安全的,如果用于多线程访问,注意使用同步。以下是文件输出流操作的例子:
1 //文件输出流测试 2 static void fileOutputTest(){ 3 FileOutputStream fout=null; 4 FileInputStream fin=null; 5 try{ 6 //从my.java文件中读取内容 7 fin=new FileInputStream("my.java"); 8 fout=new FileOutputStream("out.txt"); 9 byte[] buff=new byte[1024]; 10 while(fin.read(buff)>0){ 11 //FileInputStream将从my.java文件中读取到的内容写到buff数组中 12 //然后FileOutputStream将buff数组中的内容写到流中 13 fout.write(buff); 14 }//将流中缓冲的内容输出到文件中 15 fout.flush(); 16 }catch(Exception e){ 17 e.printStackTrace(); 18 }finally{ 19 try{ 20 if(fout!=null) 21 fout.close(); 22 }catch(Exception e){ 23 e.printStackTrace(); 24 } 25 } 26 }
为了减少操作,这里使用了文件输入流对象,我们从my.java文件中读取内容,然后将读取到的内容写到out.txt文件中,从my.java文件中读取内容要用输入流,向文件中写内容要用输出流,这里两种流都做了使用。
3、FilterOutputStream,该类是提供输出流的装饰器类的接口,继承该类的子类相当于一个装饰器,能够为OutputStream类型的对象操作提供额外的功能,这里以BufferedOutputStream为例,该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。 比如,我们需要向一个文件中输入内容,这时候我们可以先将内容存储到缓冲数组中,并不是真的向该文件写内容,当调用flush()方法或关闭流时,内容才真正写到文件中。该类的源码如下:
1 public class BufferedOutputStream extends FilterOutputStream { 2 //内部存储数据的缓冲数组 3 protected byte buf[]; 4 //缓冲中的有效字节数目 5 protected int count; 6 //创建一个缓冲输出流,将时数据写到特定的底层输出流中 7 public BufferedOutputStream(OutputStream out) { 8 this(out, 8192); 9 } 10 //创建一个缓冲输出流,将时数据写到具有特定大小容量的特定的底层输出流中 11 public BufferedOutputStream(OutputStream out, int size) { 12 super(out); 13 if (size <= 0) { 14 throw new IllegalArgumentException("Buffer size <= 0"); 15 } 16 buf = new byte[size]; 17 } 18 //将缓冲中的数据清理出去,输出到目的地 19 private void flushBuffer() throws IOException { 20 if (count > 0) { 21 out.write(buf, 0, count); 22 count = 0; 23 } 24 } 25 //将指定的字节写到缓冲输出流中 26 public synchronized void write(int b) throws IOException { 27 if (count >= buf.length) { 28 flushBuffer(); 29 } 30 buf[count++] = (byte)b; 31 } 32 //从指定位置off开始,将b数组内的len个字节写到缓冲输出流中 33 //一般来说,此方法将给定数组的字节存入此流的缓冲区中,根据需要将该缓冲区刷新,并转到底层输出流。 34 //但是,如果请求的长度至少与此流的缓冲区大小相同,则此方法将刷新该缓冲区并将各个字节直接写入底层输出流。因此多余的 BufferedOutputStream 将不必复制数据。 35 public synchronized void write(byte b[], int off, int len) throws以上是关于java IO之输出流——OutputStream的主要内容,如果未能解决你的问题,请参考以下文章java基础之 IO 流(InputStream/OutputStream)
Java之IOInputStream和OutputStream