Java - I/O之字节流和字符流

1. 流的概念

    stream即一系列数据,当不同介质之间有数据交互的时候Java就使用流来实现。当建立文件输入流后,这个流就可以用来把数据从硬盘读取到JVM(内存)中。流分为输入流(InputStream)和输出流(OutputStream)。

2. 流的种类

   目前我接触到的流主要分为2类,一类是字节流(byte),另一类是字符流(char)。

    关系如下:(当看到水印的时候就知道这图我绝对是借鉴的,我的xmind崩了)

2.1字节流 

    字节流即以字节的方式读取和写入数据。

   写入:FileInputStream

   读取:FileOutputStream

 /*
    * 以字节流的形式读取数据
    * */
    public static void read(File file){
        try(FileInputStream fis=new FileInputStream(file)){
            byte[] all=new byte[(int)file.length()];
            //将数据写入all数组中
            fis.read(all);
            for(byte b:all){
                System.out.println(b);
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    /*
    * 以字节流的形式向文件中写入数据
    * */
    public static void write(File file){
        try(FileOutputStream fos=new FileOutputStream(file)){
            //1.先判断一下文件路径是否存在,不存在的话新建个相应目录
            if(!file.exists()){
                //2.如果文件不存在的话找到file的目录,新建
                file.getParentFile().mkdirs();
                //3.下面这步是根据file创建空白文件,可以不写,因为outputStream在写出的时候只要路径正确,如果没有文件会自动进行创建
                file.createNewFile();
            }
            byte[] words={'a',99};
            fos.write(words);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

  2.1.1数据流

  数据流可以直接对文件进行字符串的读写,但是写好的文件需要指定的流进行读取。

 数据输入流: DataInputStream

 数据输出流: DataOutputStram

 /*
    * 以数据输入流的形式读取数据
    * */
    public static void read(File file){
        try(FileInputStream fis=new FileInputStream(file);
        DataInputStream dis=new DataInputStream(fis)){
            int i=dis.readInt();
            String str=dis.readUTF();
            char c=dis.readChar();
        }catch (IOException e){
            e.printStackTrace();
        }

    }
    /*
    * 以数据输出流的形式向文件中写入数据
    * */
    public static void write(File file){
        try(FileOutputStream fos=new FileOutputStream(file);
            DataOutputStream dos=new DataOutputStream(fos); ){
            dos.writeInt(123);
            dos.writeUTF("This is a string");
            dos.writeChar('a');
        }catch (IOException e){
            e.printStackTrace();
        }
    }

 2.1.2 对象流

    对象流指的是可以直接把一个对象以流的形式传输给其他介质。一个对象以流的形式进行传输,叫做序列化。该对象所对应的类必须是实现了Serializable接口。

对象输入流: DataInputStream

对象输出流: DataOutputStream

 将进行序列化的对象:

package Obj;

import java.io.Serializable;

public class Dog implements Serializable {
    public String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public  Dog(){
    }
}

 以对象流的形式读写数据

 public static void main(String[] args) {
        Dog dog=new Dog();
        dog.name="小黑";
        //准备一个文件用于保存该对象
        File f=new File("C:\\File\\test.txt");
        try(
                //创建对象输出流
                FileOutputStream fos=new FileOutputStream(f);
                ObjectOutputStream oos=new ObjectOutputStream(fos);
                //创建对象输入流
                FileInputStream fis=new FileInputStream(f);
                ObjectInputStream ois=new ObjectInputStream(fis);
                ){
                    //将对象写入文件中
                    oos.writeObject(dog);
                    //从文件中读取对象
                    Dog d=(Dog) ois.readObject();
        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }

  2.2字符流

      字符流专门用于字符形式读取和写入数据。

     字符流读取文件:FileRead

     字符流写入文件:FileWrite

 /*
    * 以字符输入流的形式读取数据
    * */
    public static void read(File file){
        try(FileReader fr=new FileReader(file)){
          char[] all=new char[(int)file.length()];
          for(char b:all){
              System.out.println(b);
          }
        }catch (IOException e){
            e.printStackTrace();
        }

    }
    /*
    * 以字符输出的形式向文件中写入数据
    * */
    public static void write(File file){
       try(FileWriter fw=new FileWriter(file)){
           String data="abcdefg1234567890";
           char[] all=data.toCharArray();
           fw.write(all);

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

  2.2.1缓存流

  当字节和字符流在进行每一次读写的时候,都会访问硬盘,如果读写的频率比较高,其性能会表现不佳。缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作。

缓存读取流:BufferedRead

缓存输出流:PrintWrite

/*
    * 以缓存输入流的形式读取数据
    * */
    public static void read(File file){
        try(FileReader fr=new FileReader(file);BufferedReader br=new BufferedReader(fr);){
            while(true){
                String line=br.readLine();
                if(line==null){
                    break;
                }
                System.out.println(line);

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

    }
    /*
    * 以缓存输出的形式向文件中写入数据
    * */
    public static void write(File file){
      try(FileWriter fw=new FileWriter(file);PrintWriter pw=new PrintWriter(fw);){
          pw.println("这是一个缓存输出流");
      }catch (IOException e){
          e.printStackTrace();
      }
    }

3.编码导致的中文问题

        Java采用Unicode的编码形式,写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。而这些中文字符采用的编码方式,都是使用UNICODE. "中"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。

     而在使用字符流读取这些数据时,FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了,而FileReader使用的编码方式是Charset.defaultCharset()的返回值,如果是中文的操作系统,就是GBK,FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替。

/*
    * 以InputStreamReader的形式读取数据
    * */
    public static void read(File file){
        try(FileInputStream fis=new FileInputStream(file);InputStreamReader isr=new InputStreamReader(fis, Charset.forName("utf-8"));){
           char[] cs=new char[(int)file.length()];
           isr.read(cs);
        }catch (IOException e){
            e.printStackTrace();
        }

    }