Java自学日记之IO流(二):转换流(InputStreamReader、OutputStreamWriter)

系列文章目录

Java自学日记之IO流(一):字节流和字符流



前言

上文讲到字节流和字符流在读取数据时都有不可替代的用处,字节流适合读取一些图片等字节数据,字符流适合处理汉字等字符数据,然而当我们处理较为复杂的数据时怎么办呢,这里就引入了转换流


一、转换流的作用

InputStreamReader用于将字节输入流转换为字符输入流,
OutputStreamWriter用于将字节输出流转换为字符输出流。
转换后的流中,
InputStreamReader使用指定的编码方式读取字节并将其解码为字符,
OutputStreamWriterr使用指定的编码方式将要写入流中的字符编码成字节,
由此构建起了字节流和字符流交互的桥梁


二、转换流的构造方法

有四种构造方式

public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException
public InputStreamReader(InputStream in, Charset cs)
public InputStreamReader(InputStream in, CharsetDecoder dec)

第一种是创建使用默认字符集的 InputStreamReader
第二三种都是创建使用指定字符集的 InputStreamReader
第四种是使用指定的CharsetDecoder(decoder)对象解码
值得注意的是前三种的使用指定字符集的流,到最后也会将Charset转化成CharsetDecoder

eg:

File file=new File("abc.txt");
//InputStreamReader
FileInputStream fileInputStream=new FileInputStream(file);
InputStreamReader inputStreamReader=new InputStreamReader(fileInputStream);
//OutputStreamWriter
FileOutputStream fileOutputStream=new FileOutputStream(file);
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(fileOutputStream);

输出和输入流构造方法相似,不过也有一点点不同
InputStreamReader内部定义了一个StreamDecoder对象
OutputStreamWriter内部定义了一个StreamEncoder对象
读入流存储了一个解码流,输出流存储了一个编码流

三、read()方法

有两种read()方法,都会调用sd.read()方法

public int read() throws IOException {
	return sd.read();
}
public int read(char cbuf[], int offset, int length) throws IOException {
	return sd.read(cbuf, offset, length);
}

跟进到StreamDecoder,有以下几个函数

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

@SuppressWarnings("fallthrough")
private int read0() throws IOException {
    synchronized (lock) {

        // Return the leftover char, if there is one
        if (haveLeftoverChar) {
            haveLeftoverChar = false;
            return leftoverChar;
        }

        // Convert more bytes
        char cb[] = new char[2];
        int n = read(cb, 0, 2);
        switch (n) {
        case -1:
            return -1;
        case 2:
            leftoverChar = cb[1];
            haveLeftoverChar = true;
            // FALL THROUGH
        case 1:
            return cb[0];
        default:
            assert false : n;
            return -1;
        }
    }
}

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);
    }
}

对于这三个函数,我找到了一个非常详细的源码解读博客,感兴趣的同学可以去看看
Java IO类库之InputStreamReader

四、write()方法

public void write(int c) throws IOException {
    se.write(c);
}

public void write(char cbuf[], int off, int len) throws IOException {
    se.write(cbuf, off, len);
}

public void write(String str, int off, int len) throws IOException {
    se.write(str, off, len);
}

直接跳到se.write()

public void write(int c) throws IOException {
    char cbuf[] = new char[1];
    cbuf[0] = (char) c;
    write(cbuf, 0, 1);
}

public void write(char cbuf[], int off, int len) throws IOException {
    synchronized (lock) {
        ensureOpen();
        if ((off < 0) || (off > cbuf.length) || (len < 0) ||
            ((off + len) > cbuf.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        implWrite(cbuf, off, len);
    }
}

public void write(String str, int off, int len) throws IOException {
    /* Check the len before creating a char buffer */
    if (len < 0)
        throw new IndexOutOfBoundsException();
    char cbuf[] = new char[len];
    str.getChars(off, off + len, cbuf, 0);
    write(cbuf, 0, len);
}

第一种和第三种最后都是把调用的数据转化成了字符数组,然后调用write的第二种构造
write判断完一系列边缘后,调用implWrite方法构造,继续跳

void implWrite(char cbuf[], int off, int len)
    throws IOException
{
    CharBuffer cb = CharBuffer.wrap(cbuf, off, len);
    implWrite(cb);
}

继续跳,,,算了不跳了,看到这个类名和下面的调用不难想到这个wrap是建立字符缓冲区的。
或者也可以跳进去看看,看看注释
在这里插入图片描述
嗯哼?
回到impWrite
这里又调用了implWrite(cb),继续跳吧

void implWrite(CharBuffer cb)
    throws IOException
{
    if (haveLeftoverChar) {
        flushLeftoverChar(cb, false);
    }

    while (cb.hasRemaining()) {
        CoderResult cr = encoder.encode(cb, bb, false);
        if (cr.isUnderflow()) {
            assert (cb.remaining() <= 1) : cb.remaining();
            if (cb.remaining() == 1) {
                haveLeftoverChar = true;
                leftoverChar = cb.get();
            }
            break;
        }
        if (cr.isOverflow()) {
            assert bb.position() > 0;
            writeBytes();
            continue;
        }
        cr.throwException();
    }
}

haveLeftoverChar,他的作用我去百度了一下

(haveLeftoverChar)特别为read准备,read方法要求返回一个字符,但是实际是读取2个字符,剩余1个字符会被赋予给leftoverChar变量,而haveLeftoverChar用于判断

第一次调用是false,所以先不管他

public final boolean hasRemaining() {
	return position < limit;
}

判断边界跳过
encoder.encode明显就是一个编码操作了,继续往下看
接下来就是判断堆栈是否为空或溢出,分别执行不同的读取函数,cb.get()或writeBytes(),接下来就不详细分析了,有兴趣的可以自行百度


总结

IO流进度真慢,一篇文章写了好几个小时,慢慢写吧
下一节应该会写字节数组流或者缓冲流

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aftermath123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值