io流函数略解(java_input流)[二]

背景

在写这篇随笔之前,已经写了io流函数略解(java_File)(一),主要是总结了File的一些操作,以及一些源码介绍。
在Io实际应用中,实际上运用在如果会操作File,实际上很难写出一点能实际应用的code,因为操作文件嘛,更多的是操作流,也就是steam。
下面将简单总结一些流的概念,以及流的一些基本理论,同时也会贴出源码来略看。

实践

io之所以叫io,i的意思是input,o的意思是output,也就是一个输入一个输出,分别对应read与write。

inputsteam

inputsteam 在java 中是一个abstract class。那么它和接口是不一样的,抽象类是可以有具体方法的甚至构造函数。
inputsteam是read操作,那么看下在inputsteam有什么read的函数吧。

/**
 * Reads the next byte of data from the input stream. The value byte is
 * returned as an <code>int</code> in the range <code>0</code> to
 * <code>255</code>. If no byte is available because the end of the stream
 * has been reached, the value <code>-1</code> is returned. This method
 * blocks until input data is available, the end of the stream is detected,
 * or an exception is thrown.
 *
 * <p> A subclass must provide an implementation of this method.
 *
 * @return     the next byte of data, or <code>-1</code> if the end of the
 *             stream is reached.
 * @exception  IOException  if an I/O error occurs.
 */
public abstract int read() throws IOException;

read 没有实现,是一个抽象的方法。但是告诉了我们很有用的信息。
如下:

  1. 返回的是一个字节,返回是0-255。为什么是0-255呢?因为一个字节是8位,11111111不就是255嘛。
  2. 如果没有了,则返回-1,为什么会返回-1,因为-1最高效。解释起来很复杂,可以关注我后面总结的数据结构。
  3. 如果错误会返回一个IOException 异常。

同样,我找到了另外一个read

public int read(byte b[], int off, int len) throws IOException {
//判断参数是否符合,比如说byte是否为空,然后off与len的一些基本要求,比如说一个正常的off肯定要>0,然后len>0,len还有大于b.length-off
//在看到 b.length - off的时候就可以确定off是针对b[]的,冲off开始,给b[]写入或者替换数据。
    if (b == null) {
        throw new NullPointerException();
    } else if (off < 0 || len < 0 || len > b.length - off) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return 0;
    }
//为什么要单独写一个呢?一个是优化,不需要构造for循环,第二个是可以提前检查read错误
    int c = read();
    if (c == -1) {
        return -1;
    }
    b[off] = (byte)c;
//for 循环读取,然后read -1则说明到底了。
    int i = 1;
    try {
        for (; i < len ; i++) {
            c = read();
            if (c == -1) {
                break;
            }
            b[off + i] = (byte)c;
        }
    } catch (IOException ee) {
    }
    return i;
}

这个我就不贴注释了,有源码看啥注释。一些关键点,我也给了自己的一些看法。

然后还有一个是:

public int read(byte b[]) throws IOException {
    return read(b, 0, b.length);
}

其实就是调用read(byte b[], int off, int len);
至此,read部分就基本在这了,当然有其他函数了,不可能一一来说明,用到的时候自然就ok的。

FileInputStream

我们在inputsteam有了一个大体的框架,然而呢,read没有实现。那么来看看到底是如何读取文件的吧,FileInputStream。
依然我们来看read:

public int read() throws IOException {
        return read0();
}
private native int read0() throws IOException;

出现了native,这表示是调用外部库。native解释起来有一丢丢麻烦,就是去调用不是java写的库了,例如调用c语言写的函数库,后面也写一片总结吧。
好吧,read只能暂时介绍到这里,操作一下吧。

 try(InputStream inputStream= new FileInputStream("xxxx"))
 {
     int n;
     while ((n=inputStream.read())!=-1) {
        System.out.println(n);

    }
 }catch (Exception e) {
    // TODO: handle exception
}

ps:

try(InputStream inputStream= new FileInputStream("xxxx"))这样写自动在finally中帮我们调用close方法,因为InputStream 继承了java.lang.AutoCloseable 接口。
为什么要close呢?因为要释放资源啊,用完就放,轻装前行。

ByteArrayInputStream

这个从字面意思是字节数组输入流?意思就是把字符数组转换成InputStream。
例如:

public void ByteArrayInputStreamTest() throws IOException  {
  byte[] data={11,12,15,16};
  try(InputStream inputStream=new ByteArrayInputStream(data))
  {
      int n;
      while ((n = inputStream.read()) != -1) {
      }
  }
}

来看看源码实现吧:

  1. 看看它的超类
ByteArrayInputStream extends InputStream

这就解释了为什么可以这样写:

InputStream inputStream=new ByteArrayInputStream(data)
  1. 实例化:
public ByteArrayInputStream(byte buf[]) {
    this.buf = buf;
    this.pos = 0;
    this.count = buf.length;
}

在这里我们可以想到read(byte b[], int off, int len),其实就是模拟把文件中所有的字节都读出来了,然后给了里面的一个buf 缓存属性。

Reader

InputStream 关于字节流的,Reader 是关于字符流的。
我们知道字节是byte,字符是char,两者存在千丝万缕的关系,他们中间的桥梁是编码。编码又是一个相当难以用一两句话解释的东西了,后续会添加一篇编码的随笔。
总之,看下Reader 到底干什么的吧。

// 读取单个字符
public int read() throws IOException {
    char cb[] = new char[1];
    if (read(cb, 0, 1) == -1)
        return -1;
    else
        return cb[0];
}
// 抽象没得实现
abstract public int read(char cbuf[], int off, int len) throws IOException;
//调用了抽象read(char cbuf[], int off, int len)
public int read(char cbuf[]) throws IOException {
    return read(cbuf, 0, cbuf.length);
}

好吧,没有什么具体的实现,那么就去看看InputStreamReader吧,它的一个实现类。

InputStreamReader

根据上文,我们迫切需要知道的是abstract public int read(char cbuf[], int off, int len) throws IOException的实现方法。

public int read(char[] cbuf,
    int offset,
    int length) throws IOException 

{
        int off = offset;
        int len = length;
        synchronized (lock) {
            ensureOpen();
            if ((off <  0) || (off  > cbuf.length) || (len <  0) ||
                ((off + len)  > cbuf.length) || ((off + len) <  0)) {
                throw new IndexOutOfBoundsException();
            }
            if (len == 0)
                return 0;
            int n = 0;
            if (haveLeftoverChar) {
                // Copy the leftover char into the buffer
                cbuf[off] = leftoverChar;
                off++; len--;
                haveLeftoverChar = false;
                n = 1;
                if ((len == 0) || !implReady())
                    // Return now if this is all we can produce w/o blocking
                    return n;
            }
            if (len == 1) {
                // Treat single-character array reads just like read()
                int c = read0();
                if (c == -1)
                    return (n == 0) ? -1 : n;
                cbuf[off] = (char)c;
                return n + 1;
            }
            return n + implRead(cbuf, off, off + len);
        }
}

关键部分:

int c = read0();
if (c == -1){
  return (n == 0) ? -1 : n;
}
cbuf[off] = (char) c;

上文中提及到read0()是读取一个字节,然后把字节转换成字符。
ok,那么我们就知道原理了。
实践一下吧:

public void readFile() throws IOException {
    try (Reader reader = new FileReader("xxxx")) {
        char[] buffer = new char[1000];
        int n;
        while ((n = reader.read(buffer)) != -1) {
        }
    }
}

CharArrayReader与StringReader

简单说明一下他们俩吧。

char[] test={'a','b'};
try (Reader reader = new CharArrayReader(test)) {
}
try (Reader reader = new StringReader("xxx")) {
}

就是把字符数组或者字符串专成了Reader。
以CharArrayReader为例:

  1. 继承:
    public class CharArrayReader extends Reader
  2. 实例化
public CharArrayReader(char buf[]) {
    this.buf = buf;
    this.pos = 0;
    this.count = buf.length;
}
  1. 读取
public int read() throws IOException {
    synchronized (lock) {
        ensureOpen();
        if (pos >= count)
            return -1;
        else
            return buf[pos++];
    }
}

就是模拟了假如全部的读取文件中的所有数据,然后转换成了char[],缓存起来。

总结

1.不管是一次性读取byte[]还是一个一个读byte,原理上都是一个一个读的,只是byte[] 存储起来了。
2.读取字符流其实是在读取字节后转换的。
3.避免忘记close,推荐使用try(){}这种语法。
4.对于像ByteArrayInputStream 这样的转换,其实是假设数据全部读取出来了,然后进行操作。

原文地址:https://www.cnblogs.com/aoximin/p/12044270.html

时间: 2024-10-14 00:44:44

io流函数略解(java_input流)[二]的相关文章

Java IO(input output)流&lt;二&gt;

一.字符流的缓冲区 1.什么是缓冲区       缓冲区又称为缓存,它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据, 这部分预留的空间就叫做缓冲区.缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区. 2.为什么要引入缓冲区 缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据.它使得低速的输入输出设备和高速的CPU能够协调工作,避免低 速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作. 3.字符

Java IO流(二)

目录 Java IO流(二) 7. 字节缓冲流 7.1 概述 7.2 BufferedOutputStream类 7.3 BufferedInputStream类 8. 文件复制练习(增强版 使用缓冲流) 9. 字符缓冲流 9.1 BufferedWriter类 9.2 BufferedReader类 10. 练习:文本排序 11. 转换流 11.1 字符编码和字符集 11.2 编码引出的问题 11.3 转换流的原理 11.4 OutputStreamWriter类 11.5 InputStre

java中的io系统详解

java中的io系统详解 分类: JAVA开发应用 笔记(读书.心得)2009-03-04 11:26 46118人阅读 评论(37) 收藏 举报 javaiostreamconstructorstringbyte 相关读书笔记.心得文章列表 Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他

IO(Input Output)流___字符流、字节流

一.IO流简述------用于操作数据的 IO流用来处理设备之间的数据传输; Java对数据的操作是通过流的方式; Java用于操作流的对象都是在IO包中; 流按操作数据分为: 字节流(通用)和字符流: 按流向分为: 输入流和输出流. 字节流的抽象基类: InputStream , OutputStream 字符流的抽象基类: Reader, Writer 注: 由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀. 如: InputStream的子类FileInputStream :Re

Virtualization-Cpu/Memory/IO虚拟化详解

一.定义 虚拟化是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机.在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率. 虚拟化技术可以扩大硬件的容量,简化软件的重新配置过程.CPU的虚拟化技术可以单CPU模拟多CPU并行,允许一个平台同时运行多个操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率. 二.虚拟化的类别 虚拟化的类别有很多,定义也很宽泛:无

java中的io系统详解(转)

Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他编码的字符流和 java 内 Unicode 字符流之间的转换.而类 InputStreamReader 和 OutputStreamWriter 处理字符流和字节流的转换.字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高

java中io的详解

注:本文全篇转载于:http://blog.csdn.net/taxueyingmei/article/details/7697042,觉得讲的挺详细,就借过来看看,挺不错的文章. 先贴一张图 Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他编码的字符流和 java 内 Unicode 字符

Java IO编程全解(六)——4种I/O的对比与选型

转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7804185.html 前面讲到:Java IO编程全解(五)--AIO编程 为了防止由于对一些技术概念和术语的理解或者叫法不一致而引起歧义,这里对涉及到的专业术语或者技术用语做下声明:如果它们与其他一些地方的称呼不一致,请以本解释为准. 异步非阻塞I/O 很多人喜欢将JDK1.4提供的NIO框架成为异步非阻塞I/O,但是,如果严格按照UNIX网络编程模型和JDK的实现进行区分,实际上它只能被称为非阻塞I/

Java IO编程全解(三)——伪异步IO编程

转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7723174.html 前面讲到:Java IO编程全解(二)--传统的BIO编程 为了解决同步阻塞I/O面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化,后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远远大于N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽. 下面,我们结合连接模型图和源