小白菜鸡学JAVA IO流

本文以小白视角分享Java IO流相关知识。介绍了IO类树,阐述流是流动的数据,IN和OUT相对内存而言,数据分字节和字符流,水龙头分节点流和处理流。还进行了Reader实验,包括InputStreamReader、FileReader等类的用法及特点。

废话:曾经有一次我本来想在Java课上的课群上问一下对于 IO 技术对于一些特定场景和开发规范以及不同用法对于性能的影响的问题,结果别人就直接把垃圾课本的东西给我截图过来叫我看,然后一堆人跟风讽刺我不认真看书,对不起,我错了。因此,本次文章就以一个小白身份来分享一下 Java IO流 自己做的一些实验。

1. IO类树

在这里插入图片描述

Java-IO流的方式有很多,我们如何彻底来理解这些IO类的设计初衷,我们就要体会理解设计者的想法。说实话这么多的类自己也不一定记得住,最好就是根据场景对这些类的用法有一个深刻的理解,最好就是不同的场景的能有不同的用法区分。

2. 灵魂之问:什么是流?我们为什么需要 IO 类?

什么是流?

所谓流,就是流动的数据,我们管理这些数据的出入的工具就是一个水龙头,我们为了功能更好的区分开来,我们设计了一个从目标区域取水到内存的水龙头(Input),一个是从内存灌水到目标区域的水龙头(Ouput)

因此我们的 INOUT 是相对于本体内存而言的。

当然了,有时候又这样的情况就是,比如我们从Server写入数据到Client的机器上,那么Server就是本体,我们的Server上的代码就是Output了,Client上的代码就是Input了。

是in还是out在于我们赋予代码的这台机器到底在执行一个怎样的角色

这样你想一下,为什么我们的print是 System.out.print ?

因为我们是从内存取水(数据)出来显示到屏幕上的,所以是out。

我们流动的这些数据根据类型分为:字节B和字符char

因为他们在数据类型上表示的是连续的多个的数据,因此,我们用char[]表示字符流,用byte[]表示字节流。

我们的水龙头也是分类的,分为:点到点的水龙头(节点流),这样的水龙头能非常直接而纯粹地完成数据传输的功能,但是你想,这样会不会单一了?

因此我们设计了一种更高级的水龙头包装器,这就好像道具加成一样,我们能在原来的点到点的水龙头实现更高级的功能,比如buffer,我们就相当于在水龙头的中间加了一个缓冲的水池,我们取水的时候就能大瓢大瓢地直接从水池取水,就无需老是守着我们小口径水龙头等老久了。

除此之外,还有一种(高级的水龙头道具)处理流:Data,比如我们读入的一个数据类型long(8B),用Stream的方式或者Reader的方式读取的话不一定能8B直接读取到,我们用了Data包装一下水龙头,他就能直接以8B进行格式化的读取了

Object处理流的化可以把对象转成二进制数据,序列化地读取一个抽象的数据。

我们还有其他很多高级的处理流(水龙头道具),我们后面再讲。

记住:流工具,就是一个数据流动的管理器

3. 每一个IO类不妨来个小实验?

3.1 Reader 实验

我们即将测试以下 Reader 的用法:

  1. BufferedReader - LineNumberReader
  2. CharArrayReader
  3. InputStreamReader
  4. FileReader
  5. PipedReader
  6. StringReader
  7. FilterReader - PushbackReader
	//代码格式
    public static void readFile(String path){
        try{

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{

            }catch(Exception e){
				e.printStackTrace();
            }
        }
    }

3.1.1 InputStreamReader

(水龙头)(内存到内存)点对点
这个是我们的老朋友了,这个就是一个Byte[]–>Char[]的转化器了,因此他需要到InputStream 作为初始化。

    public static void readFile(String path){
        File file = new File(path);
        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null;
        int array_size = 9000;
        try{
            fileInputStream = new FileInputStream(file);
            inputStreamReader = new InputStreamReader(fileInputStream);
            char[] chars_tmp = new char[array_size];
            int readLength = inputStreamReader.read(chars_tmp);
            while(readLength!=-1){
                if(readLength==array_size){
                    System.out.println(chars_tmp);
                }else{ // 当我们在读取最后一次的时候,读取满的长度我们需要新建一个数组来承受
                    char[] chars_tmp_end = new char[readLength];
                    System.arraycopy(chars_tmp, 0, chars_tmp_end , 0, readLength);
                    System.out.println(chars_tmp_end);
                }      
                readLength = inputStreamReader.read(chars_tmp);
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                fileInputStream.close();
                inputStreamReader.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

3.1.2 FileReader

(水龙头)(硬盘到内存)
这是对InputStreamReader的一种拓展化,我们用字符来读取文件数据,另外,他可以用buffer道具来辅助自己,为什么我们要另外这样写一个FileReader而不是直接用InputStreamReader呢?同理,我们是为了达到一种开发的规范,也是为了我们能够更好地进行理解和构造代码。

而且他可以直接从File进行构造数据流的水龙头。(更方便了)

    public static void readFile(String path){
        File file = new File(path);
        FileReader fileReader = null;
        int array_size = 9000;
        try{
            fileReader = new FileReader(file);
            char[] chars_tmp = new char[array_size];
            int readLength = fileReader.read(chars_tmp);
            while(readLength!=-1){
                if(readLength==array_size){
                    System.out.println(chars_tmp);
                }else{ // 当我们在读取最后一次的时候,读取满的长度我们需要新建一个数组来承受
                    char[] chars_tmp_end = new char[readLength];
                    System.arraycopy(chars_tmp, 0, chars_tmp_end , 0, readLength);
                    System.out.println(chars_tmp_end);
                }      
                readLength = fileReader.read(chars_tmp);
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                fileReader.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    } 

3.1.3 BufferedReader

(道具)

装饰了缓冲区的功能,我们可以从缓冲区拿数据可以更快(如果文件不大的话或者缓冲区足够大的话)

实际上,我们在上面写的char[] 就是起到了缓冲区的作用嘛,我们可以看一下BufferedReader的源码实现的效果和我们上面实现的差不多。

默认的缓冲区的大小是 8192 个 Char

而且这个缓冲Reader的水龙头还有包装了 readLine() 的功能。

    public static void readFile(String path){
        File file = new File(path);
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;

        try{
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader); // 为 FileReader 包装一下 增加使用缓冲区的功能。
            String line;
            while((line = bufferedReader.readLine())!=null){
                System.out.println(line);
            }

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                fileReader.close();
                bufferedReader.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    } 

有人说用bufferReader速度更快,这个要看实际情况,因为在Java 和python的类实现是非常不一样的,现在我也深刻体会到这个问题,我们在python里面会很强调大家尽量有API尽量用API,因为他们的API底层是用C写的,这样运行编译的时候速度会很快的。但是在Java里面,这些功能类的作用实际上都是可以用其他的类来实现的,并不是由更快速的语言编译而实现的,我们定义各种繁多的功能类,只是为了使得大家的代码能够在一定的层次达到相同的规范

# 附 BufferedReader 的子类:LineNumberReader

    public static void readFile(String path){
        File file = new File(path);
        FileReader fileReader = null;
        //BufferedReader bufferedReader = null;
        LineNumberReader lineNumberReader = null;
        try{
            fileReader = new FileReader(file);
            //bufferedReader = new BufferedReader(fileReader); // 为 FileReader 包装一下 增加使用缓冲区的功能。
            lineNumberReader = new LineNumberReader(fileReader);
            String line;
            while((line = lineNumberReader.readLine())!=null){
                System.out.println(lineNumberReader.getLineNumber()+": "+line);
            }

        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                fileReader.close();
                //bufferedReader.close();
                lineNumberReader.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

lineNumberReader 就相当于增加了可以读取行号的功能。

3.1.4 CharArrayReader

(水龙头)
是一个节点对象流(内存到内存),就是一个char[] 的访问器,我们对于这些对象的访问都应该需要访问器,而不是直接使用赋值,观察reader内部的源码,对于线程拥塞这些问题以后可以更好的进行管理。

        CharArrayReader charArrayReader = null;//CharArrayReader 就是一个访问器
        char[] my_array = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r','s', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
        char[] out_data = new char[5];
       	charArrayReader.read(out_data);
       	
       	//Method2
       	//char[] out_data = new char[5];
        //charArrayReader.read(out_data,0,5); //从指定位置读取

可以使用mark(0),下一次的reset就会定位到这个位置

            char[] out_data = new char[5];
            charArrayReader.read(out_data);
            charArrayReader.mark(0);

            char[] out_data2 = new char[5];
            charArrayReader.reset();
            charArrayReader.read(out_data2);
            charArrayReader.reset();
            charArrayReader.read(out_data);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值