IO流
本文最后更新于9 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

IO流概述

1.什么是IO流:

  • 存储和读取数据的解决方案
  • I:input O:output
  • 流:像水流一样传输数据

2.IO流的作用:

  • 用于读写数据(本地文件,网络)

3.IO流按照流向可以分类为哪两种流:

  • 输出流:程序->文件
  • 输入流:文件->程序

4.IO流按照操作文件的类型可以分为哪两种流

  • 字节流:可以操作所有类型的文件
  • 字符流:可以操作纯文本文件

问题:既然字节流可以操作所有类型的文件,那么字符流存在的意义是什么?

5.什么是纯文本文件?

  • 用windows系统自带的记事本打开并且能读懂的文件

IO流的体系

字节输出流FileOutputStream

基本用法

字节输出流初体验:

public class ByteStreamDemo1 {
    public static void main(String[] args) throws IOException {
        //创建对象
        //字节输出流 OutputStream
        FileOutputStream fileOutputStream = new FileOutputStream("a.txt");
        //写出数据
        fileOutputStream.write(97);
        //释放资源
        fileOutputStream.close();
    }
}

问题:出现FileNotFind的错误

解决:通过了解IDEA相对路径的原理,知道了相对路径的起始文件夹是整个项目所在的根目录,将”collectionDemo\\a.txt”改为”a.txt”,问题解决

换行和续写

换行写:

  • 再次写出一个换行符就可以了
  • windows:\r\n
  • linux:\n
  • mac:\r
  • 细节:
    • 在windows操作系统当中,java对回车换行进行了优化。
    • 虽然完整的是\r\n,但是我们写其中一个\r或者\n,
    • java也可以实现换行,因为java在底层会补全
    • 建议不要省略

续写:

  • 如果想要续写,打开续写开关就可以了
  • 开关位置:创建对象的第二个参数
  • 默认false:表示关闭续写,此时创建对象就会清空文件
  • 手动传递true:表示打开续写,此时创建对象不会清空文件
public class ByteStreamDemo2 {
    public static void main(String[] args) throws IOException {
        //换行续写
        FileOutputStream fos = new FileOutputStream("a.txt", true);
        String str1 = "abcd";
        byte[] bytes1 = str1.getBytes();
        fos.write(bytes1);

        //添加换行符
        String crlf = "\r\n";
        byte[] bytes2 = crlf.getBytes();
        fos.write(bytes2);

        //写数据
        String str2 = "efg";
        byte[] bytes3 = str2.getBytes();
        fos.write(bytes3);
        fos.close();
    }
}

字节输入流FileInputStream

基本使用

public class ByteStreamDemo1 {
    public static void main(String[] args) throws IOException {
        //字节输入基本使用
        FileInputStream fis = new FileInputStream("a.txt");
        //读数据
        int i = fis.read();
        System.out.println((char) i);
        fis.close();
    }
}

循环读取

public class ByteStreamDemo2 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        int b;
        while ((b = fis.read()) != -1) {
            System.out.print((char) b);
        }
        fis.close();
    }
}

文件拷贝

基本使用:

public class ByteStreamDemo3 {
    //文件拷贝
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        FileOutputStream fos = new FileOutputStream("b.txt");
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b);
        }
        fis.close();
        fos.close();
    }
}

一次读取一个字节的弊端:读写速度慢

一次读取多个数据:

public class ByteStreamDemo4 {
    //一次读取多个数据
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        //创建数组,读取字节数和字节数组的大小有关
        byte[] bytes = new byte[2];
        int len;
        len = fis.read(bytes);
        System.out.println(len);
        System.out.println(new String(bytes, 0 , len));
        len = fis.read(bytes);
        System.out.println(len);
        System.out.println(new String(bytes, 0 , len));
        len = fis.read(bytes);
        System.out.println(len);
        System.out.println(new String(bytes, 0 , len));
        fis.close();
    }
}

文件拷贝(一次读写多个字节)

public class ByteStreamDemo5 {
    //文件拷贝
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("a.txt");
        FileOutputStream fos = new FileOutputStream("b.txt");
        int len;
        byte[] bytes = new byte[1024 * 1024 * 5];
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0 , len);
        }
        fis.close();
        fos.close();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

IO流中不同JDK版本捕获异常的方式

JDK7:

将流对象的创建放在try()中

public class ByteStreamDemo5 {
    //JDk7捕获异常
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        try(FileInputStream fis = new FileInputStream("a.txt");
            FileOutputStream fos = new FileOutputStream("b.txt")) {
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0 , len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

JDK9:

将流对象的创建放在try-catch外面,try()中只写对象的引用即可

public class ByteStreamDemo6 {
    //JDk9捕获异常
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("a.txt");
        FileOutputStream fos = new FileOutputStream("b.txt");
        try(fis;fos) {
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0 , len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

字符集详解

ASCII,GBK

Unicode

乱码

出现的原因:

  • 1.用字节流读取数据时,一个字节一个字节的读,会导致乱码,比如一个汉字在Unicode字符集中占3个字节的大小,如果只读取了第一个字节,就错了
  • 2.编码解码的方式不同也会导致乱码

如何避免:

  • 1.不要用字节流读取文本文件
  • 2.编码解码时使用同一个码表,同一个编码方式

编码与解码:

Java中编码的方式:

  • public byte[] getBytes() 使用默认方式进行编码
  • public byte[] getBytes(String charsetName) 使用指定方式进行编码

Java中解码的方式:

  • String(byte[] bytes) 使用默认方式进行解码
  • String(byte[] bytes, String charsetName) 使用指定方式进行解码
public class CharSetDemo1 {
    //编码解码
    public static void main(String[] args) throws UnsupportedEncodingException {
        //编码UTF-8
        String s = "yao加油";
        byte[] bytes1 = s.getBytes();
        System.out.println(Arrays.toString(bytes1));
        //GBK
        byte[] bytes2 = s.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));

        //解码
        String s1 = new String(bytes1);
        String s2 = new String(bytes2);
        System.out.println(s1);
        System.out.println(s2);
    }
}

字符输入流FileReader

基本使用

read()细节:

  • 1.read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
  • 2.在读取之后,方法的底层还会进行解码并转成十进制
    最终把这个十进制作为返回值
    这个十进制的数据也表示在字符集上的数字
    英文:文件里面二进制数据 0110 0001
    read方法进行读取,解码并转成十进制97
    中文:文件里面的二进制数据 11100110 10110001 10001001
    read方法进行读取,解码并转成十进制27721
    如果像看到中文汉字,就是把这些十进制数据,再进行强转即可

空参read方法:

public class CharStreamDemo1 {
    //字符输入流基本使用
    public static void main(String[] args) throws IOException {
        //创建对象
        FileReader fr = new FileReader("a.txt");
        //读取数据
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.print((char) ch);
        }
        fr.close();
    }
}

有参read方法:

public class CharStreamDemo2 {
    //有参read方法
    public static void main(String[] args) throws IOException {
        //创建对象
        FileReader fr = new FileReader("a.txt");
        //读取数据
        char[] chars = new char[2];
        int len;
        while ((len = fr.read(chars)) != -1) {
            System.out.print(new String(chars, 0, len));
        }
        fr.close();
    }
}
  • 有参read方法把读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中,返回读取字符的个数
  • 有参read = 空参read + 强制类型转换

底层原理

字符输出流FileWriter

构造方法

成员方法

书写细节

基本使用

public class CharStreamDemo3 {
    //字符输出流
    public static void main(String[] args) throws IOException {
        //创建对象
        FileWriter fw = new FileWriter(new File("a.txt"), true);
        fw.write(25105);
        fw.write("\r\n");
        fw.write("收服到神奇宝贝啦!!!");
        fw.write("\r\n");
        char[] arr = {'是', '小', '火', '龙', '!'};
        fw.write(arr);
        fw.close();
    }
}

底层原理

综合练习1

Test01

public class Test01 {
    /*
    * IO流综合练习1
    * 需求:
    *   拷贝文件夹
    *   考虑子文件夹
    */
    public static void main(String[] args) throws IOException {
        File root = new File("aaa");
        File target = new File("bbb");
        copyFile(root, target);
    }

    private static void copyFile(File root, File target) throws IOException {
        target.mkdirs();
        //获取文件夹中的所有文件
        File[] files = root.listFiles();
        //遍历
        for (File file : files) {
            //判断
            if(file.isFile()) {
                //拷贝
                //字节流
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(target, file.getName()));
                byte[] bytes = new byte[1024];
                int len;
                while ((len = fis.read(bytes)) != -1) {
                    fos.write(bytes, 0, len);
                }
                fis.close();
                fos.close();
            } else{
                //递归
                copyFile(file, new File(target, file.getName()));
            }
        }
    }
}

Test02

public class Test02 {
    //加密解密文件
    public static void main(String[] args) throws IOException {
      /*  
        //加密
        FileInputStream fis = new FileInputStream("a.txt");
        FileOutputStream fos = new FileOutputStream("b.txt");
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b ^ 100);
        }
        fis.close();
        fos.close();*/
        //解密
        FileInputStream fis = new FileInputStream("b.txt");
        FileOutputStream fos = new FileOutputStream("a.txt");
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b ^ 100);
        }
        fis.close();
        fos.close();
    }
}

Test03

public class Test03 {
    /*
    * 修改文件数据
    * 对8-2-4-1-6进行排序
    * 常规方法
    */
    public static void main(String[] args) throws IOException {
        //读取数据
        //字符流
        FileReader fr = new FileReader("a.txt");
        int b;
        StringBuilder sb = new StringBuilder();
        while ((b = fr.read()) != -1) {
            sb.append((char) b);
        }
        fr.close();
        String strArr = sb.toString();
        String[] split = strArr.split("-");
        ArrayList<Integer> list = new ArrayList<>();
        for (String s : split) {
            list.add(Integer.parseInt(s));
        }
        Collections.sort(list);
        FileWriter fw = new FileWriter("a.txt");
        for (int i = 0; i < list.size(); i++) {
            if(i == list.size() - 1) {
                fw.write(list.get(i) + "");
            } else {
                fw.write(list.get(i) + "-");
            }
        }
        fw.close();
    }
}

Test04

public class Test04 {
    /*
     * 修改文件数据
     * 对8-2-4-1-6进行排序
     * 进阶方法
     */
    public static void main(String[] args) throws IOException {
        //读取数据
        //字符流
        FileReader fr = new FileReader("a.txt");
        int b;
        StringBuilder sb = new StringBuilder();
        while ((b = fr.read()) != -1) {
            sb.append((char) b);
        }
        fr.close();
        Integer[] array = Arrays.stream(sb.toString().split("-"))
                .map(Integer::parseInt)
                .sorted()
                .toArray(Integer[]::new);
        String s = Arrays.toString(array).replace(", ", "-");
        s = s.substring(1, s.length() - 1);
        FileWriter fw = new FileWriter("a.txt");
        fw.write(s);
        fw.close();
        System.out.println(s);
    }
}

字节缓冲流

体系

构造方法

可以添加第二个参数指定缓冲区大小,比如:BufferedInputStream(InputStream is, int size);

基本使用

使用字节缓冲流进行拷贝(一次操作一个字节):

public class BufferedStreamDemo1 {
    //用字节缓冲流进行拷贝
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.txt"));
        int b;
        while ((b = bis.read()) != -1) {
            bos.write(b);
        }
        bis.close();
        bos.close();
    }
}

使用字节缓冲流进行拷贝(一次操作多个字节):

public class BufferedStreamDemo2 {
    //用字节缓冲流进行拷贝(一次读写多个字节)
    public static void main(String[] args) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy2.txt"));
        int len;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bis.close();
        bos.close();
    }
}

原理

问题:字节缓冲流的缓冲区是什么时候创建的,以及是什么时候初始化的?

解答:

  • 创建(分配内存):发生在 new 对象(调用构造方法) 时。
  • 初始化(填充数据/刷新):对于输入流,发生在第一次调用 read() 时;对于输出流,发生在调用 flush() 或缓冲区写满时。

字符缓冲流

构造方法

特有方法

基本使用

用字符缓冲输入流读取数据

public class BufferedStreamDemo3 {
    //用字符缓冲输入流读取数据
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

用字符缓冲输出流写数据

public class BufferedStreamDemo4 {
    //用字符缓冲输出流写数据
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt", true));
        bw.write("哈喽呀");
        bw.newLine();
        bw.write("我换行了呀");
        bw.newLine();
        bw.close();
    }
}

转换流

概述

基本使用

指定字符集读写

public class ConvertStreamDemo1 {
    /*
    * 利用转换流按照指定字符编码读取
    * gbkfile.txt
    */
    public static void main(String[] args) throws IOException {
        /*InputStreamReader isr = new InputStreamReader(new FileInputStream("gbkfile.txt"), "GBK");
        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }
        isr.close();*/

        //JDK11之后
        FileReader fr = new FileReader("gbkfile.txt", Charset.forName("GBK"));
        int b;
        while ((b = fr.read()) != -1) {
            System.out.print((char) b);
        }
        fr.close();
    }
}
public class ConvertStreamDemo2 {
/*
* 利用转换流按照指定字符集编码写出
*/
public static void main(String[] args) throws IOException {
/*OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbkfile.txt"), "GBK");
osw.write("哈喽呀");
osw.close();*/
//GDK11之后
FileWriter fw = new FileWriter("b.txt", Charset.forName("GBK"));
fw.write("你好呀");
fw.close();
}
}

public class ConvertStreamDemo3 {
/*
* 将本地文件的GBK文件,转成UTF-8
*/
public static void main(String[] args) throws IOException {
//JDK11之前
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbkfile.txt"), "GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "UTF-8");
int b;
while ((b = isr.read()) != -1) {
osw.write(b);
}
isr.close();
osw.close();
//JDK11之后
FileReader fr = new FileReader("gbkfile.txt", Charset.forName("GBK"));
FileWriter fw = new FileWriter("c.txt", Charset.forName("UTF-8"));
int c;
while ((c = fr.read()) != -1) {
fw.write(c);
}
fr.close();
fw.close();
}
}

利用字节流读取文件中的数据(用字符缓冲流方法)

public class ConvertStreamDemo4 {
    /*
    * 利用字节流读取文件中的数据(用字符缓冲流方法)
    */
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

总结

序列化流

概述

基本使用

将一个对象写入文件中:

public class ObjectStreamDemo1 {
    /*
    * 利用序列化流将一个对象写入文件中
    */
    public static void main(String[] args) throws IOException {
        Student stu = new Student("章鱼哥", 30);
        ObjectOutputStream ois = new ObjectOutputStream(new FileOutputStream("a.txt"));
        ois.writeObject(stu);
        ois.close();
    }
}

注意:使用对象输出流将对象保存到文件时会出现NotSerializableException异常

解决方案:需要让Javabean类实现Serializable接口,该接口没有抽象方法,是一个标记型接口

将文件中的数据写入对象中

public class ObjectStreamDemo2 {
    /*
    * 利用反序列化流将文件中的数据写出到对象中
    */
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        Object o = ois.readObject();
        ois.close();
        System.out.println(o);
    }
}

细节

综合练习

写多个对象

public class Test05 {
    /*
    * 写多个对象
    */
    public static void main(String[] args) throws IOException {
        ObjectOutputStream ois = new ObjectOutputStream(new FileOutputStream("a.txt"));
        ArrayList<Student> stuList = new ArrayList<>();
        Collections.addAll(stuList, new Student("海绵宝宝", 18), new Student("章鱼哥", 30), new Student("派大星", 20));
        ois.writeObject(stuList);
        ois.close();
    }
}

读多个对象

public class Test06 {
    //读多个对象
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        ArrayList<Student> list = (ArrayList<Student>)ois.readObject();
        ois.close();
        System.out.println(list);
    }
}

打印流

概述

字节打印流

构造方法

成员方法

基本使用

public class PrintStreamDemo1 {
    /*
    * 字节打印流
    */
    public static void main(String[] args) throws FileNotFoundException {
        PrintStream ps = new PrintStream(new FileOutputStream("a.txt"), true, Charset.forName("UTF-8"));
        ps.println("哈喽呀");
        ps.print("我是蜡笔小新");
        ps.println();
        ps.printf("%s是%s的", "蜡笔小新", "春日部");
        ps.close();
    }
}

字符打印流

构造方法

成员方法

基本使用

public class PrintStreamDemo2 {
    /*
    * 字符打印流
    */
    public static void main(String[] args) throws IOException {
        PrintWriter pw = new PrintWriter(new FileWriter("a.txt"), true);
        pw.println("哈喽呀");
        pw.print("我是蜡笔小新");
        pw.println();
        pw.printf("%s是%s的", "蜡笔小新", "春日部");
        pw.close();
    }
}

总结

解压缩流/压缩流

解压缩流

作用

将一个压缩包解压

基本使用

public class ZipStreamDemo1 {
    //解压缩流
    public static void main(String[] args) throws IOException {
        File root = new File("F:\\TestFile\\IO\\aaa.zip");
        File target = new File("F:\\TestFile\\IO\\");
        unzip(root, target);
    }

    private static void unzip(File root, File target) throws IOException {
        ZipInputStream zip = new ZipInputStream(new FileInputStream(root));
        ZipEntry entry;
        while ((entry = zip.getNextEntry()) != null) {
            File file = new File(target, entry.toString());
            if (entry.isDirectory()) {
                file.mkdirs();
            } else {
                int b;
                FileOutputStream fos = new FileOutputStream(file);
                while ((b = zip.read()) != -1) {
                    fos.write(b);
                }
                fos.close();
                zip.closeEntry();
            }
            System.out.println(entry.toString());
        }
        zip.close();
    }
}

压缩流

作用

将一个文件夹或者文件进行压缩

基本使用

压缩一个文件夹

public class ZipStreamDemo2 {
    //压缩流压缩文件夹
    public static void main(String[] args) throws IOException {
        //压缩哪个文件?
        File src = new File("F:\\TestFile\\IO\\aaa");
        //压缩到哪?
        File parentFile = src.getParentFile();//F:\TestFile\IO
        File dest = new File(parentFile, src.getName() + ".zip");//F:\TestFile\IO\aaa.zip
        //创建压缩流对象
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
        //将src中的每个文件夹或文件转换成ZipEntry对象存入aaa.zip
        toZip(src, zos, src.getName());
        //释放资源
        zos.close();

    }

    private static void toZip(File src, ZipOutputStream zos, String name) throws IOException {
        //获取src中的所有文件
        File[] files = src.listFiles();
        //遍历
        for (File file : files) {
            if(file.isFile()) {
                //文件,转换为ZipEntry对象,put进zos
                ZipEntry zipEntry = new ZipEntry(name + "\\" + file.getName());
                zos.putNextEntry(zipEntry);
                //拷贝文件中的数据到entry对象中
                int b;
                FileInputStream fis = new FileInputStream(file);
                while ((b = fis.read()) != -1) {
                    zos.write(b);
                }
                fis.close();
                zos.closeEntry();
            } else {
                //文件夹,递归
                toZip(file, zos, name + "\\" + file.getName());
            }

        }
    }
}

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇