Java8 I/O源码-FileInputStream与FileOutputStream

来源:互联网 发布:mac 怎么创建xlsx文档 编辑:程序博客网 时间:2024/05/21 10:30

今天学习FileInputStream与FileOutputStream。

FileInputStream是文件输入流,用于从文件系统中的某个文件中获得输入字节。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用FileReader。

FileOutputStream是文件输出流,用于将数据写入File或FileDescriptor的输出流。FileOutputStream用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用FileWriter。

FileInputStream

先学习下FileInputStream的源码。

public class FileInputStream extends InputStream{    //FileInputStream的文件描述符    private final FileDescriptor fd;    //引用文件的路径    private final String path;    //文件通道    private FileChannel channel = null;    //关闭锁    private final Object closeLock = new Object();    //标识流是否关闭    private volatile boolean closed = false;    /**     * 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径名name指定。创建一个新FileDescriptor对象来表示此文件连接。     *      * 首先,如果有安全管理器,则用name作为参数调用其checkRead方法。     *     * @param      name   文件系统中的路径名     * @exception  FileNotFoundException  如果该文件不存在,或者它是一个目录,而不是一个常规文件,     * 抑或因为其他某些原因而无法打开进行读取。     */    public FileInputStream(String name) throws FileNotFoundException {        this(name != null ? new File(name) : null);    }    /**     * 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的File对象file指定。     *      * 参考FileInputStream(String name)的注释     */    public FileInputStream(File file) throws FileNotFoundException {        //获取文件名        String name = (file != null ? file.getPath() : null);        //获取安全管理器        SecurityManager security = System.getSecurityManager();        if (security != null) {            //如果调用线程没有访问指定文件的权限,抛出SecurityException            security.checkRead(name);        }        if (name == null) {            throw new NullPointerException();        }        if (file.isInvalid()) {            throw new FileNotFoundException("Invalid file path");        }        //fd表示此文件连接        fd = new FileDescriptor();        fd.attach(this);        path = name;        //打开文件        open(name);    }    /**     * 通过使用文件描述符fdObj创建一个FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。     */    public FileInputStream(FileDescriptor fdObj) {        SecurityManager security = System.getSecurityManager();        if (fdObj == null) {            throw new NullPointerException();        }        if (security != null) {            security.checkRead(fdObj);        }        fd = fdObj;        path = null;        fd.attach(this);    }    /*     * native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。     * Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。     * JNI是Java本机接口(Java Native Interface),是一个本机编程接口,     * 它是Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。     * JNI允许Java代码使用以其他语言编写的代码和代码库。     * Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,     * 从而允许程序员从本机代码内部调用Java代码。     * 所以想要了解open0方法的具体实现只能去查看JVM源码了。     */    private native void open0(String name) throws FileNotFoundException;    // wrap native call to allow instrumentation    /**     * Opens the specified file for reading.     * @param name the name of the file     */    private void open(String name) throws FileNotFoundException {        open0(name);    }    /**     * 从此输入流中读取一个数据字节。     */    public int read() throws IOException {        return read0();    }    /**     * 参考open0(String name)     */    private native int read0() throws IOException;    /**     * 从此输入流中将最多len个字节的数据读入一个起始偏移量为off的byte数组中。     *      * 参考open0(String name)     */    private native int readBytes(byte b[], int off, int len) throws IOException;    /**     * 从此输入流中将最多b.length个字节的数据读入一个起始偏移量为0的byte数组中。     */    public int read(byte b[]) throws IOException {        return readBytes(b, 0, b.length);    }    /**     * 从此输入流中将最多len个字节的数据读入一个起始偏移量为off的byte数组中。     */    public int read(byte b[], int off, int len) throws IOException {        return readBytes(b, off, len);    }    /**     * 从输入流中跳过并丢弃n个字节的数据。     *      * 参考open0(String name)     */    public native long skip(long n) throws IOException;    /**     * 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。     * 参考open0(String name)     */    public native int available() throws IOException;    /**     * 关闭此文件输入流并释放与此流有关的所有系统资源。     * 如果此流有一个与之关联的通道,则关闭该通道。     */    public void close() throws IOException {        synchronized (closeLock) {            if (closed) {                return;            }            closed = true;        }        //如果此流有一个与之关联的通道,则关闭该通道。        if (channel != null) {           channel.close();        }        fd.closeAll(new Closeable() {            public void close() throws IOException {               close0();           }        });    }    /**     * 返回表示到文件系统中实际文件的连接的FileDescriptor对象,该文件系统正被此FileInputStream使用。     */    public final FileDescriptor getFD() throws IOException {        if (fd != null) {            return fd;        }        throw new IOException();    }    /**     * 返回与此文件输入流有关的唯一FileChannel对象。     */    public FileChannel getChannel() {        synchronized (this) {            if (channel == null) {                channel = FileChannelImpl.open(fd, path, true, false, this);            }            return channel;        }    }    //参考open0(String name)    private static native void initIDs();    //参考open0(String name)    private native void close0() throws IOException;    static {        initIDs();    }    /**     * 确保在不再引用文件输入流时调用其close方法。     */    protected void finalize() throws IOException {        if ((fd != null) &&  (fd != FileDescriptor.in)) {            close();        }    }}

FileOutputStream

public class FileOutputStream extends OutputStream{    /**     * 文件描述符     */    private final FileDescriptor fd;    /**     * 标识添加还是替换文件的内容     */    private final boolean append;    /**     * 关联的通道     * 懒加载     */    private FileChannel channel;    //引用文件的路径    private final String path;    //锁    private final Object closeLock = new Object();    //标识流是否关闭    private volatile boolean closed = false;    /**     * 创建一个向具有指定名称的文件中写入数据的输出文件流。     * 创建一个新FileDescriptor对象来表示此文件连接。     * 首先,如果有安全管理器,则用 name 作为参数调用checkWrite方法。     *     * @param      name   文件路径     * @exception  FileNotFoundException  如果文件存在,但它是一个目录,而不是一个常规文件;或者该文件不存在,但无法创建它;抑或因为其他某些原因而无法打开它     * @exception  SecurityException   如果存在安全管理器,且其checkWrite方法拒绝对文件进行写入访问     */    public FileOutputStream(String name) throws FileNotFoundException {        this(name != null ? new File(name) : null, false);    }    /**     * 创建一个向具有指定name的文件中写入数据的输出文件流。如果第二个参数为append为true,则将字节写入文件末尾处,而不是写入文件开始处。     */    public FileOutputStream(String name, boolean append)        throws FileNotFoundException    {        this(name != null ? new File(name) : null, append);    }    /**     * 创建一个向指定File对象表示的文件中写入数据的文件输出流。创建一个新FileDescriptor对象来表示此文件连接。     */    public FileOutputStream(File file) throws FileNotFoundException {        this(file, false);    }    /**     * 创建一个向指定File对象表示的文件中写入数据的文件输出流。如果第二个参数append为true,则将字节写入文件末尾处,而不是写入文件开始处。     */    public FileOutputStream(File file, boolean append)        throws FileNotFoundException    {        String name = (file != null ? file.getPath() : null);        SecurityManager security = System.getSecurityManager();        if (security != null) {            security.checkWrite(name);        }        if (name == null) {            throw new NullPointerException();        }        if (file.isInvalid()) {            throw new FileNotFoundException("Invalid file path");        }        this.fd = new FileDescriptor();        fd.attach(this);        this.append = append;        this.path = name;        open(name, append);    }    /**     * 创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。     */    public FileOutputStream(FileDescriptor fdObj) {        SecurityManager security = System.getSecurityManager();        if (fdObj == null) {            throw new NullPointerException();        }        if (security != null) {            security.checkWrite(fdObj);        }        this.fd = fdObj;        this.append = false;        this.path = null;        fd.attach(this);    }    //参考FileInputStream.open0(String name)    private native void open0(String name, boolean append)        throws FileNotFoundException;    private void open(String name, boolean append)        throws FileNotFoundException {        open0(name, append);    }    /**     * 将指定字节写入此文件输出流。     * 参考FileInputStream.open0(String name)     */    private native void write(int b, boolean append) throws IOException;    /**     * 将指定字节写入此文件输出流。     */    public void write(int b) throws IOException {        write(b, append);    }    /**     * 将b.length个字节从指定byte数组写入此文件输出流中     * 参考FileInputStream.open0(String name)     */    private native void writeBytes(byte b[], int off, int len, boolean append)        throws IOException;    /**     * 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流。     */    public void write(byte b[]) throws IOException {        writeBytes(b, 0, b.length, append);    }    /**     * 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流。     */    public void write(byte b[], int off, int len) throws IOException {        writeBytes(b, off, len, append);    }    /**     * 关闭此文件输出流并释放与此流有关的所有系统资源。此文件输出流不能再用于写入字节。     * 如果此流有一个与之关联的通道,则关闭该通道。     */    public void close() throws IOException {        synchronized (closeLock) {            if (closed) {                return;            }            closed = true;        }        if (channel != null) {            channel.close();        }        fd.closeAll(new Closeable() {            public void close() throws IOException {               close0();           }        });    }    /**     * 返回与此流有关的文件描述符。     */     public final FileDescriptor getFD()  throws IOException {        if (fd != null) {            return fd;        }        throw new IOException();     }    /**     * 返回与此文件输出流有关的唯一FileChannel对象。     */    public FileChannel getChannel() {        synchronized (this) {            if (channel == null) {                channel = FileChannelImpl.open(fd, path, false, true, append, this);            }            return channel;        }    }    /**     * 清理到文件的连接,并确保在不再引用此文件输出流时调用此流的close方法。     */    protected void finalize() throws IOException {        if (fd != null) {            if (fd == FileDescriptor.out || fd == FileDescriptor.err) {                flush();            } else {                close();            }        }    }    ////参考FileInputStream.open0(String name)    private native void close0() throws IOException;    //参考FileInputStream.open0(String name)    private static native void initIDs();    static {        initIDs();    }}

demo

FileInputStream与FileOutputStream与用于操作诸如图像数据之类的原始字节流。要操作字符流,请考虑使用FileReader与FileWriter。

import java.io.File;import java.io.FileDescriptor;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;/** * FileInputStream 和FileOutputStream的API测试类 */public class FileStreamTest {    private static final String FileName = "fileStream.txt";    public static void main(String[] args) {        testFileOutputStream();        testFileInputStream();    }    /**     * FileOutputStream的API测试类     */    private static void testFileOutputStream() {        try {            // FileOutputStream fos = new FileOutputStream("fileStream.txt");            // 创建文件对应File对象            File file = new File(FileName);            // 创建文件对应的FileOutputStream对象,默认是覆盖模式            FileOutputStream fos = new FileOutputStream(file);            fos.write(new byte[] { 0x61, 0x62, 0x63, 0x64 });// abcd            fos.write(new byte[] { 0x65, 0x66, 0x67, 0x68 });// efgh            // 创建文件对应的FileOutputStream对象,默认是覆盖模式            FileOutputStream fos2 = new FileOutputStream(file);            fos2.write(new byte[] { 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78 });// qrstuvwx            // 创建文件对应的FileOutputStream对象,模式为追加模式            FileOutputStream fos3 = new FileOutputStream(file, true);            fos3.write(new byte[] { 0x51, 0x52, 0x53, 0x54 });// QRST            fos.close();            fos2.close();            fos3.close();        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * FileInputStream的API测试函数     */    private static void testFileInputStream() {        try {            File file = new File(FileName);            FileInputStream fis = new FileInputStream(file);            FileDescriptor fd = fis.getFD();            // 根据文件描述符创建FileInputStream对象            FileInputStream fis2 = new FileInputStream(fd);            // 测试read()            System.out.println("使用read()读取一个字节:" + (char) fis.read());            System.out.println("使用available()获取当前可用字节数:" + fis.available());            // 测试read(byte[] b,int off,int len)            byte[] b = new byte[5];            fis.read(b, 0, b.length);            System.out.println("使用readread(byte[] b,int off,int len)读取5个字节到b中:" + new String(b));            System.out.println("使用available()获取当前可用字节数:" + fis.available());            // 测试skip(long byteCount)            System.out.printf("使用skip(long n)跳过%s个字节\n", fis.skip(1));            System.out.println("使用available()获取当前可用字节数:" + fis.available());            fis.close();            fis2.close();        } catch (IOException e) {            e.printStackTrace();        }    }}

testFileOutputStream执行后,fileStream.txt中的内容为

qrstuvwxQRST
fos.write(new byte[] { 0x61, 0x62, 0x63, 0x64 });// abcdfos.write(new byte[] { 0x65, 0x66, 0x67, 0x68 });// efgh

执行后,文件内容为abcdefgh

FileOutputStream fos2 = new FileOutputStream(file);

执行后,文件内容为空

fos2.write(new byte[] { 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78 });// qrstuvwx

执行后,文件内容为qrstuvwx

FileOutputStream fos3 = new FileOutputStream(file, true);

执行后,文件内容为qrstuvwx

fos3.write(new byte[] { 0x51, 0x52, 0x53, 0x54 });// QRST

执行后,文件内容为qrstuvwxQRST

testFileInputStream执行后后控制台打印内容为:

使用read()读取一个字节:q使用available()获取当前可用字节数:11使用readread(byte[] b,int off,int len)读取5个字节到b中:rstuv使用available()获取当前可用字节数:6使用skip(long n)跳过1个字节使用available()获取当前可用字节数:5

总结

  • FileInputStream是文件输入流,用于从文件系统中的某个文件中获得输入字节。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用FileReader。
  • FileOutputStream是文件输出流,用于将数据写入File或FileDescriptor的输出流。FileOutputStream用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用FileWriter。
  • Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
  • FileInputStream不支持mark方法与set方法。

关于FileInputStream与FileOutputStream就讲到这里,想了解更多内容请参考

  • Java8 I/O源码系列专栏-目录
版权声明 作者:潘威威

原文地址:CSDN博客-潘威威的博客-http://blog.csdn.net/panweiwei1994/article/details/78277714

本文版权归作者所有,欢迎转载。转载时请在文章明显位置给出原文作者名字(潘威威)及原文链接。请勿将本文用于任何商业用途。
阅读全文
1 0
原创粉丝点击