继承自 OutputStream 类 构造方法摘要 FileOutputStream(File file) 创建一个向指定 文件输出流。 File 对象表示的文件中写入数据的 FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的 文件输出流。 FileOutputStream(FileDescriptor fdObj) 创建一个向指定文件描述符处写入数据的输出文件流, 该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件 流。 FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出 文件流。 方法摘要 void close() 关闭此文件输出流并释放与此流有关 的所有系统资源。 protected void finalize() 清理到文件的连接,并确保在不再引 用此文件输出流时调用此流的 close 方法。 FileChannel getChannel() 返回与此文件输出流有关的唯一 FileChannel FileDescriptor getFD() 返回与此流有关的文件描述符。 void write(byte[] b) 个字节从指定 byte 将 b.length 数组写入此文件输出流中。 void write(byte[] 量 off 对象。 b, int off, int len) 将指定 byte 数组中从偏移 开始的 len 个字节写入此文件输出流。 b) 将指定字节写入此文件输出流。 void write(int 实例代码:使用这两个类进行文件复制: [java] view plain copy 1. import java.io.FileInputStream; 2. import java.io.FileOutputStream; 3. import java.io.IOException; 4. class IODemo 5. { 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 数组装满 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. } } } } catch (IOException e) { e.printStackTrace(); } fis.close(); fos.close(); read=fis.read(b); while(read!=-1) { fos.write(b,0,read); read=fis.read(b); //read=fis.read(); public static void main(String[] args) { try { //使用 FileInputStream 和 FileOutputStream 进行文件复制 FileInputStream fis=new FileInputStream("a.txt"); FileOutputStream fos=new FileOutputStream("b.txt"); int read; //read=fis.read(); byte b[]=new byte[1024]; //读取文件,存入字节数组 b,返回读取到的字符数,存入 read,默认每次将 b 上述的例子采用也可以只使用对象 fis 的 read()方法,逐个字节的读入数据,然 后用对象 fos 的 write()这个字节输出。 注:FileInputStream 和 FileOutputStream 适用于操作于任何形式的文件(因为 是以字节为向导),如果想要操作文本文件,采用 FileInputReader 和 FileOutputWriter 效率更高。 Q31 那个方法能够获得 file.txt 文件的父路径 A string name=File.getParentName(“file.txt”); B String name=(new File(“file.txt”)).getParent(); C String name=(new File(:file.txt”)).getParentName(); D String name=(new File(“file.txt)).getParentFile(); E Directory dir =(new File(“file.txt)).getParentDir(); D Q32 将“hello”字符写入文件 file.txt 末尾 A Outputstream out =new FileOutPutStream(“file.txt”); Out.wirteBytes(“hello”) B Outputstream out =new FileOutPutStream(“file.txt”,true); DataPutputStream out=new DataOutputStream(os); Out.writerBytes(“hello”) C Outputstream out =new FileOutPutStream(“file.txt”); DataPutputStream out=new DataOutputStream(os); Out.writerBytes(“hello”) D Outputstream out =new OutPutStream(“file.txt”,true); DataPutputStream out=new DataOutputStream(os); Out.writerBytes(“hello”) A 使用默认的文件写入方式,重新写入文件,即清楚已经存在的文件内容 c 和 a 一样; D 中 outputstream 并没有上述的构造方法 Q 那个类似 FilterOutoutStream 类的构造器的合法参数 FilterOutoutStream 类似 BufferOutoutStream 类、Dataoutputstrame 类和 PrintStream 类的父类。哪个类似 FileterOutputStream 类的合法构造器的合法参数 A inputStream B OutputStream C File D RandomAccessFile E StreamTokenizer 面在 Java IO 概述的文章中已经提过, JDK1.0 中就有的传统的 IO 包括字节流和 字符流。我们这篇就说下 java.io 中的字节流。说 Java IO 字节流也就是 InputStream 和 OutputStream,而从这两个类的继承类层次结构来看,从 FilterInputStream 和 FilterOutputStream 开始, 上下可分为原始的字节流和“装 饰”过的功能封装字节流。这篇先说说前半部分——原始字节流的各个类——包 括部分源码分析。 0. InputStream 和 OutputStream 先看下类的声明: 1 public abstract class InputStream implements Closeable 2 public abstract class OutputStream implements Closeable, Flushable 可见此二者都是抽象类,而非接口。也就是说除了分别满足 java.io.Closeable 和 java.io.Flushable,提供了 close()和 flush()方法的默认实现外,还给出了其它实 现,像 InputStream 就提供了 skip()方法实现等。 我们更关心的是 InputStream 中的几个 read()方法和 OutputStream 的几个 write()方法。而实际上最核心的 read()和 write()方法,InputStream 和 OutputStream 并未给出直接实现,这正是 InputStream 和 OutputStream 抽象 的地方,我们来看下。 InputStream 的 read()方法: ? public abstract int read() throws IOException; 核心 read()方法, 留给子 类实现。 public int read(byte b[]) ? public int read(byte b[], int off, int len) 后两者是调用第一个方法读特定长度的数据放入 byte 数组中。 ? OutputStream 的 write()方法: ? public abstract void write(int b) throws IOException; 核心 write()方法, 留给子类实现。 public void write(byte b[]) ? public void write(byte b[], int off, int len) 和 read()类似,把字节数组中的特定数据挨个调用第一个 write()方法写出。 ? 再说 read()和 wirte()实现,那么就要看一下 InputStream 和 OutputStream 的 子类。我们先来看看 InputStream 和 OutputStream 的直接子类。 在 java.io 中 InputStream 的直接子类有: ? ? ? ? ? ? java.io.ByteArrayInputStream java.io.FileInputStream java.io.FilterInputStream java.io.ObjectInputStream java.io.PipedInputStream java.io.SequenceInputStream java.io.StringBufferInputStream 而 java.io 中 OutputStream 的直接子类有: ? java.io.ByteArrayOutputStream ? java.io.FileOutputStream ? java.io.FilterOutputStream ? java.io.ObjectOutputStream ? java.io.PipedOutputStream 这些当中,FileInputStream 和 FileOutputStream 是与外部 IO 直接有关系的, 而 FilterInputStream 和 FilterOutputStream 是“装饰者”设计实现的基类,其它 各类都是特定场景下 InputStream 和 OutputStream 的实现,我们来具体看看。 ? 1. FileInputStream/FileOutputStream 顾名思义, 是文件的输入流和输出流。文件存在于哪里呢?通常存在于外部设备 上,这个是这些实现类中比较特殊的。我们如果做过 C 语言开发,知道我们通 常会 open 操作系统中的文件,并保留其文件描述符 fd。其实,在 Java 中我们 也有类似的东西。首先,就有 fd 属性,它是 FileDescriptor 类,和 C 等底层语 言中一样, 这实际上是对底层文件的一个描述符,和底层文件进行交互的时候少 不了它。实际上在 FileInputStream/FileOutputStream 对象构造方法中,我们 初始化了 FileDescriptor 的 fd 对象,并调用了 open()方法。 遗憾的是,像 open()、read()、write()、skip()、available()这些 FileInputStream/FileOutputStream 中的具体操作方法,在(Sun)JDK 中都冠以 native, 即本地实现, 这样做的好处就是上层开发使用者不必关心, 统统交由 JVM 等底层实现进行处理,实现了平台无关性。 在 FileInputStream 还有这样几个属性: ? ? private final FileDescriptor fd; private FileChannel channel = null; JDK1.4 之后为了支持 NIO 的 Channel 操作 private final Object closeLock = new Object(); 关闭时的并发同步锁 private volatile boolean closed = false; 关闭标志 private static final ThreadLocal runningFinalize = new ThreadLocal<>(); finalize 是否运行的标志 ? ? ? 此外,再简要看下 FileInputStream 构造方法的一个具体实现: 1 2 3 4 5 6 7 8 9 10 11 12 13 public FileInputStream(File file) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(name); } if (name == null) { throw new NullPointerException(); } fd = new FileDescriptor(); fd.incrementAndGetUseCount(); open(name); } 其它重载要么是调用了这个方法,要么实质上是一致的。FileOutputStream 也 是类似, 出了 open()调用的时候有一个 append 属性参数, 标志是否为文件追加。 2. ByteArrayInputStream/ByteArrayOutputStream 名称也很直接,实际上就是字节数组,在构造类对象的时候给出一个 byte 数组。 对于 ByteArrayInputStream 来说,这个 byte 数组就是读取的源头,而 ByteArrayOutputStream 则是 write()的目的地。 此外,构造方法中还可以指定这个 byte 数组的有效起点和长度。 1 public ByteArrayInputStream(byte buf[], int offset, int length) { 2 3 4 5 6 } this.buf = buf; this.pos = offset; this.count = Math.min(offset + length, buf.length); this.mark = offset; 而对于 ByteArrayOutputStream,给出 byte 数组长度参数即可。甚至还有默认 实现,长度 32。 public ByteArrayOutputStream(int size) { if (size < 0) { throw new IllegalArgumentException("Negative initial size: " + size); } buf = new byte[size]; } 1 2 3 4 5 6 7 3. PipedInputStream/PipedOutputStream 从名字理解也没错,就是管道输入输出(字节)流。它们的存在需要彼此,即 PipedInputStream 对象和 PipedOutputStream 对象只有互相存在才真正有意 义,当然可以先构建后连接(connect)。 总体来说就是 PipedInputStream 维护了一个 PipedOutputStream 对象的属性, 而 PipedOutputStream 也维护了一个 PipedInputStream 对象属性。而 PipedInputStream 额外维护了一个缓冲区数组。当 PipedOutputStream 执行 write()的时候,如果 PipedInputStream 对象未就绪,会发生一个异常。就绪情 况下会调用 PipedInputStream 对象的 receive()方法进行接收,也就是写入缓冲 数组 buf[]。而最终的 PipedInputStream 的读取就从这个 buf[]缓冲数组中来。 4. SequenceInputStream 这个 SequenceInputStream 是 InputStream 的另一个实现,而且没有 OutputStream 与其对应(至少在 java.io 中没有)。这个类实际上所做的,也是 一个对多个其它 InputStream 的“包装”,而这个所谓的“包装”的作用就是把这些 依次(sequently)连接起来,当一个 read()到头了(返回-1)就接着读下一个。 下面是 read()方法实现: 1 2 3 4 5 6 7 8 9 10 11 } } return c; } int c = in.read(); if (c == -1) { nextStream(); return read(); public int read() throws IOException { if (in == null) { return -1; 5. StringBufferInputStream 这个类的对象是通过一个 String 字符串对象来构造, read()实际上就是读取其中 的字节 (读取到 char 再和 0xFF 与操作) 。 遗憾的是这个类已经被@Deprecated, 即不建议使用。不多说了。 6. ObjectInputStream/ObjectOutputStream ObjectInputStream 除了 extends 了 InputStream,还实现了 ObjectOutput, ObjectStreamConstants 两个接口。ObjectOutputStream 也类似。 这两个类与序列化有关,后面序列化文章统一说明。 7. FilterInputStream/FilterOutputStream 从文章开头就提到了,这两个类实际上是 Java IO 字节流另外一部分的开头,就 是以“装饰者模式”实现的,提供了更多功能的封装类别字节流,下篇文章详细介 绍整理。 java.io 包中的字节流中的类关系有用到 GoF《设计模式》中的装饰者模式, 而这正体现在 FilterInputStream 和 FilterOutputStream 和它的子类上, 我们这篇就来看一看。 0. FilterInputStream 和 FilterOutputStream 首先,这两个都分别是 InputStream 和 OutputStream 的子类。而且, FilterInputStream 和 FilterOutputStream 是具体的子类,实现了 InputStream 和 OutputStream 这两个抽象类中为给出实现的方法。 但是,FilterInputStream 和 FilterOutputStream 仅仅是“装饰者模式”封装的开 始, 它们在各个方法中的实现都是最基本的实现,都是基于构造方法中传入参数 封装的 InputStream 和 OutputStream 的原始对象。 比如,在 FilterInputStream 类中,封装了这样一个属性: 1 protected volatile InputStream in; 而对应的构造方法是: 1 2 3 } protected FilterInputStream(InputStream in) { this.in = in; read()方法的实现则为: 1 2 public int read() throws IOException { return in.read(); 3 } 其它方法的实现,以及 FilterOutputStream 也都是同理类似的。 我们注意到 FilterInputStream 和 FilterOutputStream 并没给出其它额外的功能 实现, 只是做了一层简单地封装。 那么实现额外功能的实际是 FilterInputStream 和 FilterOutputStream 的各个子类。 1. BufferedInputStream/BufferedOutputStream 先说说这个最简单的一对,BufferedInputStream 和 BufferedOutputStream。 顾名思义,Buffered 就是缓冲了的。在 BufferedInputStream 和 BufferedOutputStream 中,都额外实现了 byte 数组做 buffer。 我们知道在父类 FilterInputStream 和 FilterOutputStream 类中, 已经在构造方 法时封装了原始的 InputStream 或者 OutputStream 对象。 在我们使用 BufferedInputStream 和 BufferedOutputStream 来进行 read()和 write()调用的时候, 并不一定直接对封装的 InputStream 或者 OutputStream 对 象进行操作,而是要经过缓冲处理。 在 BufferedInputStream 的 read()中, 实际上是一次读取了多个字节到缓冲数组, 而非一次只读取一个。后续的 read()操作可以直接从数组中获取字节,而不必再 次调用封装的 InputStream 对象的 read()操作。这样做其实在一定情况下可以 减少底层的 read 调用次数,降低成本开销,提高了效率。 在 BufferedOutputStream 中也是一样, 它的 write()会先把数据写到缓冲数组中, 直到数据达到了某个特定的限额,再调用 write()的时候回真正调用到封装的 OutputStream 对象的 write()方法。 2. DataInputStream/DataOutputStream 这也是比较重要的一对 Filter 实现。那么说起功能,实际上就不得不提到他们除 了 extends FilterInputStream/FilterOutputStream 外, 还额外实现了 DataInput 和 DataOutput 接口。 我们可以先来看下 DataInput 和 DataOutput 这两个 interface。 DataInput 接口的 outline 再看 DataOutput: DataOutput 接口的 outline 而 DataInputStream/DataOutputStream 这一对实际上所做的也就是这两个接 口所定义的方法。再 DataInputStream/DataOutputStream 中,这些方法做了 拼接和拆分字节的工作。通过这些方法,我们可以方便的读取、写出各种我们实 际所面对的类型的数据,而不必具体去在字节层面上做细节操作。 3. PrintStream 这个类不成对,只有这样一个 OutputStream。看起名字中的 Print,我们会想到 什么?println()方法和 print()方法。实际上,包括 java.lang 包中的 System.out 和 System.error,使用的都是 PrintStream 类对象。 从使用层面来看,这个类主要是提供了丰富的 print()和 println()的各类参数重 载方法。对各个类型的输出,包括换行处理等都集成在内。还有 format()方法, 帮我们做到了类似 C 语言中 printf()函数的效果。 而从实现上来看,这些 print()和 println()最终都调用了各类 write()方法。在这 些 write()方法中,实际上使用到了类中封装的这两个属性。 1 2 private BufferedWriter textOut; private OutputStreamWriter charOut; 他们都是 Writer 的子类,是字符流处理类,有编码解码机制,我们会在后续文 章中详细说明。 4. PushbackInputStream 此类增加了“回退(push back)”功能。在读完一部分数据之后,可以“回退”到之 前读过的某个“位置”,并重新设置这些数据。 在实现上,类似于 BufferedInputStream,在 PushbackInputStream 增加了缓 冲数组,回退时调整数组的下标索引,并边回退边重置数据。 5. LineNumberInputStream