IO流的分类和基本使用

IO流

IO流体系:

字节流

  • 这两个为抽象类

  • inputStream 字节输入流 负责读取数据

    • FileInputStream 操作本地文件 字节输入流

  • OutputStream 字节输出流 负责写出数据

    • FileOutputStream 操作本地文件的字节输出流

      • 把程序中的数据写到本地文件中

      • 创建输出流对象 写数据 释放资源

      • FileOutputStream fileOutputStream = new FileOutputStream("文件目录");
                //写入数据
                fileOutputStream.write(97);
                //释放资源
                fileOutputStream.close();
        ​
      • 创建对象    细节1:参数是字符串表示的路径或者是File对象都是可以的    细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径的存在    细节3:如果文件已经存在会清空文件写出数据    细节1:write方法中的参数是整数,实际写入文件的数据是ASCII码中对应的字符释放资源    每次使用完必须释放资源
        ​
      • 一次性写多个,把数据放入Byte数组里就行

      • //一次性写入多个
        byte[] bytes ={97,98,99,100,101};
        fileOutputStream.write(bytes);
        fileOutputStream.write(bytes,0,3);//0  起始索引 3 个数
      • 换行和续写

      • //        换行写 再次写出一个换行符
                /*
                换行符
                Windows :\r\n
                Linux: \n
                Mac:  \r
                细节:在java中Windows只需要写一个\r或者\n就可以  因为底层会补全 建议写全
                 */
                String str = "qwertyjkl";
                byte[] bytes1 = str.getBytes();//把字符串转为字符数组
                fileOutputStream.write(bytes1);
        ​
                String wrap = "\r\n";
                byte[] wrapBytes = wrap.getBytes();
                fileOutputStream.write(wrapBytes);
        ​
                String str2 = "666";
                byte[] bytes2 = str2.getBytes();//把字符串转为字符数组
                fileOutputStream.write(bytes2);
        ​
        //      续写 打开续写开关就行 位置在创建对象的第二个参数 默认为false
                //FileOutputStream fileOutputStream = new FileOutputStream("",true);
    • FileInputStream 操作本地文件的字节输入流

      • 步骤 创建字节输入流对象 读数据 释放资源

      • /*
                演示 字符输入流  FileInputStream 读取文件中的数据
                步骤:
                创建对象
                    细节:如果文件不存在直接报错
                读取数据
                    细节:一次读一个字节 读出来是ASCII码上对应的数字
                    细节:读到文件末尾 read方法返回-1 如果是空格不是文件末尾 是32
                释放资源
        ​
                 */
                FileInputStream fileInputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt");
                int read = fileInputStream.read();//只读取了第一个数据 返回其ASICC码
                System.out.println(read);
                int read2 = fileInputStream.read();//只读取了第二个数据 返回其ASICC码
                System.out.println((char) read2);//强转输出原先字符  如果读取不到 就返回-1
        ​
                //循环读取
                int b;
                //read 读取一次 移动一次指针
                while ((b=fileInputStream.read())!=-1){
                    System.out.print((char) b);
                }
                fileInputStream.close();
    • 文件拷贝

      • /*
                文件拷贝
                 */
                FileInputStream inputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt");
                FileOutputStream outputStream = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo03\\aa.txt");
        ​
                //拷贝 核心 边读边写
                int b;
                while ((b = inputStream.read())!=-1){
                    outputStream.write(b);
                }
                
                outputStream.close();
                inputStream.close();//先开后关
    • 文件拷贝使用字节数组

      • FileInputStream inputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt");
                FileOutputStream outputStream = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo03\\aaa.txt");
        ​
                //拷贝
                int len;
                byte[] bytes = new byte[1024];
                while ((len = inputStream.read(bytes))!=-1){
                    outputStream.write(bytes,0,len);
                }
                outputStream.close();
                inputStream.close();
    • 文件拷贝使用try catch 去抛出异常

      • package IOLearn.Stream.demo03;
        ​
        import java.io.FileInputStream;
        import java.io.FileOutputStream;
        import java.io.IOException;
        ​
        public class ByteStreamDemo04 {
        ​
            public static void main(String[] args) throws Exception {
                /*
                文件拷贝
                利用try catch finally 捕获异常
                 */
                FileInputStream inputStream = null;
                FileOutputStream outputStream = null;
                try {
                    inputStream = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo01\\a.txt");
                     outputStream = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\demo03\\aaa.txt");
        ​
                    //拷贝
                    int len;
                    byte[] bytes = new byte[1024];
                    while ((len = inputStream.read(bytes))!=-1){
                        outputStream.write(bytes,0,len);
                    }
        ​
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    //释放资源
                    //优化 实现AutoCloseable接口 特定情况下可以自动释放接口
                    if (outputStream!=null){
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (inputStream!=null){
                        try {
                            inputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
        ​
                }
            }
        ​
        }
        ​

字符集

  • ASCII:存储英文和符号 一共128个 从0到127

  • GBK:Windows系统默认使用GBK

    • GBK完全兼容ASCII

    • 英文一个字节存储编码规则 不足8位 前面补0

    • 汉字使用两个字节存储 第一个字节为高位字节 第二个为低位字节

    • 高位字节二进制一定以1开头 转成十进制后是一个负数 编码规则不需要变动 和英文进行区别

  • Unicode:国际标准字符集 将世界各种语言的每一个字符定义唯一的编码

    • UTF-16 就是转成16个比特位 用2-4个字节保存

    • UTF-32 固定使用四个字节

    • UTF-8 用1-4个字节保存 实际开发常见

      • ASCII 一个字节 不足补0

      • 简体中文 3个字节 1110xxxx 10xxxxxx 10xxxxxx 编码规则 x 用汉字对应Unicode的二进制填充

乱码原因

  1. 读取数据未读完整个汉字

  2. 编码和解码方式不统一

  3. 字节流读取中文会乱码 为什么拷贝文本文件不乱码

    1. 因为拷贝是一个一个字节拷贝过去 数据没有丢失 只要记事本读取时字符集和编码方式与数据源一致就不会有乱码

java中编码和解码方法

编码
getBytes()//默认的方式编码
getBytes(String charsetName)//使用指定的方法编码

idea默认为UTF-8 eclipse默认为GBK

解码
String(byte[] bytes)//默认方式解码
String(byte[] bytes,String charsetName)//使用指定方式进行解码
/*
        编码  解码
         */
        //编码
        String str = "ai你嘿嘿";
        byte[] bytes = str.getBytes();//idea默认为UTF-8
        System.out.println(Arrays.toString(bytes));//[97, 105, -28, -67, -96, -27, -104, -65, -27, -104, -65] 编码后的字节数据 中文三个 英文一个00

        byte[] gbks = str.getBytes("GBK");//指定使用GBK编码
        System.out.println(Arrays.toString(gbks));//[97, 105, -60, -29, -70, -39, -70, -39] 编码后字节数据 英文一个字节 中文两个字节

        //解码
        String str2 = new String(bytes);
        System.out.println(str2);

        String gbk = new String(gbks, "GBK");//编码解码应规则一致 如果这里解bytes就会乱码ai浣犲樋鍢�
        System.out.println(gbk);

    }

字符流

字符流底层就是字节流 + 字符集

  • 这两个为抽象类

  • Reader 字符输入流 一次读一个字节,遇到中文 一次读多个字节 读到文件末尾返回-1

    • 使用子类FileReader 操作本地文件

    • 空参read方法:

    • /*
              字符输入流
               */
              FileReader fr = new FileReader("javaselearn\\src\\IOLearn\\Stream\\mycharset\\a.txt");
              //读取数据 字符流底层就是字节流 默认一个一个字节读
              //如果遇到中文就一次读取多个字节
              /*
              read细节:
              默认一个一个读,中文多个
              读取后 方法底层进行解码返回十进制
               */
              int ch;
              while ((ch=fr.read())!=-1){
                  System.out.print((char) ch);
              }
              fr.close();
      
    • 有参read方法:

    • /*
              字符输入流
               */
              FileReader fr = new FileReader("javaselearn\\src\\IOLearn\\Stream\\mycharset\\a.txt");
              //读取数据 字符流底层就是字节流 默认一个一个字节读
              //如果遇到中文就一次读取多个字节
              /*
              read细节:
              默认一个一个读,中文多个
              读取后 方法底层进行解码返回十进制 看见中文需要自己强转
              有参的read方法 把读取数据,解码,强转 三步合并了,把强转之后的字符放数组里了
              相当于空参read方法+强制类型转换
               */
              char[] chars = new char[2];
              int len;
              while ((len=fr.read(chars))!=-1){
                  System.out.print(new String(chars,0,len));
              }
              fr.close();
      
  • Write 字符输出流 底层会把数据按照指定的编码方式进行编码,变成字节在写入文件

    • 使用子类FileWrite 操作本地文件 可以续写,默认为false

    • /*
              字符输出流
      
              创建对象 可以开启是否续写 默认false
              读取数据 可以写出一个字符 字符串 字符数组等
               */
              FileWriter fw = new FileWriter("javaselearn\\src\\IOLearn\\Stream\\mycharstream01\\a.txt",true);
      //        fw.write(25105);
              /*
              25105 我  如果使用字节流写入会乱码 因为字节流是一个一个字节写
              字符流会先按照编码规则 在写入文件
               */
      
      //        fw.write("你好啊");
              char[] chars = {'a','b','v'};
              fw.write(chars);
              fw.close();
      
  • 适合操作纯文本文件

  • 字符流原理解析:

    • 在内存中有一个8192的字节数组缓存区 提高效率 先从数据源读取数据尽可能填满缓冲区 如果缓冲区有数据则从缓存区中读取 缓存区中没有就区数据源读取数据尽可能填满缓冲区

    • 补充:

    • FileReader fr = new FileReader("javaselearn\\src\\IOLearn\\Stream\\mycharstream01\\a.txt");
              fr.read();
              FileWriter fw = new FileWriter("javaselearn\\src\\IOLearn\\Stream\\mycharstream01\\a.txt");
              //因为没开续写会清空文件
              //如果再次使用fr读取会有数据吗?
              //会有数据 因为字符流读取会先存到缓存区中 然后在从缓存区中读取 虽然文件里面的数据清空了都是缓冲区还有数据 只能读取缓存区中 超过缓存区的无法读取
              int ch;
              while ((ch= fr.read())!=-1){
                  System.out.println((char) ch);
              }
      
      
              fw.close();
              fr.close();
      
    • 什么时候缓存区中的数据才真正写入目的地?

      • 缓冲区装满了

      • flush刷新 还可以继续写

      • close释放资源 不能继续写

使用场景:

  • 字节流 拷贝任意类型的文件

  • 字符流 读取存文本文件中的数据 往纯文本文件中写出数据

  • 练习一:拷贝一个文件夹 考虑子文件夹

  • package IOLearn.Stream.mytest;
    
    import java.io.*;
    
    public class Test01 {
    
        public static void main(String[] args) throws IOException {
            /*
            拷贝一个文件夹 考虑子文件夹
             */
            //1创建对象表示数据源
            File src = new File("E:\\test\\bb");
            //2创建对象目的地
            File dest = new File("E:\\test\\cc");
    
            //3调用方法开始拷贝
           copydir(src,dest);
        }
    
        /*
        作用拷贝文件夹 file是数据源 dest是目的地
         */
        private static void copydir(File src,File dest) throws IOException {
            //dest可能不存在 直接创建出来 有了创建失败 不会报错
            dest.mkdir();
            //递归
    
            //1.进入数据源
            File[] files = src.listFiles();
            //2.遍历数组
            for (File file1 : files) {
                if (file1.isFile()){
                    //3.判断文件 拷贝 文件开始 文件结束
                    FileInputStream fis = new FileInputStream(file1);
                    FileOutputStream fos = new FileOutputStream(new File(dest,file1.getName()));//不能直接dest 因为dest这是文件夹 不是文件
                    byte[] bytes = new byte[1024];
                    int len;
                    while ((len=fis.read(bytes))!=-1){
                        fos.write(bytes,0,len);
                    }
                    fos.close();
                    fis.close();
                }else {
                    //4.判断文件夹 递归
                    copydir(file1,new File(dest,file1.getName()));
    
                }
            }
    
    
        }
    }
    
    
  • 练习二:文件加密

    • 加密原理:对原始文件中的每一个字节数据进行更改,将更改以后的数据存储到新的文件

    • 解密原理:读取加密后的文件,按照加密的规则反向操作,变成原始文件

  • /*
            文件加密
            ^ :异或操作  两边相同结果 false  两边不同 true
            如果两边是数字 把数字变为二进制 进行计算 相同为0 不同为1
            100^10 ->110
            110^10 ->100
    
             */
            //创建对象关联原始文件
    //        FileInputStream fis = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\1.jpg");//没有加密的原始文件
            FileInputStream fis = new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\11.jpg");//加密了的文件
            //创建对象关联加密文件
            FileOutputStream fos = new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\111.jpg");
            //加密处理
            int b;
            while ((b=fis.read())!=-1){
                fos.write(b^2);
            }
    
            fos.close();
            fis.close();
    
  • 练习三:修改文件中的数据

  • /*
            修改文件中数据
            有文本文件 2-1-9-4-7-8
            将其排序变为 1-2-4-7-8-9
             */
            //1.读取数据
            FileReader reader = new FileReader("a.txt");
            StringBuilder sb = new StringBuilder();
            int ch;
            while ((ch=reader.read())!=-1){
                sb.append((char) ch);
            }
            reader.close();
            System.out.println(sb);
            //2.排序
            String str = sb.toString();//转成字符串
            String[] arrstr = str.split("-");//按照-进行切割
    
            ArrayList<Integer> list = new ArrayList<>();
            for (String s : arrstr) {
                int i = Integer.parseInt(s);
                list.add(i);
            }
            System.out.println(list);
            Collections.sort(list);
            System.out.println(list);
    
            //3.排序结果写出
            FileWriter writer = new FileWriter("a.txt");
            for (int i = 0; i < list.size(); i++) {
                if (i==list.size()-1){
                    writer.write(list.get(i)+"");//加+"" 原因如果不加直接写整数进去会是对应的ASCII码
                }else {
                    writer.write(list.get(i)+"-");
                }
            }
            writer.close();
        }
    
  • 非常规写法 stream流和方法引用

  • /*
            修改文件中数据
            有文本文件 2-1-9-4-7-8
            将其排序变为 1-2-4-7-8-9
            非常规写法
            细节
                文件中的数据不要换行
                本地文件可能会有一个隐藏的bom头
             */
            //1.读取数据
            FileReader reader = new FileReader("a.txt");
            StringBuilder sb = new StringBuilder();
            int ch;
            while ((ch=reader.read())!=-1){
                sb.append((char) ch);
            }
            reader.close();
            System.out.println(sb);
            //2.排序
            //stream流操作
            Integer[] arr = Arrays.stream(sb.toString()
                            .split("-"))
                    .map(Integer::parseInt)
                    .sorted()
                    .toArray(Integer[]::new);
    
            System.out.println(Arrays.toString(arr));//[1, 2, 4, 7, 8, 9]
    
            //3.排序结果写出
            FileWriter fw = new FileWriter("a.txt");
            String s= Arrays.toString(arr).replace(", ","-");//字符串替换[1-2-4-7-8-9]
            System.out.println(s);
            String substring = s.substring(1, s.length() - 1);//字符串截取1-2-4-7-8-9
            System.out.println(substring);
            fw.write(substring);
            fw.close();
    

高级:缓冲流 提高效率

为什么可以提高效率:

  • 缓冲流自带长度问为8192的缓冲区(字节数组

  • 可以显著提高字节流的读写性能

  • 对于字符流提升不明显,关键是readLine和nemLine两个特有方法

字节缓冲流
  • BufferedInputStream 字节缓冲输入流 底层自带8192的缓冲区 是对基本流的包装

  • BufferedOutputStream 字节缓冲输出流

  • /*
            利用字节缓冲流 拷贝文件
             */
            //创建缓冲流对象
            BufferedInputStream bis = new BufferedInputStream(
                    new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\a.txt"));
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\copy.txt"));
            //读取
            int b;
            while ((b= bis.read())!=-1){
                bos.write(b);
            }
    
            bos.close();
            bis.close();
    
    
  • /*
            利用字节缓冲流 拷贝文件 使用byte数组 一次读取多个数据
             */
            //创建缓冲流对象
            BufferedInputStream bis = new BufferedInputStream(
                    new FileInputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\a.txt"));
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\copy02.txt"));
            //读取
            byte[] bytes = new byte[1024];
            int b;
            while ((b= bis.read(bytes))!=-1){
                bos.write(bytes,0,b);
            }
    
            bos.close();
            bis.close();
    
  • 原理解析:

    • 缓冲输入和输出都分别有一个缓冲区 两个的缓冲区不一样

字符缓冲流
  • BufferedReader 字符缓冲输入流 特有方法 readLine 读取一行数据 读完返回null

  • /*
           字符缓冲输入流
           特有方法:
           readLine 读取一整行
            */
           BufferedReader br = new BufferedReader(
                   new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\a.txt"));
           //读取数据
           //细节  readLine一次读取一整行 遇到回车换行结束
           //但是不会把回车换行 读到内存中
    //        String line = br.readLine();
    //        System.out.println(line);
    //        String line2 = br.readLine();
    //        System.out.println(line2);
    
            String line;
            while ((line= br.readLine())!=null){
                System.out.println(line);
            }
    
            //
            br.close();
    
  • BufferedWrite 字符缓冲输出流 特有方法 newLine 跨平台的换行

  • /*
           字符缓冲输出流
           特有方法:
               newLine 跨平台的换行
               开启续写在对象FileWriter中
            */
           BufferedWriter bw = new BufferedWriter(
                   new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\myBufferedStream\\b.txt",true));
           bw.write("海拔比阿扁");
           bw.newLine();
           bw.write("lv哈哈哈");
           bw.close();
    
练习
拷贝文件时间测试
public static void main(String[] args) throws Exception {
        /*
        四种不同拷贝文件方式测试时间
         */
        long start = System.currentTimeMillis();
//        method01();//75316毫秒
//        method02();//22毫秒
//        method03();//249毫秒
        method04();//20毫秒
        long end =System.currentTimeMillis();
        System.out.println(end-start+"毫秒");

    }

    //字节流读取 一次读取一个字节
    public static void method01() throws Exception{
        FileInputStream fis = new FileInputStream("E:\\test\\test.mp4");
        FileOutputStream fos = new FileOutputStream("E:\\test\\copy01.mp4");

        int b;
        while ((b=fis.read())!=-1){
            fos.write(b);
        }

        fos.close();
        fis.close();
    }
    //字节流的基本流 一次读取一个字节数组
    public static void method02() throws Exception{
        FileInputStream fis = new FileInputStream("E:\\test\\test.mp4");
        FileOutputStream fos = new FileOutputStream("E:\\test\\copy02.mp4");

        byte[] bytes = new byte[8192];
        int len;
        while ((len=fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }

        fos.close();
        fis.close();
    }
    //字节流的缓冲流 一次读取一个字节
    public static void method03() throws Exception{

        BufferedInputStream br = new BufferedInputStream(new FileInputStream("E:\\test\\test.mp4"));
        BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("E:\\test\\copy03.mp4"));

        int b;
        while ((b=br.read())!=-1){
            bw.write(b);
        }

        bw.close();
        br.close();
    }
    //字节流的缓冲流 一次读取字节数组
    public static void method04() throws Exception{

        BufferedInputStream br = new BufferedInputStream(new FileInputStream("E:\\test\\test.mp4"));
        BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("E:\\test\\copy04.mp4"));

        byte[] bytes = new byte[8192];
        int len;
        while ((len=br.read(bytes))!=-1){
            bw.write(bytes,0,len);
        }

        bw.close();
        br.close();
    }
恢复诗词顺序
/*
        恢复诗词顺序
         */
        BufferedReader br = new BufferedReader(
                new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\a.txt"));
        String line;
        ArrayList<String> list = new ArrayList<>();
        while ((line= br.readLine())!=null){
            System.out.println(line);
            list.add(line);
        }
        br.close();

        //排序
        //排序规则 按照序号排列
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //获取o1 和 o2 的序号
                int i1 = Integer.parseInt(o1.split("\\.")[0]);
                int i2 = Integer.parseInt(o2.split("\\.")[0]);
                return i1-i2;
            }
        });
        System.out.println(list);
        //写入文件
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\sout.txt"));
        for (String str : list) {
            bw.write(str);
            bw.newLine();
        }
        bw.close();
/*
3.这是第三句
5.这是第一句,后面的后面的,就是第五句
1.第一句
4.这是第四句!好
2.这是第二
*/

写法2:通过Tree集合 会自动排序 使用TreeMap 把前面的序号作为键,后面的中文作为值

/*
        恢复诗词顺序
         */
        BufferedReader br = new BufferedReader(
                new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\a.txt"));
        String line;
        TreeMap<Integer,String> tm =new TreeMap<>();//Tree集合会自动排序
        while ((line= br.readLine())!=null){
            System.out.println(line);
            //0. 序号 1:内容
            String[] arr = line.split("\\.");
            tm.put(Integer.parseInt(arr[0]),arr[1]);
        }
        br.close();

        System.out.println(tm);

        //写入文件
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\sout02.txt"));
        Set<Map.Entry<Integer, String>> entries = tm.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            bw.write(entry.getValue());
            bw.newLine();
        }
        bw.close();
验证软件运行次数,运行超过三次收费
/*
        验证软件运行次数,运行超过三次收费
        把标志存入文件中
         */
        //1.读取文件数字
        BufferedReader br = new BufferedReader(
                new FileReader("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\count.txt"));
        String line = br.readLine();
       	br.close();
        int count = Integer.parseInt(line);
        count++;
        if (count<=3){
            System.out.println("欢迎您第"+count+"次使用本软件");
        }else {
            System.out.println("免费次数以用尽,请充值");
        }
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("E:\\idealearn\\javase\\javaselearn\\src\\IOLearn\\Stream\\mytest\\count.txt"));
        bw.write(count+"");
        bw.close();

IO流使用原则随用随创建,不用立即关闭

高级:转换流

属于字符流 是字符流和字节流之间的桥梁

作用:字节流想要使用字符流中的方法

指定字符集读写数据(JDK11中淘汰

  • 转换输入流 InputStreamReader

  • 转换输出流OutputStreamWrite

  • 利用转换流按照指定字符编码读取
    
             */
            /*
            InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"), "GBK");
            //读取
            int ch;
            while ((ch= isr.read())!=-1){
                System.out.print((char) ch);
            }
            isr.close();
            上面方案了解就行,在JDK11被淘汰了
             */
    //        FileReader fr = new FileReader("gbk.txt", Charset.forName("GBK"));
    //        int ch;
    //        while ((ch= fr.read())!=-1){
    //            System.out.print((char) ch);
    //        }
    //        fr.close();
    
  • /*
            利用转换流按照指定字符编码写出
            不要在idea里面修改文件的字符编码,修改的话可能会有很大问题
             */
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk02.txt"),"GBK");
            //写出数据
            osw.write("你好你好");
            osw.close();
            //上面JDK11淘汰了
            //JDK11写法
    
    //        FileWriter fw = new FileWriter("gbk03.txt", Charset.forName("GBK"));
    //        fw.write("JDK11里面使用");
    //        fw.close();
    
    
  • /*
            将本地文件中的GBK文件,转成UTF-8
             */
            InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"), "GBK");
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
            int ch;
            while ((ch=isr.read())!=-1){
                osw.write(ch);
            }
            osw.close();
            isr.close();
            //JDK11以前的写法上面  11后可以不用转换流 直接使用new FileReader  里面可以指定编码
            //new FileWriter("gbk03.txt", Charset.forName("GBK"))
    
  • 练习2:利用字节流读取文件中数据,一次读一整行,不能乱码

  • /*
           利用字节流读取文件中数据,一次读一整行,不能乱码
           字节流没有这些功能所以需要把字节流转换成字符流
           在包装成字符缓冲流
            */
    //        FileInputStream fis = new FileInputStream("gbk.txt");
    //        InputStreamReader isr = new InputStreamReader(fis);
    //        BufferedReader br = new BufferedReader(isr);
    //        String str = br.readLine();
    //        System.out.println(str);
    //        br.close();
            BufferedReader br = new BufferedReader(
                                new InputStreamReader(new FileInputStream("gbk.txt"), "GBK"));
            String line;
            while ((line= br.readLine())!=null){
                System.out.println(line);
            }
            br.close();
    

高级:序列化流

属于字节流

  • 序列化流 输出数据 ObjectOutputStream 把java中的对象写入本地文件

    • 如果对象没有实现Serializable接口 进行序列化会报错java.io.NotSerializableExceptionSerializable接口没有抽象方法 是标记型接口
      
    • /*
              利用序列化流/对象操作输出流,把一个对象写入本地文件
              如果对象没有实现Serializable 进行序列化会报错
              java.io.NotSerializableException
              Serializable接口没有抽象方法 是标记型接口
      
               */
              //创建
              Student student = new Student("zhangsna",23);
              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));
              oos.writeObject(student);
              oos.close();
      

  • 反序列化流 输入数据 ObjectInputStream

    • /*
              利用反序列化流把本地序列化的对象 读取到程序中
               */
              ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt"));
              Student o = (Student) ois.readObject();
              System.out.println(o);
              ois.close();
      
  • 对象实现Serializable接口后会根据对象的信息生成一个版本号,如果对对象进行了修改操作,版本号会改变,这时候如果进行反序列化 两种的版本号不一致就会报错

    • 解决:固定版本号 自己写一个固定的版本号 写法固定:

    • private static final long serialVersionUID = 1L;//第一种写法
      
    • 第二种通过IDea设置检查 然后自动生成

    • private transient String address;//如果不想把该属性序列化 添加关键字transient
      
  • 序列化流后的文件不能修改 一但修改无法再反序列化流

练习

写入

/*
        将多个自定义对象序列化到文件中,但是对象的个数不确定
         */
        Student s1 = new Student("张三", 21, "南京");
        Student s2 = new Student("李四", 20, "北京");
        Student s3 = new Student("王五", 23, "深圳");

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));

        ArrayList<Student> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);
        oos.writeObject(list);


        oos.close();

读出:

/*
        反序列化读取 存了不定量的对象
         */
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        //读取
//        Student o1 = (Student) ois.readObject();
//        Student o2 = (Student) ois.readObject();
//        Student o3 = (Student) ois.readObject();
//        Student o4 = (Student) ois.readObject();//EOFException  读到末尾报异常
//
//        System.out.println(o1);
//        System.out.println(o2);
//        System.out.println(o3);
//        System.out.println(o4);
//        这样读取无法知道是否读完 因此我们要在序列化时把对象放入一个集合 再把集合序列化写入文件

        ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
        System.out.println(list);
        ois.close();

高级:打印流

打印流只能写

特点:

  1. 打印流只能操作文件目的地不能操作数据源

  2. 特有的写出方法 数据原样写出

  3. 特有的写出方法 可以实现自动刷新 自动换行

  • 字节打印流 PrintStream

    • 底层没有缓冲区 开不开自动刷新都一样

    • new 的时候可以传入(OutputStream/File/String)

    • 可以指定字符编码

    • write 普通方法

    • println 特有方法 打印任意数据 自动刷新 自动换行

    • print 特有方法 打印任意数据 不换行

    • printf特有方法 带有占位符的打印语句 不换行

    • /*
              字节打印流
               */
              //创建对象
      //        PrintStream ps = new PrintStream("a.txt");//默认utf-8
              PrintStream ps = new PrintStream(new FileOutputStream("a.txt"),true,"UTF-8");//默认utf-8
              ps.println(97);//原样写出 文件中写出97
              ps.print(true);
              ps.printf("%s 爱死了 %s","A","B");//占位符
              /*
              %s 字符串的占位符
              %n 换行
              %c 把字符变成大写
              %b 布尔类型
              。。。。。
               */
              ps.close();
      

  • 字符打印流 PrintWriter

    • 字符打印流底层有缓冲区 自动刷新需要开启

    • 构造方法和字节打印流一样 成员方法有一样

    • /*
              字符打印流 自动刷新需要自己开启
               */
              PrintWriter pw = new PrintWriter(new FileWriter("a.txt"),true);
              pw.println("你好");
              pw.print("我不好");
              pw.printf("%s 很好","它");
      
              pw.close();
      
  • 输出语句和打印流之间的关系

    • //获取打印流的对象,此打印流在虚拟机启动的时候 由虚拟机创建 默认指向控制台//特殊的打印流 系统中的标准输出流 不能被关闭的 在系统中唯一
      PrintStream ps = System.out;
      ps.println("123");
      

高级:压缩流

属于字节流

  • 解压缩流

    • 在java中压缩包中的每一个文件就是一个ZipEntry对象

    • public static void main(String[] args) throws Exception {
              /*
              解压缩流
               */
              File src = new File("aaa.zip");
              File dest = new File("E:\\");
              unZip(src,dest);
          }
      
          public static void unZip(File src,File dest) throws Exception{
              //解压本质是把压缩包里面的文件或文件夹读取出来 按照层级拷贝
      
              ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
      
      //        ZipEntry entry = zip.getNextEntry();
      //        System.out.println(entry);
      //        //如果获取完毕 后面返回null
      //        ZipEntry entry1 = zip.getNextEntry();
      //        System.out.println(entry1);
              ZipEntry entry;
              while ((entry= zip.getNextEntry())!=null){
                  System.out.println(entry);
                  //文件夹 在目的地创建一个同样的文件夹
                  //文件 按照层级目录存放
                  if (entry.isDirectory()){
                      //是文件夹
                      File file = new File(dest,entry.toString());
                      file.mkdir();
                  }else {
                      //目的地:
                      FileOutputStream fos = new FileOutputStream(new File(dest,entry.toString()));
                      int b;
                      while ((b=zip.read())!=-1){
                          fos.write(b);
                      }
                      fos.close();
                      zip.closeEntry();
                  }
              }
              zip.close();
          }
      
  • 压缩流

    • 压缩单独一个文件

    • public static void main(String[] args) throws Exception {
              /*
              压缩流
              把一个文件压缩
               */
              //创建对象
              File src = new File("E:\\aaa\\aaa.txt");
              File dest = new File("E:\\aaa");
              toZip(src,dest);
          }
      
          public static void toZip(File src,File dest) throws Exception {
              ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));
              //创建ZipEntry对象 表示压缩包里的每一个文件和文件夹
              ZipEntry entry = new ZipEntry("aaa.txt");
              //把entry放入压缩包
              zos.putNextEntry(entry);
              //上面只能把结构建好 数据还没有写入
              FileInputStream fis = new FileInputStream(src);
              int b;
              while ((b=fis.read())!=-1){
                  zos.write(b);
              }
              fis.close();
      
              zos.closeEntry();
              zos.close();
      
          }
      
    • //        ZipEntry entry = new ZipEntry("bbb\\ccc\\aaa.txt");里面的路径表示压缩包里面的路径
      
      
    • 压缩文件夹

    • public static void main(String[] args) throws Exception{
              /*
              压缩流
              压缩文件夹
               */
              //被压缩文件夹
              File src = new File("E:\\aaa");
              //压缩包被放在哪里
              File destParent = src.getParentFile();//D:\\
              //创建File对象 表示压缩包路径
              File dest = new File(destParent,src.getName()+".zip");
              System.out.println(dest);
              //创建压缩流
              ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
              //获取src中的每一个文件 变成ZipEntry对象 放入压缩包
      
              toZip(src,zos, src.getName());
              zos.close();
          }
      
          /*
          获取src中的每一个文件 变成ZipEntry对象 放入压缩包
          src 数据源 zos 压缩流 name 内部路径
           */
          public static void toZip(File src,ZipOutputStream zos,String name) throws Exception {
              //进入src文件夹
              File[] files = src.listFiles();
              //遍历数组
              for (File file : files) {
                  if (file.isFile()){
                      //文件 变成ZipEntry对象 放入压缩包中
                      ZipEntry entry = new ZipEntry(name+"\\"+file.getName());//file 是一条路径 这里name是前面的路径aaa
                      zos.putNextEntry(entry);
                      //读取文件
                      FileInputStream fis = new FileInputStream(file);
                      int b;
                      while ((b= fis.read())!=-1){
                          zos.write(b);
                      }
                      fis.close();
                      zos.closeEntry();//表示当前文件书写完毕
                  }else {
                      //文件夹 i递归
                      toZip(file,zos,name+"\\"+file.getName());
                  }
              }
          }
      

Commons-io IO操作的开源工具包

提高io流开发效率

  • FileUtils类 (文件,文件夹相关)

  • IOUtils类 (流相关)

/*
        FileUtils类
         */
//        File src = new File("a.txt");
//        File dest = new File("copy.txt");
//        FileUtils.copyFile(src,dest);//拷贝文件
        //拷贝文件夹
        File src = new File("E:\\aaa");
        File dest = new File("E:\\bbb");
        FileUtils.copyDirectory(src,dest);//直接拷贝aaa里面的内容放入bbb
        FileUtils.copyDirectoryToDirectory(src,dest);//直接把aaa整个放入bbb (在bbb中有了aaa这个文件夹 上面的没有)
        FileUtils.deleteDirectory(src);//删除整个文件夹 aaa也没有了
        FileUtils.cleanDirectory(src);//清空文件夹 aaa里面的内容没有了 aaa还存在

Hutool工具包

 /*
        Hutool工具包
            FileUtil类
         */
        /*
        File file = FileUtil.file("D:\\", "aaa", "a.txt");//工具参数创建file对象 强大:可以写可变参数 自动拼接
        System.out.println(file);//D:\aaa\a.txt

        File touch = FileUtil.touch(file);//根据参数创建文件 父级不存在 会帮你创建
        System.out.println(touch);

        //写入文件按行
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("aaa");
        list.add("aaa");
        File file1 = FileUtil.writeLines(list, "E:\\a.txt", "UTF-8", false);//false 不追加 true追加
        System.out.println(file1);
        */

        //追加写入
        /*
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("aaa");
        File file3 = FileUtil.appendLines(list, "E:\\a.txt", "UTF-8");
        System.out.println(file3);
        */

        //把文件读取后 放入集合返回
        List<String> list = FileUtil.readLines("E:\\a.txt", "UTF-8");
        System.out.println(list);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值