StringReader类JDK1.7源码详解

一、StringReader类的用法

1.根据流的使用规律:
  • 明确读操作还是写操作:
    用来读取一个String类型的字符串
  • 明确操作基类(操作字节还是字符)
    StringReader操作的是字符
  • 明确操作的具体类(具体介质)
    操作的具体介质:内存
2.既然都有了Reader,还要StringReader干什么?(应用场景)

有的情况下使用一个Reader来作为参数传递参数,但数据源又仅仅是一个String类型数据,无需从文件中写出,那么此时就可以用到StringReader。

3.具体方法应用示例

方法介绍:

  • 判断此流是否支持markSupported()
  • 标记流中的当前位置 mark()
  • 读取单个字符 read(),如果读到流的末尾,返回-1,操作不当会抛出IO异常
  • 将字符读入数组的某一部分 read(char[] cbuf,int off,int len),off存开始在数组中存储数据的起点,操作不当会抛出IO异常
  • 判断此流是否已经准备好用于读取 boolean ready()
  • 将该流重置为最新的标记,如果从未标记,则将其重置到字符串开头 reset()→将当前读取位置回退到流中的上次标记的位置
  • 跳过流中指定数量的字符返回跳过的字符数skip(long n),操作不当会抛出IO异常
  • 关闭流close()
public class F_StringReader326 {

    public static String Reader(String cont) throws IOException {

        //只有一个构造方法 :StringReader(String s) 创建一个新字符串
        StringReader stringReader = new StringReader(cont);

      
        //判断是否准备好用于读取
        System.out.println(stringReader.ready());

        //判断是否支持标记
        boolean a = stringReader.markSupported();
        System.out.println(a);

        stringReader.mark(2); //标记位置

        stringReader.skip(3);//跳过3个字符

        //读取单个字符
        String  s1 = "";
        int s;
        while ((s = stringReader.read())!= -1){
            s1 += (char)s; //将读取的字符串进行拼接
        }

        //批量读取:读取到char类型的数组中,第二个参数为数组中的偏移量,第三个参数为读取的长度
        char[] ch = new char[10];
        stringReader.read(ch,0,5);


        stringReader.close();//关闭流 ,在关闭之后调用流的其他方法时会抛出IO异常
        return s1;


    }
    public static void main(String[] args) throws IOException {

        System.out.println(Reader("StringReader"));//打印读取的数据
        }
    }

在这里插入图片描述

二、源码剖析

  1. 首先先看一下整体结构
    在这里插入图片描述
  2. 属性分析:
    private String str; //内置了一个String类型的变量。用于存储读取的内容
    private int length;  //读取数据的长度
    private int next = 0; //下一个要读取的位置,相当于指针
    private int mark = 0; //标记的位置
  1. 构造方法:
    public StringReader(String s) { //通过String类型的s来初始化内置的str和length属性
        this.str = s;
        this.length = s.length();
    }
  1. 具体方法介绍:
  • ensureOpen() 判断当前流是否属于开启状态
  private void ensureOpen() throws IOException {
        if (str == null)  //如果当前字符串为null,则抛出IO异常,声明流已经关闭!
            throw new IOException("Stream closed");
    }
  • read() 读取一个字符
    public int read() throws IOException {
        synchronized (lock) {  //有安全锁修饰,表示线程安全
            ensureOpen();   //判断流是否处于开启状态
            if (next >= length)   //如果下一次读取的位置比数据的总长度还大,表示已经读到结尾
                return -1;
            return str.charAt(next++);  //调用String的charAt方法,读取下一个数据
        }
    }

String的charAt方法:首先对下标进行安全校验,下标合格则直接返回当前下标所对应的字符
在这里插入图片描述

  • read(char cbuf[], int off, int len) 批量读取数据
   public int read(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 0;
            }
            if (next >= length)
                return -1;
            //调用数学方法,求最小值:判断length-next和len的大小,
            //(length-next:比如共有10个数据,参数给定一次读取6个,剩下4个数据,就是length-next。就可以将整个数据读完)    
            int n = Math.min(length - next, len);
			//使用String类的getChars方法,将指定str从next到next+n位置的数据拷贝到传入cbuf中,拷贝位置从off开始
            str.getChars(next, next + n, cbuf, off);
            next += n;//更新下一次读取的位置
            return n;  //返回n(实际读取的数据长度)
        }
    }

String类的getChar()方法:底层调用了System.arrayCopy()方法进行批量拷贝
在这里插入图片描述

  • skip(long ns) 跳过指定个数的字符
    public long skip(long ns) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (next >= length)
                return 0;
            //和批量读取的原理一样,为了防止最后的数据不够规定的长度。
            long n = Math.min(length - next, ns);  
            //处理传入的ns为负数的情况,上一步如果ns为负数的话此时的n是ns
			n = Math.max(-next, n); 
            next += n;//更新下一次读取的位置
            return n;
        }
    }
  • ready() 判断是否准备好用于读取
    public boolean ready() throws IOException {
        synchronized (lock) {  
        ensureOpen();  //判断流是否打开
        return true;
        }
    }
  • markSupported() 判断是否支持mark(标记)
    public boolean markSupported() {
        return true;
    }
  • mark() 标记位置,和reset方法联合使用,可以回退上一次标记的位置
    public void mark(int readAheadLimit) throws IOException {
        if (readAheadLimit < 0){  //参数校验
            throw new IllegalArgumentException("Read-ahead limit < 0");
        }
        synchronized (lock) {
            ensureOpen(); 
            mark = next;  //将当前读取的位置用mark标记
        }
    }
  • reset() 重置标记
    public void reset() throws IOException {
        synchronized (lock) {
            ensureOpen();
            next = mark; //将mark标记的位置赋给next,将当前位置改为为上一次读取的位置
        }
    }
  • close() 关闭流
    public void close() {
        str = null; //直接将内置的字符串置为null
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值