Java-Python-Django社区我爱编程

【JavaSE(十三)】JavaIO流(中)

2018-05-28  本文已影响10人  苍云横渡

1 IO流

1.1 IO流概述

Java中使用 IO流 来读取和写入,读写设备上的数据、硬盘文件、内存、键盘等等,一个流被定义为一个数据序列。

根据数据的走向可分为输入流和输出流

根据处理的数据类型可分为*字节流和字符流

什么情况下使用哪种流呢?

下图是一个描述输入流和输出流的类层次图。

IO流常用基类:

1.2 字节流写数据

通过查看API可以看到OutputStream是一个抽象类,不能实例化。所以我们需要一个继承自OutputStream的实现类,FileOutputStream文件输出流

文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。

其构造方法:FileOutputStream(File file)FileOutputStream(String name)

字节输出流操作步骤:

字节输出流方法:(抛出: IOException - 如果发生 I/O 错误)

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");
        //写数据
        fos.write(97);

        byte[] bys={97,98,99,100,101};
        fos.write(bys);

        fos.write("\r\n".getBytes());

        fos.write("java".getBytes());
        //释放资源
        fos.close(); //关闭此文件输出流并释放与此流有关的所有系统资源
    }
}

/*文件内容:
aabcde
java
*/

如何实现数据的追加写入:用构造方法带第二个参数是true的情况即可。

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo3 {
    public static void main(String[] args) throws IOException {
        // 创建字节输出流对象
        // 创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
        FileOutputStream fos = new FileOutputStream("fos3.txt", true);

        // 写数据
        fos.write("\r\n".getBytes());
        fos.write(("hello").getBytes());
        

        // 释放资源
        fos.close();
    }
}
/*文件内容:
aabcde
java
hello
*/

标准代码(带异常处理)

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo4 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("fos.txt");
            fos.write("java".getBytes());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 如果fos不是null,才需要close()
            if (fos != null) {
                // 为了保证close()一定会执行,就放到这里了
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1.3 字节流读数据

和OutputStream一样,InputStream也是一个抽象类。所以我们使用其具体实现类FileInputStream文件输入流。FileInputStream 从文件系统中的某个文件中获得输入字节。

其操作步骤如下:

读取数据的方式:

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("fos.txt");
        int by = 0;
        // 读取,赋值,判断
        while ((by = fos.read()) != -1) {
            System.out.print((char) by);
        }
        fis.close();
    }
}

当fos.txt里面有中文时,是不能正常显示的,因为by被强制转换成字符输出了。

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("fos.txt");
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = fis.read(bys)) != -1) {
            System.out.print(new String(bys, 0, len));
        }
        fis.close();
    }
}

在计算机中中文的存储分两个字节:第一个字节肯定是负数;第二个字节常见的是负数,可能有正数,但是没影响。当识别到负数的时候,计算机就把两个字节转换为一个中文。所以上述的 bys 可以输出中文。

1.4 字节流复制文件

需求:把 e:\a.mp4 复制到当前项目目录下的 copy.mp4 中。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyMp4Demo {
    public static void main(String[] args) throws IOException {
        // 封装数据源
        FileInputStream fis = new FileInputStream("e:\\a.mp4");
        // 封装目的地
        FileOutputStream fos = new FileOutputStream("copy.mp4");

        // 复制数据
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = fis.read(bys)) != -1) {
            fos.write(bys, 0, len);
        }

        // 释放资源
        fos.close();
        fis.close();
    }
}

1.5 字节缓冲区流

字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想,所以提供了字节缓冲区流

构造方法传入的是一个字节流对象(还可以指定缓冲区的大小,但是我们一般用不上,因为默认缓冲区大小就足够了)。

为什么不传递一个具体的文件或者文件路径,而是传递一个字节流对象呢?

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("bos.txt"));
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));

        // 读数据
        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            System.out.print(new String(bys, 0, len));
        }

        // 写数据
        bos.write("hello".getBytes());

        // 释放资源
        bos.close();
        bis.close();
    }
}

2 字符流

2.1 字符流概述

由于字节流操作中文数据不是特别的方便,所以就出现了转换流。而转换流的作用就是把字节流转换成字符流来使用。

转换流其实是一个字符流:字符流 = 字节流 + 编码表

2.2 编码表

编码表是由字符和对应的数值组成的一张表。

常见编码表:

字符串中的编码:

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class StringDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "你好";

        // String -- byte[]
        byte[] bys = s.getBytes(); // [-60, -29, -70, -61]
        byte[] bys1 = s.getBytes("GBK");// [-60, -29, -70, -61]
        byte[] bys2 = s.getBytes("UTF-8");// [-28, -67, -96, -27, -91, -67]

        // byte[] -- String
        String ss = new String(bys); // 你好
        String ss1 = new String(bys, "GBK"); // 你好
        String ss2 = new String(bys, "GBK"); // 浣犲ソ
    }
}

2.3 OutputStreamWriter类及其方法

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class OutputStreamWriterDemo {
    public static void main(String[] args) throws IOException {
        // 创建对象
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "UTF-8"); // 指定UTF-8
        // 写数据
        osw.write("中国");
        // 释放资源
        osw.close();
    }
}

方法:

查阅文档可以发现,IO流中每一个类都实现了Closeable接口,它们进行资源操作之后都需要执行close()方法将流关闭 。但字节流与字符流的不同之处在于:字节流是直接与数据产生交互,而字符流在与数据交互之前要经过一个缓冲区 。如下图:

为什么循环写入的时候,每一次循环里边都要刷新呢?

close()和flush()的区别:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class OutputStreamWriterDemo {
    public static void main(String[] args) throws IOException {
        // 创建对象
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw2.txt"));

        char[] chs = {'a','b','c','d','e'};
        // public void write(char[] cbuf):写一个字符数组
        osw.write(chs);
        // public void write(char[] cbuf,int off,int len):写一个字符数组的一部分
        osw.write(chs,1,3);

        // public void write(String str):写一个字符串
        osw.write("我爱林青霞");

        // public void write(String str,int off,int len):写一个字符串的一部分
        osw.write("我爱林青霞", 2, 3);

        // 刷新缓冲区
        osw.flush();
        osw.write("我爱林青霞", 2, 3);

        // 释放资源
        osw.close();
        // osw.write("我爱林青霞", 2, 3); // java.io.IOException: Stream closed
    }
}

2.4 InputStreamReader类及其方法

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "UTF-8");
        // 读取数据,一次读取一个字符
        int ch = 0;
        while ((ch = isr.read()) != -1) {
            System.out.print((char) ch);
        }
        // 释放资源
        isr.close();
    }
}

方法:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException {
        // 创建对象
        InputStreamReader isr = new InputStreamReader(new FileInputStream("StringDemo.java"));

        // 一次读取一个字符数组
        char[] chs = new char[1024];
        int len = 0;
        while ((len = isr.read(chs)) != -1) {
            System.out.print(new String(chs, 0, len));
        }

        // 释放资源
        isr.close();
    }
}

2.5 字符缓冲流

字符流为了高效读写,也提供了对应的字符缓冲流。字符缓冲流有自己的特殊方法。构造方法传入一个字符流。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedDemo {
    public static void main(String[] args) throws IOException {
        write();
        read();
    }

    private static void read() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("bw2.txt"));
        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }

    private static void write() throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw2.txt"));
        for (int x = 0; x < 10; x++) {
            bw.write("hello" + x);
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}

其中 BufferedReader 还有一个子类 LineNumberReader ,他有两个特殊方法:

import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

public class LineNumberReaderDemo {
    public static void main(String[] args) throws IOException {
        LineNumberReader lnr = new LineNumberReader(new FileReader("my.txt"));

        // 从10开始
        // lnr.setLineNumber(10);

        String line = null;
        while ((line = lnr.readLine()) != null) {
            System.out.println(lnr.getLineNumber() + ":" + line);
        }

        lnr.close();
    }
}

2.6 IO流小结

字节流

字符流

2.7 五种字符流复制文件方法

除了 OutputStreamWriter、InputStreamReader 可以用来读写文件,Java还提供了简单类名的 FileWriter、FileReader 来读写文件,当然,还有更高效的 BufferedWriter、BufferdReader 。

推荐使用字符缓冲流一次读写一个字符串。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
 * 分析:
 *      复制数据,如果用记事本打开并能够读懂,就用字符流,否则用字节流。采用字符流更方便一些。
 * 数据源:
 *      c:\\a.txt -- FileReader -- BufferdReader
 * 目的地:
 *      d:\\b.txt -- FileWriter -- BufferedWriter
 */
public class CopyFileDemo {
    public static void main(String[] args) throws IOException {
        String srcString = "c:\\a.txt";
        String destString = "d:\\b.txt";
        // method1(srcString, destString);
        // method2(srcString, destString);
        // method3(srcString, destString);
        // method4(srcString, destString);
        method5(srcString, destString);
    }

    // 字符缓冲流一次读写一个字符串
    private static void method5(String srcString, String destString)
            throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(srcString));
        BufferedWriter bw = new BufferedWriter(new FileWriter(destString));

        String line = null;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        bw.close();
        br.close();
    }

    // 字符缓冲流一次读写一个字符数组
    private static void method4(String srcString, String destString)
            throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(srcString));
        BufferedWriter bw = new BufferedWriter(new FileWriter(destString));

        char[] chs = new char[1024];
        int len = 0;
        while ((len = br.read(chs)) != -1) {
            bw.write(chs, 0, len);
        }

        bw.close();
        br.close();
    }

    // 字符缓冲流一次读写一个字符
    private static void method3(String srcString, String destString)
            throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(srcString));
        BufferedWriter bw = new BufferedWriter(new FileWriter(destString));

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

        bw.close();
        br.close();
    }

    // 基本字符流一次读写一个字符数组
    private static void method2(String srcString, String destString)
            throws IOException {
        FileReader fr = new FileReader(srcString);
        FileWriter fw = new FileWriter(destString);

        char[] chs = new char[1024];
        int len = 0;
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs, 0, len);
        }

        fw.close();
        fr.close();
    }

    // 基本字符流一次读写一个字符
    private static void method1(String srcString, String destString)
            throws IOException {
        FileReader fr = new FileReader(srcString);
        FileWriter fw = new FileWriter(destString);

        int ch = 0;
        while ((ch = fr.read()) != -1) {
            fw.write(ch);
        }

        fw.close();
        fr.close();
    }
}

2.8 四种字节流复制图片方法

推荐掌握第4种,字节缓冲流一次读写一个字节数组。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
 * 数据源:
 *      c:\\a.jpg -- FileInputStream -- BufferedInputStream
 * 目的地:
 *      d:\\b.jpg -- FileOutputStream -- BufferedOutputStream
 */
public class CopyImageDemo {
    public static void main(String[] args) throws IOException {
        // 使用File对象做为参数
        File srcFile = new File("c:\\a.jpg");
        File destFile = new File("d:\\b.jpg");

        // method1(srcFile, destFile);
        // method2(srcFile, destFile);
        // method3(srcFile, destFile);
        method4(srcFile, destFile);
    }

    // 字节缓冲流一次读写一个字节数组
    private static void method4(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }

        bos.close();
        bis.close();
    }

    // 字节缓冲流一次读写一个字节
    private static void method3(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));

        int by = 0;
        while ((by = bis.read()) != -1) {
            bos.write(by);
        }

        bos.close();
        bis.close();
    }

    // 基本字节流一次读写一个字节数组
    private static void method2(File srcFile, File destFile) throws IOException {
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);

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

        fos.close();
        fis.close();
    }

    // 基本字节流一次读写一个字节
    private static void method1(File srcFile, File destFile) throws IOException {
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);

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

        fos.close();
        fis.close();
    }
}

2.9 字节流复制多级文件夹

需求:复制多极文件夹

分析:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyFoldersDemo {
    public static void main(String[] args) throws IOException {
        // 封装数据源File
        File srcFile = new File("E:\\JavaSE\\day21\\code\\demos");
        // 封装目的地File
        File destFile = new File("E:\\");

        // 复制文件夹的功能
        copyFolder(srcFile, destFile);
    }

    private static void copyFolder(File srcFile, File destFile) throws IOException {
        // 判断该File是文件夹还是文件
        if (srcFile.isDirectory()) {
            // 文件夹
            File newFolder = new File(destFile, srcFile.getName());
            newFolder.mkdir();

            // 获取该File对象下的所有文件或者文件夹File对象
            File[] fileArray = srcFile.listFiles();
            for (File file : fileArray) {
                copyFolder(file, newFolder);
            }
        } else {
            // 文件
            File newFile = new File(destFile, srcFile.getName());
            copyFile(srcFile, newFile);
        }
    }

    private static void copyFile(File srcFile, File newFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile));

        byte[] bys = new byte[1024];
        int len = 0;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }

        bos.close();
        bis.close();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读