【JAVA基础】FileInputStream和FileOutputStream 详解

父类简介

InputStream和OutputStream是抽象类,是所有字节输入流和输出流的父类。这里,我们首先要分清楚两个概念:

  • InputStream(输入流):输入流是用来读入数据的。- - - > > >读入

  • OutputStream(输出流):输出流是用来写出数据的。- - - > > >写出

FileInputStream和FileOutputStream 详解

文件输入流——FileInputStream

FileInputStream 从文件系统中的某个文件中获得输入字节。

 FileInputStream提供的API如下:

FileInputStream(File file)         // 创建“File对象”对应的“文件输入流”
FileInputStream(FileDescriptor fd) // 创建“文件描述符”对应的“文件输入流”
FileInputStream(String path)       // 创建“文件(路径为path)”对应的“文件输入流”
int      available()             // 返回“剩余的可读取的字节数”或者“skip的字节数”
void     close()                 // 关闭“文件输入流”
FileChannel      getChannel()    // 返回“FileChannel”
final FileDescriptor     getFD() // 返回“文件描述符”
int      read()                  // 返回“文件输入流”的下一个字节
int      read(byte[] buffer, int off, int len) // 读取“文件输入流”的数据并存在到buffer,从off开始存储,存储长度是len。
long     skip(long n)    // 跳过n个字节

源码分析:read (byte[] b, int off, int len) 和  skip(long n) 

public int read(byte b[], int off, int len) throws IOException {  
    if (b == null) {   // 检测参数是否为null  
        throw new NullPointerException();  
    } else if (off < 0 || len < 0 || len > b.length - off) {  
        throw new IndexOutOfBoundsException(); // 数组越界检测  
    } else if (len == 0) {  
        return 0;   //如果b为空数组,返回0  
    }  
  
    int c = read(); // 调用read()方法获取下一个字节  
    if (c == -1) {  
        return -1;  
    }               // 遇到流尾部,返回-1  
    b[off] = (byte)c;  //读入的第一个字节存入b[off]  
  
    int i = 1;    // 统计实际读入的字节数  
    try {  
        for (; i < len ; i++) { // 循环调用read,直到流尾部  
            c = read();  
            if (c == -1) {  
                break;  
            }  
            b[off + i] = (byte)c; // 一次存入字节数组  
        }  
    } catch (IOException ee) {  
    }  
    return i;  // 返回实际读入的字节数  
}  

public long skip(long n) throws IOException {  
  
     long remaining = n; // 还有多少字节没跳过  
     int nr;  
  
     if (n <= 0) {  
         return 0;  // n小于0 简单返回0  
     }  
  
     int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); // 这里的常数在类中定义为2048  
     byte[] skipBuffer = new byte[size]; // 新建一个字节数组,如果n<2048,数组大小为n,否则为2048  
     while (remaining > 0) {  
         nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); // 读入字节,存入数组   
         if (nr < 0) {  // 遇到流尾部 跳出循环  
             break;  
         }  
         remaining -= nr;  
     }  
  
     return n - remaining;  
 }

文件输出流——FileOutputStream

文件输出流是用于将数据写入到文件中。

 FileOutputStream提供的API如下:

FileOutputStream(File file)                   // 创建“File对象”对应的“文件输入流”;默认“追加模式”是false,即“写到输出的流内容”不是以追加的方式添加到文件中。
FileOutputStream(File file, boolean append)   // 创建“File对象”对应的“文件输入流”;指定“追加模式”。
FileOutputStream(FileDescriptor fd)           // 创建“文件描述符”对应的“文件输入流”;默认“追加模式”是false,即“写到输出的流内容”不是以追加的方式添加到文件中。
FileOutputStream(String path)                 // 创建“文件(路径为path)”对应的“文件输入流”;默认“追加模式”是false,即“写到输出的流内容”不是以追加的方式添加到文件中。
FileOutputStream(String path, boolean append) // 创建“文件(路径为path)”对应的“文件输入流”;指定“追加模式”。
void                    close()      // 关闭“输出流”
FileChannel             getChannel() // 返回“
FileChannel”final FileDescriptor    getFD()      // 返回“文件描述符”
void                    write(byte[] buffer, int off, int len) // 将buffer写入到“文件输出流”中,从buffer的off开始写,写入长度是len。
void                    write(int n)  // 写入字节n到“文件输出

write(int b)

这是唯一一个抽象方法,子类中必须提供具体的实现。这个方法写出一个字节到输出流中。仔细看一下参数,发现是int类型,也就是有4个字节,那是如何处理的呢?很容易想到,只取低8位,也就意味这int的取值范围为0-255.如果提供一个超出这个范围的参数,将自动把高的24位去掉,具体地说,如果给定一个b超出范围,则将b除以256取模。例如,传递一个256,实际上写入的是1。将这个值写入输出流之后,输出流的另一端如何解析,取决于另一端。例如,对于控制台,是将其转换成ASCII码输出。子类必须实现这一方法,例如,FileOutputStream就调用本地方法来实现这个方法。如果写出过程遇到错误,抛出IOException,例如说,这个流已经被关闭,则抛出这个异常。

write(byte b[], int off, int len)

上一个方法一次只写入一个字节,许多时候是不方便的,这个方法则写入data字节数组中的len字节到输出流。参数中data就是待写入的字节(不一定全部写入),从第offset位开始写入,off+len-1是最后一位被写入的。在OutputStream这个抽象类中,直接循环调用write(int b)方法,如下:

if (b == null) {  
            throw new NullPointerException();  
        } else if ((off < 0) || (off > b.length) || (len < 0) ||  
                   ((off + len) > b.length) || ((off + len) < 0)) {  
            throw new IndexOutOfBoundsException();  
        } else if (len == 0) {  
            return;  
        }  
        for (int i = 0 ; i < len ; i++) {  
            write(b[off + i]);  
        }API鼓励子类提供更高性能的实现方法。在这个方法中,由于字节本身就是8位,没有超出范围的情况,所以直接写入无需转换。如果传递的data为空,则抛出NullPointerException异常。如果被写入的部分超出字节数组范围,抛出IndexOutofBoundsException异常。具体地说,有以下5中情况,也就是if语句当中的5个表达式。除此之外,如果写入过程出现错误,一样抛出IOException。

flush()

这个方法是对Flushable接口的实现。如果在写出方法的具体实现中,用到了缓冲机制。则这个方法用于将缓冲区的数据“冲刷”到目的地去。这里需要特别理解,如果写出的方法用到了操作系统层的抽象,比如说FileOutputStream,那么该方法只能保证将缓冲区的数据提交给操作系统,但是不能保证数据被写入到磁盘文件中去。如果冲刷过程出现I/O 错误,抛出IOException。OutputStream类中的方法什么都不做。当输出流被关闭或者程序退出的时候,缓冲区的数据互自动被冲刷。对于System.out、System.err这样的输出流,当调用println()方法或者遇到换行符‘ ’的时候,缓冲数据自动被冲刷。当然,你可以在PrintStream的构造函数中传递参数来设置是否自动冲刷。

close()

这是对Closeable接口中close方法的实现。用于关闭输出流,释放相关的系统资源(如文件句柄(file handle)或者网络端口)。关闭之后,该输出流不能再被操作或者重新打开,否则抛出异常。当然,你可以不关闭,但是这不是一个好习惯,时常会出现严重问题。比如你打开文件,正在操作,那你不关闭,其他线程可能就会一直阻塞下去了。

640?wx_fmt=jpeg

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值