IO流-Buffered(缓冲流)的应用以及流的使用规律

流的使用规律:

1,明确是读操作还是写操作(数据源是当前程序或者数据目的地)
  • 读:InputStream ,Reader
  • 写:OutputStream,Writer
2,明确是操作字符还是操作字节:明确操作基类
  • 读:
  • 字节:InputStream;
  • 字符:Reader;
  • 写:
  • 字节:OutputStream;
  • 字符:Writer;
3,操作的具体介质:明确具体操作类
  • 读:
  • 文件: File
  • 内存:char,array,double
  • 网络:Socket
  • 键盘:System. in
  • 写:
  • 文件:File
  • 内存:char,array
  • 网络:Socket
  • 屏幕:System.out
4,明确是否需要额外操作:
  • 缓冲区:BufferedXXX
  • 转换:InputStreamReader/OutputStreamWriter

Buffer(缓冲流)流:

缓冲流是对文件流处理的一种流,它本身并不具备 IO 功能,只是在别的流上加上缓冲提高了效率,当对文件或其他目标频繁读写或操作效率低,效能差。这时使用缓冲流能够更高效的读写信息。因为缓冲流先将数据缓存起来,然后一起写入或读取出来。所以说,缓冲流还是很重要的,在IO操作时记得加上缓冲流提升性能。

BufferedInputStream 和BufferedOutputStream 这两个类分别是InputStream 和OutputStream 的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。

我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!

同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream写完数据后,要调用flush()方法或close()方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReader 和BufferedWriter 两个类。

优点:显著提高IO读取速度;提供一些别的方法,比如一行读入或者读出

方法演示:

package com.io;

import java.io.*;
import java.util.*;

public class Buffer{
    public static void main(String[] args) throws IOException {
       read();
       write();
      //FileInputStrean\BufferInputStream()
        
    }

    //缓冲流读操作
    public static void read() throws IOException {
        String path = "/Users/gongdezhe/Desktop/test.txt";

        //字节文件操作流
        FileInputStream fis = new FileInputStream(path);

        //字节字符转换流,字节转字符
        InputStreamReader isr = new InputStreamReader(fis);


        //缓冲流
        //字符缓冲流
        BufferedReader br = new BufferedReader(isr);//默认8k
//        BufferedReader brs = new BufferedReader(isr,1024); //指定1k

        //字节缓冲流
        BufferedInputStream bis = new BufferedInputStream(fis);

        //特有的方法罗列
        String line;
        while ((line = br.readLine()) != null) { //读取一行.读到末位会返回null
            System.out.println(line);
            br.flush();//刷新
        }

        br.close();

    }

    //缓冲流写操作
    public static void write() throws IOException {
        String path = "/Users/gongdezhe/Desktop/test.txt";

        BufferedWriter writer = 
        new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path))); 
         //创建字节输出流=》转换为字符输出流=》装饰到BufferedWriter
        Random random = new Random();
        for (int i = 0; i < 100; i++) {
            int i1 = random.nextInt(5) + 5;
            StringBuffer stringBuffer = new StringBuffer();
            for (int j = 0; j < i1; j++) {
                int i2 = random.nextInt(1000) + 1;
                stringBuffer.append(i2);
                stringBuffer.append(",");
            }
            writer.write(stringBuffer.toString());
            writer.newLine(); //换行
        }

        writer.close();//关闭流

    }
   
}

注意:

BufferedWriter的readLine()方法使用注意:原博主链接

BufferReader的read方法和readLine方法在任何情况下都是阻塞的。readLine方法每次读一行,相对于一个字符/字节地读取、转换、返回来说,它有一个缓冲区,读满缓冲区才返回;一般情况下,都建议使用它们把其它Reader/InputStream包起来,使得读取数据更高效;对于文件来说,经常遇到一行一行的,特别符合情景。 如果不指定buffer大小,则readLine()使用的buffer有8192个字符。在达到buffer大小之前,只有遇到"/r"、"/n"、"/r/n"才会返回,否则一直阻塞。
          说来也是惭愧,这个方法用了无数次,却从未注意过这个方法是阻塞的。今天用socket写了聊天小demo,问题出现了,程序走到readLine()这个地方,一直阻塞着不往下走,结果就发现了这个隐藏很久的坑。
          解决办法也比较简单粗暴,就是在传输的字符串后面加上换行符。t和曾经做过一个项目现象比较类似,服务端是netty写的,用来接受不同厂商通过网络设备发送来的消息,在接入的过程中很多厂商的设备的消息发送后都没有返回任何消息,后来在分析中发现,由于接入协议中,要求每隔五分钟各个设备要向服务端发送一条消息,消息的格式是字符串,很多厂商在连接后没有关闭连接,消息与消息之间也没有任何标识一条消息结束的标识符号,出现了粘包。解决方法也差不多,1、传输完成后,必须关闭连接2、每条消息结尾加上换行符’\n‘;

BufferedWriter.newline() 方法换行的陷阱:原博主链接

首先导出到文件需要用到 BufferedWriter。而换行则是通过 bw.newline() 方法,问题将出在 newline() 方法上面。
newlineAPI:

public void newLine()
                 throws IOException
    Writes a line separator. The line separator string is defined
     by the system property line.separator,
      and is not necessarily a single newline ('\n') character.
    Throws:
        IOException - If an I/O error occurs

英文本身应该没有什么难度,意思是:newLine 方法会调用系统的换行符。而这就是问题的根本。
不同系统的换行符:
windows --> \r\n
linux --> \r
mac --> \n
          我们一般开发是在 windows 下开发,而服务器一般情况下都是 linux。
          如果我们使用 newline 函数换行,在本机测试的时候,因为是 windows 环境,换行符是 \r\n ,打开文件时候自然文件是换行处理,没有问题。
          当我们部署到服务器时候,服务器是 linux 环境,newline 读取系统换行符是 \r ,导出到文件,文件的换行符是 \r,当我们把这个文件通过浏览器下载到 windows 时候,再打开文件将会出现没有换行的问题。因为 windows 下对于 \r 的解释并不是换行符。
          所以,我们在开发时候,如果需要指定文件在某些地方换行,则不能使用 newline 方法。必须手动指定换行符:\r\n 因为按照上面列举的不同系统换行符看,如果字符串的末尾是 \r\n 在三个系统中,查看该文件,都会解释为换行。

练习:读取文件,找出文件中出现次数最多的十个元素:

FileReader fileReader=null; //文件字符输入流
BufferedReader bufferedReader=null; //字符输入缓存流
try{
    fileReader=new FileReader("E:\\javaSource2\\javacod\\20181121\\test (2).txt");
    bufferedReader=new BufferedReader(fileReader);
    String s=null;
    HashMap<Integer,Integer> hashMap=new HashMap<>(); //key存储读入数据,value存储出现次数
    while((s=bufferedReader.readLine())!=null){ //读取一行
        String[] strings=s.split(","); //将这一行字符串由逗号分割成字符数组
        for (int i = 0; i <strings.length ; i++) {
            Integer num=Integer.valueOf(strings[i]);
            if(hashMap.containsKey(num)){
                hashMap.put(num,hashMap.get(num)+1);
            }else {
                hashMap.put(num,1);
            }
        }
    }
    //优先级队列集合,根据元素出现次数排序
    PriorityQueue<Map.Entry<Integer,Integer>> priorityQueue=
    new PriorityQueue<>(new Comparator<Map.Entry<Integer, Integer>>() {
        @Override
        public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
            if(o2.getValue()-o1.getValue()==0){
                return o2.getKey()-o1.getKey();
            }
            return o2.getValue()-o1.getValue();
        }
    });
    Iterator<Map.Entry<Integer,Integer>> iterator=hashMap.entrySet().iterator(); 
    //对hashmap进行迭代
    while(iterator.hasNext()){
        priorityQueue.add(iterator.next());
    }
    Map.Entry<Integer,Integer> entry=null;
    for (int i = 0; i <10 ; i++) {
        if((entry=priorityQueue.poll())==null){
            System.out.println("文件元素不足十个");
            break;
        }
        System.out.println("值:"+entry.getKey()+"  次数:"+entry.getValue());
    }

}catch (FileNotFoundException f){
    f.printStackTrace();
}catch (IOException e){
    e.printStackTrace();
}finally {
    if(fileReader!=null){
        try {
            fileReader.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    if(bufferedReader!=null)
         try{
            bufferedReader.close();
         }catch (IOException e){
            e.printStackTrace();
         }
    }
}

你可能感兴趣的:(笔记,IO流)