JAVA IO

JAVA I/O 流

这里写图片描述

基本概念就不介绍了,都知道输入/输出流,高级流都是继承于OutputStream/InputSream,
很多类的中方法都是一样的,使用都是一样的

1:ByteArrayInputStream /ByteArrayOutputStream

字节数组流,顾名思义

        byte buf[]= {61,62,63,64,65,66,67,68,69};

        //使用的buf为缓冲区数组。
        ByteArrayInputStream arrayInputStream=new ByteArrayInputStream(buf);
        System.out.println(arrayInputStream.read()); //61

        //在这里添加标记
        arrayInputStream.mark(0);

        //从输入流读入指定长度的数据到指定数组中
        byte buf1[]=new byte[2];
        arrayInputStream.read(buf1, 0, 2);//62, 63

        //available剩余可读数,上面总共读取3个后剩余6个,
        System.out.println(arrayInputStream.available());//6

        //上面读取了2个字段后,再继续读取输出64
        System.out.println(arrayInputStream.read());//64

        //流跳过2个字节
        arrayInputStream.skip(2); //65 66
        System.out.println(arrayInputStream.read());//67

        //这里rest重置流到上面标记的位置
        arrayInputStream.reset();
        System.out.println(arrayInputStream.read());//62

        System.out.println(arrayInputStream.available());//7

        //mark和reset必须配套使用
        byte buf[] = { 'a', 'b', 'c', 'd', 'e'};

        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();

        // 指定数组写入到ByteArrayOutputStream输出流中
        arrayOutputStream.write(buf, 0, 5);

        // 打印buf数组
        System.out.println(arrayOutputStream.toString());//abcde

        //重新接受一个字符
        arrayOutputStream.write(78);
        System.out.println(arrayOutputStream.toString());//abcdeN

        //如上我们第二次再接受一个字符时,因为流中已经有数据了,所以输出结果是跟在上一个数据后面
        //使用rest就是清空输出流中的数据
        arrayOutputStream.reset();
        arrayOutputStream.write(78);
        System.out.println(arrayOutputStream.toString());//N

        FileOutputStream fileOutputStream=new FileOutputStream(new File("path"));
        //writeTo:就是把ByteArrayOutputStream流的全部内容写入到指定的输出流参数
        arrayOutputStream.writeTo(fileOutputStream);

mark和reset必须配套使用,另外.mark(0)重点值大于0即可,他只是用来标记位置,比如你传递10 和传递0,
效果是一样的,这里的值并不代表流的偏移位置,知识记录当前位置,比如你读了5个字节后,在这里mark下
那么rest时读取数据的位置是在5字节这个位置开始读取

2:CharArrayReader/CharArrayWriter

字符数组流
方法和使用都和ByteArrayInputStream /ByteArrayOutputStream一模一样
区别:一个是字节流,一个是字符流,接受参数注意一个是byte数组一个是char数组

byte和char使用上一般人看起来没有区别,但是byte是有符号类型占一个字节表示-128-127,
char是无符号类型占两个字节表示0-65535

3:File文件和目录路径名的抽象表示

某些常规方法略

  • 创建文件目录 mkdirs和mkdir
        File file=new File("D:\\test\\test1\\test2");
        if(!file.mkdir()) {
                file.mkdir();
        }

文件目录创建mkdir和mkdirs的区别,
mkdir只能创建一集目录不能再多了,mkdirs可以创建多级目录
D:\test\test1\test2 如果这个文件夹不存在则只能mkdirs创建,
如果D:\test\test1这两级目录存在,要在test1里面再创建test2,那么此时用mkdir或者mkdirs没有区别,
如果D:\test目录存在,要在他下面创建test\test1两级目录,也只能用mkdirs

  • 创建临时文件对象createTempFile
//prefix文件名称前缀,suffix文件名称后缀
static File createTempFile(String prefix, String suffix) 
//directory在这个文件目录中创建这个临时文件
static File createTempFile(String prefix, String suffix, File directory) 

如上方法创建临时文件,注意只能创建文件对象,不能创建文件目录哦,
给出文件的前缀和后缀,系统使用随机生成一个名称,如果不指定文件生成的目录,
则文件位置系统给定,给出了文件生成的目录则文件在该目录下,如果是多级目录,则文件位置在最后一级目录里面。

  • 获取磁盘大小
 long getTotalSpace() //获取当前文件所处位置(分区)的总大小(单位字节)
 long getUsableSpace()//获取当前文件所处位置(分区)的剩余大小(单位字节)
  • 权限
 //这样只允许读操作的文件或目录
 boolean setReadOnly() 
 //true允许写操作 false 不允许写
 boolean setWritable(boolean writable) 
 //true 允许读插座, false不允许读
 boolean setReadable(boolean readable) 
  • 文件过滤Filter
```
 //查找当前目录下的所有文件或文件夹名,列出名称,注意不包含子目录哦
 File file=new File("D:\\");
 String list[]=file.list();
 for(String str:list) {
     System.out.println(str);
 }
 ----------------------------
 //同上,只是返回对象
 File[] listFiles() 
 ----------------------------
 //获取当前系统的根目录,电脑上来说就是系统分的几个区()
 File file=new File("");
 File list[]=file.listRoots();
 for(File str:list) {
    System.out.println(str.getAbsolutePath());//C:\,D:\,E:\,F:\
 }
----------------------------------
//过滤指定文件或者目录,通过FilenameFilter或者FileFilter 指定规则,只返回符合指定规则的文件和目录
            //FilenameFilter 和FileFilter用法一样,只是一个方法中返回对象,一个返回文件名称
            //String[] list(FilenameFilter filter) 
            //File[] listFiles(FileFilter filter) 
            //File[] listFiles(FilenameFilter filter)   

//          FilenameFilter filenameFilter=new FilenameFilter() {
//              
//              @Override
//              public boolean accept(File dir, String name) {
//                  // TODO Auto-generated method stub
//                  return false;
//              }
//          };


            FileFilter fileFilter=new FileFilter() {

                //accept方法返回文件对象或者路径,然后我们自定义规则,如果满足规则则return true
                //执行完成后满足规则的文件就会保存到数组中
                @Override
                public boolean accept(File pathname) {

                    if(pathname.getName().startsWith("test")) {
                        return true;
                    }

                    return false;
                }
            };

            File file=new File("D:\\");
            File list[]=file.listFiles(fileFilter);
            for(File str:list) {
                System.out.println(str.getAbsolutePath());
            }
  • 文件流操作相关FileInputStream/FileOutputStream/FileReader/FileWriter
        //FileInputStream/FileOutputStream用法
        FileOutputStream fileOutputStream = null;
        FileInputStream fileInputStream = null;

        try {

            String str = "helloWorld";
            File file = new File("D:\\test.txt");
            // 如果文件不存在,则新建一个文件
            if (!file.exists()) {
                file.createNewFile();
            }
            //写入数据
            fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(str.getBytes());

            // ----------------------------
            //创建字节数组
            byte buf[] = new byte[1024];
            fileInputStream = new FileInputStream(file);
            //循环读取数据写入buf数组,当数据为-1时表示数据读完
            while ((fileInputStream.read(buf, 0, buf.length)) != -1) {
                System.out.println(new String(buf));

        } catch (Exception e) {
        } finally {
            try {
                //关闭流
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                if(fileInputStream!=null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //FileReader/FileWriter
        FileWriter fileWriter=null; 
        FileReader fileReader=null;

        try {
            String str = "helloWorld";
            File file = new File("D:\\test.txt");
            // 如果文件不存在,则新建一个文件
            if (!file.exists()) {
                file.createNewFile();
            }
            fileWriter=new FileWriter(file);
            fileWriter.write(str);
        //刷新下 这里注意下,我们是先写入数据马上再去读取,如果不执行flush,你会发现下面的代码读不到数据
        //这是因为fileWriter的数据输出时还在缓存中,没有实际写到文件中,这里必须调用flush强制把内容写出
        //如果我们下面不马上读取数据,正常来说我们会调用close,调用close会自动调用flush,
        //最终也会把内容输出到文件中
            fileWriter.flush();
            //------------------------------
            //这里注意接收的是char字符数组哦。
            char buf[] = new char[1024];
            fileReader=new FileReader(file);
            //循环读取数据写入buf数组,当数据为-1时表示数据读完
            while ((fileReader.read(buf, 0, buf.length)) != -1) {
                System.out.println(new String(buf));
            }

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }finally {
            try {

                if(fileWriter!=null) {
                    fileWriter.close();
                }
                if(fileReader!=null) {
                    fileReader.close();
                }

            } catch (Exception e2) {
            }
        }
4:RandomAccessFile读取和写入随机访问文件。

FileInputStream/FileOutputStream/FileReader/FileWriter:

这四个是用来输入、输出文件流,但是这几个方法有个问题是不能随机访问流,意思就是一个4G文件你要在中间插入一段文字,或者你只需要读取文件内容2G-4G的内容,他们是不好处理的,如果硬要实现也可以实现,就是把流全部读取出来,然后取或者处理相关,但是这种后果是什么,内存撑爆

RandomAccessFile
RandomAccessFile支持随机访问流的位置,我们可以轻易的从指定位置开始获取我们要的内容
应用场景:常用的文件断点续传,在指定文件出插入、修改内容
RandomAccessFile(String name, String mode)
RandomAccessFile(File file, String mode)
mode:模式有4个值
r 代表以只读方式打开指定文件 。
rw 以读写方式打开指定文件 。
rws 读写方式打开,并对内容或元数据都同步写入底层存储设备 。
rwd 读写方式打开,对文件内容的更新同步更新至底层存储设备 。

rws 和rwd 区别不是很明白,但是有一点rws会把文件元数据更新同步到设备
元数据:简单点理解就是描述数据的数据,比如文件中的时间、大小等其他信息?或者是java代码中辅助描述方法类信息的数据

            //如果test.txt这个文件内容是空的,我们指向accessFile.seek(5),然后再去获取他的偏移结果还是5
            //这时候accessFile.writeBytes("adsads");写入数据,则文件内容前面会用5个空格占位
            //如果test.txt内容是123456,设置内容偏移5,然后写入数据adsads,结果是12345adsads
            //偏移下标是0开始,写入数据后会把从原有内容5偏移开始覆盖替换成adsads新写入的数据
            RandomAccessFile accessFile=new RandomAccessFile("D:\\test.txt", "rw");
            //getFilePointer()获取当前记录指向当前文件内容偏移位置,初始是0
            System.out.println( accessFile.getFilePointer());//0
            //seek()移动指向未见内容位置
            accessFile.seek(5);
            System.out.println( accessFile.getFilePointer());//5
            accessFile.writeBytes("adsads");

            //如下代码test.txt内容是123456
            //调用accessFile.writeBytes("a");写入数据a,此时getFilePointer内容偏移就变成了1
            //接着我们去accessFile.readLine()读取数据为23456,但是本地内容是a23456
            //这是因为当前程序中在文件头部添加数据后,文件内容偏移指针从0自动变成了添加数据长度
            //后面读取数据就相当于使用了seek()方法,

            RandomAccessFile accessFile=new RandomAccessFile("D:\\test.txt", "rws");//123456
            accessFile.writeBytes("a");
            System.out.println( accessFile.getFilePointer());//1
            System.out.println(accessFile.readLine());//23456

上面两段代码很好的展示了,我们可以在任意位置添加或者修改文件内容
RandomAccessFile中有seek(..)和skipBytes(int n) 两个方法,用法上没有多少区别
skipBytes方法内部最终还是调用的seek方法,找了很多文章也没发现他们本质区别,
有一点肯定的是skipBytes是相对定位,而seek是绝对定位,
意思skipBytes跳过的字节开始位置是相对于流上次所处的位置,而seek你0就是定位到文件开始,你20就是20的位置。

            //test.txt内容是123456789
             RandomAccessFile raf = new RandomAccessFile("D:\\test.txt", "rw");

             //先读取一次内容,读取后相当于文件指针偏移到文件末尾了
             System.out.println(raf.readLine());//123456789
             System.out.println(raf.getFilePointer());//9
             //此时我们是有skipBytes跳过2个字节,实际上他是在我文件末尾即9这里再跳过2个字节,
             //明显我们数据只有9个字节长度,这里再去读取数据肯定是null了
             raf.skipBytes(2);
             System.out.println(raf.readLine());//null
             System.out.println(raf.getFilePointer());//9

             //----------------------

             System.out.println(raf.getFilePointer());//9
             //seek(2)让文件指针偏移到2的位置,他是绝对定位,设置后文件指针偏移位置就是在2
             raf.seek(2);
             System.out.println(raf.getFilePointer());//2
             //所以这里打印的时候值就是3456789
             System.out.println(raf.readLine());//3456789

             //以上能清楚看到skipBytes的跳过是相对于上次位置再进行偏移
             //seek是绝对位置
5:FilterInputSream/FilterOutputSream/FilterWriter/FilterReader

这个流本身是不能直接使用,他提供的构造方法不是怕public的,他要基于他的子类来使用
BufferedInputStream/DataInputStream/LineNumberInputStream/PushbackInputStream

6:缓冲流BufferedInputStream/BufferedOutputStream/BufferedWriter/BufferedReader

使用很简单,BufferedOutputStream(OutputStream out)里面套一个流

重点说下缓冲流和其他流的区别与好处
  • 我们拿文件流和缓冲流来比较,在表面上看,使用方式和方法都一样,以为一样?
    源码:
FileOutputStream fileOutputStream=new FileOutputStream(new File(""));
            fileOutputStream.write(b, off, len);
            //查看write的源码,直接调用native 方法进行文件读写,
private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;
     //---------------------------------
BufferedOutputStream bufferedOutputStream=new  BufferedOutputStream(fileOutputStream);
            bufferedOutputStream.write(b, off, len);
            //先查看BufferedOutputStream的构造函数
            //在BufferedOutputStream对象创建时内部会创建一个byte数组,
            //如果不指定数组大小,内部默认大小为8192
public BufferedOutputStream(OutputStream out, int size) {
        super(out);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
      //再查看BufferedOutputStream 的write方法源码,注意flushBuffer();方法
        public synchronized void write(byte b[], int off, int len) throws IOException {
        if (len >= buf.length) {
            /* If the request length exceeds the size of the output buffer,
               flush the output buffer and then write the data directly.
               In this way buffered streams will cascade harmlessly. */
            flushBuffer();
            out.write(b, off, len);
            return;
        }
        if (len > buf.length - count) {
            flushBuffer();
        }
        System.arraycopy(b, off, buf, count, len);
        count += len;
    }
   //查看flushBuffer方法,这里明显看到,是把我们的流输入到buf刚才创建的数组中
    private void flushBuffer() throws IOException {
        if (count > 0) {
            out.write(buf, 0, count);
            count = 0;
        }
    }

通过上面的源码片段可以看出,尽管文件流和缓存流2个在方法上和使用上一模一样,
但是源码中可以清楚看到缓存流在构建对象时会自动生成一个buf数组的缓存区,
我们每次读写流时先写入到那个缓存区中去,等缓冲区满了再写入到磁盘
这样的好处就是可以减少磁盘IO,提高效率,对磁盘有好处,这样的缺点就是占内存
至于处理的文件的效率不是说用了缓冲流就绝对的快,这个还是看具体应用场景吧

  • BufferedWriter.newLine()在输出流中加入一个换行
StringWriter sw = null;
        BufferedWriter bw = null;
        String s = "Hello World!!";

        try {
            sw = new StringWriter();
            bw = new BufferedWriter(sw);
            bw.write(s, 0, 5);// Hello
            bw.newLine();// 换一行
            bw.write(s, 6, s.length() - 6);
            bw.flush();
            System.out.print(sw.getBuffer());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
        //结果Hello World!!分两行显示
        //hello
        //World
7:DataOutputStream/DataInputStream数据输入输出流

数据流:见名知意思,这个流主要是 从来输入输出数据流,

这里写图片描述

            File file=new File("D:\\test.txt");
            DataOutputStream dataOutputStream=new DataOutputStream(new FileOutputStream(file));
            //往文件中输入11.52f,不要试图去打开这个文件夹,因为现实不出来
            dataOutputStream.writeFloat(11.52f);

            //再读取这个数据结果11.52
            DataInputStream dataInputStream=new DataInputStream(new FileInputStream(file));
            System.out.println(dataInputStream.readFloat());
  • 数据流的应用举例,把如下图片的数据按照指定格式把数据存入文本中,然后读取出来

这里写图片描述

        DataOutputStream dos = null ;           // 声明数据输出流对象  
        File f = new File("d:" + File.separator + "order.txt") ; // 文件的保存路径  
        dos = new DataOutputStream(new FileOutputStream(f)) ;   // 实例化数据输出流对象  
        String names[] = {"衬衣","手套","围巾"} ; // 商品名称  
        float prices[] = {98.3f,30.3f,50.5f} ;      // 商品价格  
        int nums[] = {3,2,1} ;  // 商品数量  
        for(int i=0;i<names.length;i++){ // 循环输出  
            dos.writeChars(names[i]) ;  // 写入字符串  
            dos.writeChar('\t') ;   // 写入分隔符  
            dos.writeFloat(prices[i]) ; // 写入价格  
            dos.writeChar('\t') ;   // 写入分隔符  
            dos.writeInt(nums[i]) ; // 写入数量  
            dos.writeChar('\n') ;   // 换行  
        }  
        dos.close() ;   // 关闭输出流  
        //-----------------------------------------------
        DataInputStream dis = null ;        // 声明数据输入流对象  
        File f = new File("d:" + File.separator + "order.txt") ; // 文件的保存路径  
        dis = new DataInputStream(new FileInputStream(f)) ; // 实例化数据输入流对象  
        String name = null ;    // 接收名称  
        float price = 0.0f ;    // 接收价格  
        int num = 0 ;   // 接收数量  
        char temp[] = null ;    // 接收商品名称  
        int len = 0 ;   // 保存读取数据的个数  
        char c = 0 ;    // '\u0000'  
        try{  
            while(true){  
                temp = new char[200] ;  // 开辟空间  
                len = 0 ;  
                while((c=dis.readChar())!='\t'){    // 接收内容  
                    temp[len] = c ;  
                    len ++ ;    // 读取长度加1  
                }  
                name = new String(temp,0,len) ; // 将字符数组变为String  
                price = dis.readFloat() ;   // 读取价格  
                dis.readChar() ;    // 读取\t  
                num = dis.readInt() ;   // 读取int  
                dis.readChar() ;    // 读取\n  
                System.out.printf("名称:%s;价格:%5.2f;数量:%d\n",name,price,num) ;  
            }  
        }catch(Exception e){}  
        dis.close() ;  
8:PrintStream/PrintWriter/Scanner

Scanner不属于流中,空用于控制台进行输入操作
PrintStream/PrintWriter/:忽略设备底层的差异,进行一致的IO操作。因此这种流也称为处理流或者包装流。

        //控制台输入一个数  
        Scanner scanner=new Scanner(System.in);
        String str=scanner.nextLine();
        //打印出来
        PrintWriter printWriter=new PrintWriter(System.out);
        printWriter.print(str);
        //记住使用flush刷新
        printWriter.flush();

System.in/System.out标准的输入输出设备

        //使用PrintWriter 给文件中输入数据
        FileWriter fileWriter=  new FileWriter(new File("D:\\test.txt"));
        PrintWriter printWriter=new PrintWriter(fileWriter);
        printWriter.write("123123");    
        printWriter.flush();

        FileOutputStream  fileOutputStream=new FileOutputStream(new File("D:\\test.txt"));
        PrintStream printStream=new PrintStream(fileOutputStream);
        printStream.println("dsadsa");
        //记住使用flush刷新
        printWriter.flush();
9:ObjectOutputStream/ObjectInputStream对象输入输出流

用于java对象的保存和读取,
注意对象必须要序列化

     //创建一个user对象,注意user必须序列化,否则读取时会报错
            User user=new User();
            user.setUserName("chenxin");
            user.setUserPhone(123456789);
            //把这个对象写入文件中
            File file=new File("D:\\test.txt");
            ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));
            objectOutputStream.writeObject(user);
            objectOutputStream.flush();

            //读取这个对象,注意对象必须序列化,否则读取时会报错
            ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));
            User readUser=(User)objectInputStream.readObject();
            System.out.println(readUser.getUserName());
            System.out.println(readUser.getUserPhone());
10:SequenceInputStream 合并输入流

SequenceInputStream 和并输入流,只能和平输入,未提供输出,
把多个输入流和合拼后一起输入到指定文件中

          //创建一个集合把添加多个输入流
            Vector<FileInputStream> vector = new Vector<FileInputStream>();
            vector.addElement(new FileInputStream(new File("D:\\test1.txt")));
            vector.addElement(new FileInputStream(new File("D:\\test2.txt")));
            vector.addElement(new FileInputStream(new File("D:\\test3.txt")));

            //创建一个枚举接口
            Enumeration<FileInputStream> enumeration=vector.elements();
            SequenceInputStream sequenceInputStream=new SequenceInputStream(enumeration);

            byte[] buf = new byte[1024];
            int len = 0;
            while((len=sequenceInputStream.read(buf))!=-1)
            {
                System.out.println(new String(buf,0,len));
            }
11:PipedInputStream/PipedOutputStream管道输出流和管道输入流

PipedInputStream/PipedOutputStream必须配套使用
他的最主要的作用就是2个线程间通信
我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现,线程A和线程B的通信。
connect(..)两个类都有这个方法,就是关联另外一个流,两个类的这个方法是等价的,使用 一个即可

**Demo:创建一个Sender 类(线程),里面使用Scanner 来在控制台输入数据,
每次输入后PipedOutputStream 就write一次创建一个Reciver (线程)来接收Sender 发过来的数据,并打印**

public class Sender extends Thread {

    private PipedOutputStream out = new PipedOutputStream();

    public PipedOutputStream getPipedOutputStream() {
        return out;
    }

    @Override
    public void run() {
        super.run();
        wirteMessage();
    }

    private void wirteMessage() {

        try {
            Scanner scanner = new Scanner(System.in);
            while (true) { //接收一次输入,然后就写出一次
                String str= scanner.nextLine();
                out.write(str.getBytes());
            }
        } catch (Exception e) {
        }
    }

}
public class Reciver extends Thread {

    private PipedInputStream in = new PipedInputStream();

    public PipedInputStream getPipedInputStream() {
        return in;
    }

    @Override
    public void run() {
        readMessage();
        super.run();
    }

    private void readMessage() {

        try {

            byte buf[]=new byte[1024];
            int len=0;
            while((len=in.read(buf))!=-1) {
                System.out.println("接收到的数据是:"+new String(buf,0,len));
            }

        } catch (Exception e) {
        }

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

        try {

            Sender sender = new Sender();
            Reciver reciver = new Reciver();

            PipedInputStream in = reciver.getPipedInputStream();
            PipedOutputStream out = sender.getPipedOutputStream();

            in.connect(out);
//          out.connect(in);一样的

            sender.start();
            reciver.start();

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
12:PushbackInputStream/PushbackReader回推(回退)流

此流只有提供输入流,没有输出流
回推流,把某个数据回推到当前流的偏移位置

            String s = "abcdefg";
            ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes());
            PushbackInputStream pbin = new PushbackInputStream(in);         
            int temp;
            while ((temp = pbin.read()) != -1) {
                System.out.println("--------------------out:"+(char)temp) ;  
                if(temp=='a'){  
                    pbin.unread('u') ;  //这里回推U之后,再去读取数据就是U
                    //pbin.unread(tmep) ;结果死循环,
                }
            }

            //结果:aubcdefg

解读上面代码,当我们读到a时,就回推字符u,此时流里就回推到a之前的位置,带上数据u,此时我们再取数据就取出u
为什么是在a的位置呢?

//查看流读取的源码知道,当我们读取流时通过偏移指针知道pos++,先取出值,然后pos+1
//即我们读取第一个字节完成后,pos偏移是值1
 public synchronized int read() {
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }
    //unread回推方法源码偏移--pos, 其实就是回到上个字节位置,再赋值
 public void unread(int b) throws IOException {
        ensureOpen();
        if (pos == 0) {
            throw new IOException("Push back buffer is full");
        }
        buf[--pos] = (byte)b;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值