Java----IO详解

IO是java中很重要的一块,包含了对文件,文件目录,二进制流文件等各种资源的操作,同时IO操作复杂多变,用得好会提高性能,用的不好导致效率低下,所以详细了解并掌握IO相关知识是很有必要的。

IO主要分为字节流和字符流,然后每一种又分为输入流和输出流,涉及到的类很多,下面一一分析。

字节流—-InputStream和OutputStream

先上类图,InputStream这个大家庭的:

这里写图片描述

(1)文件流:FileInputStream
    public static void testStream(){
        try {
            byte [] bytes = new byte [12];
            FileInputStream fis = new FileInputStream(new File("D:\\javaFile\\test/hah.txt"));
            while(fis.read(bytes)!=-1){
                System.out.println("Each:"+new String(bytes));
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

说明:FileInputStream是没有缓冲功能的,但是上面的程序里fis将读出的字节放到了字节数组bytes中实现了缓存功能。

while(fis.read()!=-1) //这样就没有缓存功能,一个一个字节读取
(2)对象流:ObjectInputStream
    public static void testStream(){
        try {
            //为了读取,我们先写一个文件,注意这个文件是二进制的,所以打开看是乱码
            FileOutputStream fos = new FileOutputStream("D:\\javaFile\\test\\student.txt");
            ObjectOutputStream oop = new ObjectOutputStream(fos);
            oop.writeObject(new Student(001 , "wangliang" , true , 22));
            oop.writeObject(new Student(002 , "lisi" , false , 22));
            //开始读取
            FileInputStream fis = new FileInputStream("D:\\javaFile\\test\\student.txt");
            ObjectInputStream ons = new ObjectInputStream(fis);
            //一次只能读取一个Object对象出来
            Student stu = (Student) ons.readObject();

            System.out.println("age:"+stu.stuAge);
            System.out.println("No:"+stu.stuId);
            System.out.println("name:"+stu.stuName);
            System.out.println("sex:"+(stu.stuSex==true?"男":"女"));

        } catch (IOException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

输出结果如下:

age:22
No:1
name:wangliang
sex:

从结果可以看出,我们写入了两个对象,但是只读出了一个,那么应该怎样读出所有的对象呢?方法是循环读取,用EOFException作为边界结束,像这样:

try{
            while(true){
            Student stu = (Student) ons.readObject();
            System.out.println("age:"+stu.stuAge);
            System.out.println("No:"+stu.stuId);
            System.out.println("name:"+stu.stuName);
            System.out.println("sex:"+(stu.stuSex==true?"男":"女"));
            }
            }catch(EOFException e){}

如果你觉得上面的方式不那么优雅的话,还可以这样:

//如果插入是可控的,那么可以在结尾插入一个结束标记符,一般为null.但是如果还要追加数据的话就比较麻烦了。
oop.writeObject(null);
//然后在读取的时候读到这个结束符就判定结束了
while(true){
            Student stu = (Student) ons.readObject();
            if(stu == null) break;
            System.out.println("age:"+stu.stuAge);
            System.out.println("No:"+stu.stuId);
            System.out.println("name:"+stu.stuName);
            System.out.println("sex:"+(stu.stuSex==true?"男":"女"));
            }
(3)字节数组流:ByteArrayInputStream

ByteArrayInputStream的两个构造函数都需要接受一个byte数组,不同于其他流接受文件或者流。

        //ByteArrayInputStream
        byte[] byteArray = {'a','b',8,'c','d','e',-128,127,0,-1,2,'i'};
        ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
        int tem;
        while((tem = bais.read())!=-1){
            //在读取到8后,跳过后面的c,d,e三个字节
            if(tem==8)
                bais.skip(3);
            System.out.println("byte:"+tem);
        }

这个类我不知道存在的意义是什么?在知乎上找到一个回答是说将byte数组转换为InputStream,从而可以使用一些InputStream的API,但是我个人觉得直接操作byte数组更方便啊。欢迎留言讨论。

(4)字符流:StringBufferInputStream

这个用作将一个字符串转换为流对象,现在早已被标记out了,可以用 StringReader或者ByteArrayInputStream代替。

(5)顺序合并流:SequenceInputStream

这个流的作用呢,就是将几个流按照顺序一个一个去读取出来,可以保证顺序。

//第一种写法
            FileInputStream fis = new FileInputStream("D:\\javaFile\\test\\hah.txt");
            FileInputStream fis1 = new FileInputStream("D:\\javaFile\\test\\hah1.txt");
            SequenceInputStream sis = new SequenceInputStream(fis , fis1);
            byte[] temp = new byte [1024];
            while(sis.read(temp)!=-1){
                System.out.println("Sequence:"+new String(temp));
            }
            //第二种写法
            Enumeration en;
            Vector<FileInputStream> vectors = new Vector<FileInputStream>();
            vectors.addElement(fis1);
            vectors.addElement(fis);
            en = vectors.elements();
            SequenceInputStream sis2 = new SequenceInputStream(en);
            byte[] temp2 = new byte [1024];
            while(sis2.read(temp2)!=-1){
                System.out.println("Sequence2:"+new String(temp2));
            }
(6)管道流:PipedInputStream和PipedOutputStream

这两个必须配套使用,管道流用于跨线程数据交互。首先将PipedInputStream和PipedOutputStream通过connect方法绑定,然后在A线程中通过PipedOutputStream写数据,此时,写入的数据会发送到与之关联的PipedInputStream中,在线程B中就可以读取数据了。

//管道流测试
        //创建输入输出流
        final PipedOutputStream pos = new PipedOutputStream();
        final PipedInputStream pis = new PipedInputStream();
        try {
            //关联起来
            pos.connect(pis);
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        //线程A发数据
        new Thread(){
            @Override
            public void run(){
                try {
                    pos.write("hello,i come from thread A!".getBytes());
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }.start();
        //线程B接受数据
        new Thread(){
            @Override
            public void run(){
                byte [] temp = new byte[27];
                try {
                    pis.read(temp);
                    System.out.println("来自线程A的消息:"+new String(temp));
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }.start();
(7)回退流:PushbackInputStream

在读取下一个字节(或者下一个字节数组)前用unread推回流的前端,注意这个会影响流的mark和reset方法,使用者两个方法前一定要先判断。

//PushbackInputStream测试
        try {
            FileInputStream fis = new FileInputStream("D:\\javaFile\\test\\hah.txt");
            //最多可以插入512个字节,是总共的字节数,不是一次的
            PushbackInputStream pbis = new PushbackInputStream(fis ,30 );
            byte [] temp3 = new byte[5];
            while(pbis.read(temp3)!=-1){
                if(new String(temp3).equals("hello")){
                    pbis.unread("i am contents".getBytes());
                    //continue;
                }
                System.out.println("PushbackInputStream:"+new String(temp3));
            }
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

输出结果是:hello,i am contents

(8)缓存流:BufferedInputStream

关于这个流需要好好说道说道;首先,看到这个流,再联想到上面讲FileInputStream时使用缓存时的场景,你可能会疑问有什么区别呢?FileInputStream用read(byte [])也可以缓存啊。

第一:BufferedInputStream的read()方法会使用默认的byte[]来缓存,虽然还是一个字节一个字节的读,但是是从缓存的byte[]里面读取了,而FileInputStream一个字节一个字节的直接从数据源读取,IO操作增加了很多。

第二:BufferedInputStream存在的意义是封装了mark和reset方法,如果有mark和reset的操作,用这个流比较好。

mark和reset

mark()是标记当前位置,reset可以回到标记的位置。光说不练假把式,上代码:

//mark,reset测试
        byte[] samplebyte = {'a','b','c','d','e','f','h','i','j','k'};
        //将byte[]读入ByteArrayInputStream
        ByteArrayInputStream bis = new ByteArrayInputStream(samplebyte);
        //用BufferedInputStream封装ByteArrayInputStream
        BufferedInputStream buffer = new BufferedInputStream(bis);
        //开始读取
        try {
            int curbyte;
            //标记只回退一次
            boolean haveMarked = false;
            while((curbyte=buffer.read())!=-1){
                //在读取到c的时候mark,参数是限制最多字节数
                if(curbyte == 'c' && !haveMarked){
                    if(buffer.markSupported()){
                    buffer.mark(5);
                    haveMarked = true;
                    }
                }
                System.out.print(curbyte+",");
            }
            //在读取到h的时候回到标记的位置
            if(buffer.markSupported())              
                    buffer.reset();
                    int curbyte2 ;
                    while((curbyte2=buffer.read())!=-1){
                        System.out.print(curbyte2+":");
                    }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

结果可以看到,读取完了以后,从我们mark的位置开始从新读取(这里的从新读取不是又进行一次IO操作,而是用之前缓存的那个byte[],这样就提高了效率):

97,98,99,100,101,102,104,105,106,107,100:101:102:104:105:106:107:

关于mark(int readlimit)的参数是表明读取多少字节标记才失效,但是实际中是取readlimit和BufferedInputStream类的缓冲区(默认缓存大小byte[1024*8])大小两者中的最大值,而并非完全由readlimit确定。


字符流

顾名思义了,字符流呢是以字符为操作单位了。
还是先上类图:

这里写图片描述

大部分类的用法和字节流的用法差不多,就不一一赘述了,只有一个LineNumberReader多了一个按行读取的方法。
//LineNumberReader
        try {
            Reader fileReader2 = new FileReader("D:\\javaFile\\test\\hah.txt");
            LineNumberReader lineNumberReader = new LineNumberReader(fileReader2);
            String line;
            while((line=lineNumberReader.readLine())!=null){
                System.out.println("LineNumberReader:"+line);
            }
        } catch (IOException e2) {
            // TODO Auto-generated catch block
            e2.printStackTrace();
        }

        //CharArrayReader和BufferedReader
        char[] char1 = {'a' ,1,'我',234,1234};
        char[] tempchar;
        BufferedReader bufferReader = new BufferedReader(new CharArrayReader(char1));
        int temp;
        try {
            while((temp = bufferReader.read())!=-1){
                System.out.println("char:"+temp);
            }
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        //FileReader
        try {
            FileInputStream fileInputStream = new FileInputStream("D:\\javaFile\\test\\hah.txt");
            int bytecount = 0; 

            while(fileInputStream.read()!=-1){
                bytecount++;
            }
            System.out.println("字节数:"+bytecount);
            Reader fileReader = new FileReader("D:\\javaFile\\test\\hah.txt");
            int count = 0;
            while(fileReader.read()!=-1){
                count++;
            }
            System.out.println("字符数:"+count);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

基本上关于IO的知识点都梳理在这里了,不管是字符流还是字节流,都只讲解了输入流,输出流基本都和输入流是一一对应的。IO涉及到的类比较多,系统完整的学习一次大有裨益。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值