一、IO流概述及其分类
1、概述:IO流用来处理设备之间的数据传输、Java对数据的操作是通过流的方式、Java用于操作流的对象都在IO包中
2、IO流分类
按照数据流向

输入流

输出流

读入数据

写出数据

按照数据类型

字节流

字符流

可以读写任何类型的文件 比如音频 视频 文本文件

只能读写文本文件

字节流的抽象基类:InputStream ,OutputStream

字符流的抽象基类:Reader , Writer

字节输入流 InputStream,字节输出流 OutputStream

字符输入流 Reader,字符输出流 Writer

注意:由这四个抽象基类类派生出来的子类名称都是以其父类名作为子类名的后缀。
二、字符流
1、FileOutputStream写出数据

①、构造方法

FileOutputStream(File file)// 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
 FileOutputStream(String name) // 创建一个向具有指定名称的文件中写入数据的输出文件流。

②、FileOutputStream的三个write()方法

public void write(int b):写一个字节 超过一个字节 砍掉前面的字节
 public void write(byte[] b):写一个字节数组
 public void write(byte[] b,int off,int len):写一个字节数组的一部分

③、FileOutputStream写出数据实现换行和追加写入

windows

Linux

Mac

\r\n

\n

\r

FileOutputStream(String name, boolean append),参2 代表是否追加写入
例如:FileOutputStream out = new FileOutputStream(“b.txt”,true);//true 表示追加写入

public class MyIODemo {
    public static void main(String[] args) throws IOException {
      //参2 代表是否追加写入
      //FileOutputStream(String name, boolean append)
      //创建一个向具有指定 name 的文件中写入数据的输出文件流。
      FileOutputStream out = new FileOutputStream("b.txt",true);//true 表示追加写入
      out.write(97); //一次写入一个字节
      out.write(98);
      //写入一个字节数组
      out.write("\r\n".getBytes());//换行
      out.write("像我这样优秀的人,本该灿烂过一生".getBytes());
      out.write("\r\n".getBytes());
      //写入字节数组的一部分 从某个索引开始,写多少个字节
      out.write("怎么二十多年到头来,还在人海里浮沉".getBytes(),0,27);
      out.write("\r\n".getBytes());
      //流使用完毕之后,必须释放资源
      out.close();
    }
}

3、FileInputStream读取数据
①、从文件系统中的某个文件中获得输入字节
②、FileInputStream(String name)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
③、FileInputStream(File file)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
④、输入流,所关联的文件如果不存在,就会报错

int read():一次读取一个字节,效率低
 byte[] bytes = new byte[1024];
 int len = in.read(bytes); //一次读取一个字节数组 返回值是你读取到有效字节个数

注意:流使用完毕之后,必须释放资源
4、高效的字节输入输出流
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedInputStream(InputStream in)创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
高效的输入输出流的使用与基本字节流基本相同
5、案例

分别使用使用高效字节流和基本字节流复制文件   
 public class MyDemo3 {
     public static void main(String[] args) throws IOException { 
        long start = System.currentTimeMillis();
        copy2(); //耗时18542毫秒  耗时13802毫秒
        copy1();  //耗时14046毫秒 耗时14331毫秒
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end-start)+"毫秒");
    }
    private static void copy2() throws IOException {
        FileInputStream in = new FileInputStream("E:\\我的图片和音乐\\疯狂动物城.mp4");
        FileOutputStream out = new FileOutputStream("D:\\疯狂动物城.mp4");
        //创建一个数组缓冲区
        byte[] bytes = new byte[1024*8];
        int len = 0; //记录读取到的有效的字节个数
        while ((len = in.read(bytes)) != -1) {
            out.write(bytes, 0, len);
            out.flush();
        }
        in.close();
        out.close();
    }
    private static void copy1() throws IOException {
        BufferedInputStream in = new BufferedInputStream(new FileInputStream("E:\\我的图片和音乐\\疯狂动物城.mp4"));
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("D:\\疯狂动物城.mp4"));
        byte[] bytes = new byte[1024*8];
        int len=0;
        while ((len=in.read(bytes))!=-1){
            out.write(bytes,0,len);
        }
        out.close();
        in.close();
    }
    }

三、字符流
1、字符流出现的原因:由于字节流操作中文不是特别方便,所以,java就提供了字符流。
字符流 = 字节流 + 编码表
2、String类中的编码和解码问题
编码: 就是把字符串转换成字节数组,把看得懂的变成看不懂的

• public byte[] getBytes();使用平台的默认字符集将此 String编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
• public byte[] getBytes(String charsetName) 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。

解码: 把字节数组转换成字符串,把看不懂的变成看得懂的

• public String(byte[] bytes): 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
• public String(byte[] bytes, String charsetName) 通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。

3、转换流OutputStreamWriter的使用
①、OutputStreamWriter
是字符流通向字节流的桥梁:可使用指定的码表将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
②、构造方法

a:OutputStreamWriter(OutputStream out)//创建使用默认字符编码的 OutputStreamWriter。
 b:OutputStreamWriter(OutputStream out, Charset cs)//创建使用给定字符集的 OutputStreamWriter。

注意:输出流所关联的文件,如果不存在,系统会自动创建
③、字符流的5种写数据的方式

public void write(int c) 写一个字符
public void write(char[] cbuf) 写一个字符数组
public void write(char[] cbuf,int off,int len) 写一个字符数组的 一部分
public void write(String str) 写一个字符串
public void write(String str,int off,int len) 写一个字符串的一部分
举例:
public class MyDemo {
public static void main(String[] args) throws IOException {
    OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("a.txt"));
    //往文件中写入数据
    out.write('尼'); //一次写入一个字符
    out.write("\r\n");
    out.flush();
    out.write("我爱学java");
    out.write("\r\n");
    out.flush();
    out.write("路慢慢慢其修远兮,吾将上下而求索", 0, 8);
    out.write("\r\n");
    out.flush();
    out.write(new char[]{'a', '9', 98, 'e'});
    out.write("\r\n");
    out.write(new char[]{'我', '爱', '你', '们'}, 2, 2);
    out.write("\r\n");
    //字符流 需要将数据从缓冲区刷新过去
    out.flush();
    out.close(); //刷新并关闭
 }
}

4、转换流InputStreamReader的使用
①构造方法

InputStreamReader(InputStream is):用默认的编码(GBK)读取数据
 InputStreamReader(InputStream is,String charsetName):用指定的编码读取数据

②、字符流的2种读数据的方式
public int read() 一次读取一个字符
public int read(char[] cbuf) 一次读取一个字符数组 如果没有读到 返回-1

public class CopyFile2 {
    public static void main(String[] args) throws IOException {
        InputStreamReader in = new InputStreamReader(new FileInputStream("a.txt"));
       OutputStreamWriter out = new OutputStreamWriter(newFileOutputStream("b.txt"));
        //创建一个字符缓冲区
        char[] chars = new char[100];  
        int len = 0; //len 是记录 你每次读取到的有效的字符个数
        while ((len=in.read(chars)) != -1) {
            out.write(chars, 0, len);//
            out.flush(); //字符流记得刷新一下
        }
        //释放资源
        in.close();
        out.close();
    }
}

5、字符流便捷类
因为转换流的名字太长了,并且在一般情况下我们不需要制定字符集,于是java就给我们提供转换流对应的便捷类。唯一缺点就是不能指定编码。

转换流

便捷类

OutputStreamWriter

FileWriter

InputStreamReader

FileReade

FileWriter(File file);根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(String fileName);根据给定的文件名构造一个 FileWriter 对象。
6、高效字符输入书出流
①、高效的字符输出流: BufferedWriter//写出数据
构造方法: public BufferedWriter(Writer w)
②、高效的字符输入流: BufferedReader//读取数据
构造方法: public BufferedReader(Reader e)
③、字符缓冲流的特殊功能
BufferedWriter: public void newLine():根据系统来决定换行符 具有系统兼容性的换行符。
BufferedReader: public String readLine():一次读取一行数据 是以换行符为标记的 读到换行符就换行 没读到数据返回null。

举例://使用高效的字符流,采用读取一行,写入一行的方式来复制文本文件
public class MyDemo8 {
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader("a.txt"));
        //BufferedWriter(Writer out)
        //创建一个使用默认大小输出缓冲区的缓冲字符输出流。
        BufferedWriter out = new BufferedWriter(new FileWriter("b.txt"));
        String line=null; //记录每次读取到的一行文本
        while ((line=in.readLine())!=null){ //这里注意读取不到返回null
            out.write(line);
            out.newLine(); //写入一个换行符
            out.flush();//刷新
        }
        //释放资源
        out.close();
        in.close();
    }
}