JavaIO流——文件的读取与传输

I/O也叫做输入/输出,在java编程语言中,I/O更被看作是一种流;JavaI/O的体系设计与Linux内核I/O有着密不可分的关系;

为了操作系统的安全考虑,Linux进程是无法直接操作I/O设备的,必须通过内核来协助完成I/O动作,而内核会为每个I/O设备维护一个缓冲区Buffer,又叫做缓冲区;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KV20kEep-1639811866164)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211214203409731.png)]

数据从I/O设备拷贝到buffer缓冲区(等待数据阶段);

数据再从缓冲区拷贝到Linux进程(拷贝数据阶段);

此过程可分为五种模型:

**阻塞I/O模型(Blocking I/O):**在Linux中默认情况下所有请求都是阻塞的,一个典型的读操作如下图:

JavaIO流——文件的读取与传输_第1张图片

**非阻塞I/O(Non-Blocking I/O):**应用会不断询问内核是否准备好了数据,系统不会阻塞用户进程,而是立即返回,从用户进程角度讲,并不需要等待可以立即返回结果;

JavaIO流——文件的读取与传输_第2张图片

I/O 复用(I/O Multiplexing):

信号驱动 I/O(Signal Driven I/O):

异步 I/O(Asynchrnous I/O):

Java标准I/O模型(Blocking I/O)

早期的Java I/O都是基于阻塞I/O模型的,他按照处理的数据类型分为字符流和字节流,按照工作职责分为处理流和装饰流。

字节流: 以 8 位( 即 1byte, 8bit) 作为一个数据单元, 数据流中最小的数据单元是字节;

字符流: 以 16 位( 即 1char, 2byte, 16bit) 作为一个数据单元, 数据流中最小的数据单元是字符, Java 中的字符是 Unicode 编码, 一个字符占用两个字节;

Java依据据字节流定义了了InputStream/OutputStream,依据字符流Java定义了Reader/Writer;Java 再根据不同应用场景或功能, 通过继承这两种抽象基类派生出子类, 用

来满足文件、 网络、 管道等不同场景的 I/O 需求, 从而形成了 Java 的基本 I/O 体系。

JavaIO流——文件的读取与传输_第3张图片

又可按照工作职能分为:

处理流: 真正直接处理 I/O 数据的类, 包括文件流、 数组流、 字符串流、 管道流;

装饰流: 用来装饰加工节点流的, 比如封装某些功能, 包括缓冲流、 转换流、 数据流、 对象流;

JavaIO流——文件的读取与传输_第4张图片

这些标准输入现在正逐渐被NIO取代

Java NIO模型(Non-blocking I/O)

**NIO 出现了(有的也称之为 New I/O),NIO 不但新增加了许多全新的类,而且还对原 java.io 包中的很多类进行了改写,NIO 主要包含三个核心部分:Channel、Buffer 和 **

Selector。

**Channel(通道):**NIO 中所有的读写操作都是从 Channel(通道)开始的,顾名思义,Channel 就是一条能够让数据通过的管道(像喝奶茶的吸管那样),它最重要的实可

以分为两大类:用于本地文件的 Channel 和用于网络的 Channel。

**Buffer(缓冲区):**Buffer 是专门用来缓存输入或输出的数据,分为输入缓冲区和输出缓冲区,它可以解决高速设备与低速设备速度的不匹配问题,也可以减少读写次数,

Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过 Buffer。

JavaIO流——文件的读取与传输_第5张图片

Selector(选择器):数据总是从 Channel 读取到 Buffer,或者从 Buffer 写入到Channel,单个线程可以监听多个 Channel——Selector 就是这个线程背后的实现机制。

Selector 通过单线程处理多个 Channel,而 Selector(线程)之间需要传递数据的话,需要用到 Pipe,它是用于 Selector 之间数据传递的一种「单向」「管道」。

JavaIO流——文件的读取与传输_第6张图片

Scatter/Gather

Java NIO 支持一种称之为支持本地矢量 I/O(native vectored I/O)的技术(或者称为 Scatter/Gather 操作),当应用在一个 Channel 上请求一个 Scatter/Gather 操作时,这

个请求会被翻译为适当的本地调用来直接填充或抽取缓冲区,减少或避免了缓冲区拷贝和系统调用,这也意味着,Scatter/Gather 将使用直接缓冲区以从本地 I/O 获取最大性优

势(直接缓冲区的概念请参考名词解释),具体来说:

Scatter:将从一个 Channel 里读取的信息分散到多个 Buffer 中;

Gather:将多个 Buffer 里面的内容按顺序发送到一个 Channel 中。

JavaIO流——文件的读取与传输_第7张图片

标准I/O如何使用

JavaIO流——文件的读取与传输_第8张图片

File类及相关应用

File类的构造方法:

File(File parent,String child) 从父抽象路径名和子路径名字符串创建新的 File实例。

File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。

File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例。

File(URI uri) 通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。

常用方法

public String getAbsolutePath() : 返回此File的绝对路径名字符串。

public String getPath() : 将此File转换为路径名字符串。

public String getName() : 返回由此File表示的文件或目录的名称。

public long length() : 返回由此File表示的文件的长度。

判断功能的方法

public boolean exists() :File表示的文件或目录是否实际存在。

public boolean isDirectory() :File表示的是否为目录。

public boolean isFile() :File表示的是否为文件。

增删的方法

public boolean createNewFile() : 当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。

public boolean delete() : 删除由此File表示的文件或目录。

public boolean mkdir() : 创建由此File表示的目录。

public boolean mkdirs() : 创建由此File表示的目录,包括任何必需但不存在的父目录。

目录的遍历

public String[] list() : 返回一个String数组,表示该File目录中的所有子文件或目录。

public File[] listFiles() : 返回一个File数组,表示该File目录中的所有的子文件或目录

任务一:如果是 Windows 系统,以递归方式读取 C 盘中所有的目录和文件,并打印出每个文件的大小和每个目录中文件的数量;如果是 Linux/Mac 系统,那么递归方式读取/usr中所有的目录和文件,并打印出每个文件的大小和每个目录中文件的数量。

package T11.windowC;

import java.io.File;
import java.text.DecimalFormat;

import static java.lang.Math.pow;

/**
 * @Author: Administrator
 * Date: 2021/12/14 21:15
 * @Version:
 * @Description:
 */
public class Test {
    public void soutWindowC(File file) {
        File[] files = {};
        if (file.isFile()) {
            System.out.print(file.getName());
            String s = fileSize(file);
            System.out.println("----" + s);
            return;
        } else if (file.isDirectory()) {

            files = file.listFiles();
            if (files == null) {
                return;
            } else {
                for (File file1 : files) {
                    if (file1.isDirectory()) {
                        System.out.println(file1.getName());
                        System.out.print("\t");
                        soutWindowC(file1);
                    }
                    if (file1.isFile()) {
                        System.out.print(file1.getName());
                        String s = fileSize(file1);
                        System.out.println("----" + s);
                        return;
                    }
                }
            }
        }


    }

    String fileSize(File file) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSize = "";
        if (file.length() < 1024) {
            fileSize = df.format(file.length()) + "B";
        }
        else if (file.length() < pow(1024, 2)) {
            fileSize = df.format(file.length()/1024) + "KB";
        }
        else if (file.length() < pow(1024, 3)) {
            fileSize = df.format(file.length()/pow(1024, 2)) + "MB";
        }
        else if (file.length() < pow(1024, 4)) {
            fileSize = df.format(file.length()/pow(1024, 3)) + "GB";
        }
        return fileSize;
    }

    @org.junit.Test
    public void test() {
        File file = new File("D:/");
        soutWindowC(file);
    }

}

JavaIO流——文件的读取与传输_第9张图片

字节操作流

InputStream(字节输入流):

public void close() : 关闭此输入流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用此方法,释放系统资源。

public abstract int read() : 从输入流中读取下一个数据字节。值字节返回int ,范围为0255 。如果由于到达流末尾而没有可用字节,则返回值-1 。此方法将阻塞,直到输入数据可用,检测到流的末尾或抛出异常。子类必须提供此方法的实现.

public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。

OutputStream(字节输出流):

public void close() : 关闭此输出流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用此方法,释放系统资源。

public void flush() : 刷新此输出流并强制任何缓冲的输出字节被写出。

public void write(byte[] b) : 将 b.length字节从指定的字节数组写入此输出流。

public void write(byte[] b, int off, int len) : 从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。

public abstract void write(int b) : 将指定的字节输出流。

public class InputStreamDemo {
    @Test
    public void test() throws IOException {
        FileInputStream fis = new FileInputStream("E://old/battleField.png");
        FileOutputStream fos = new FileOutputStream("E://new/battleField.png");
        byte[] b= new byte[1024];
        int len;
        while((len = fis.read(b))!=-1){
            fos.write(b, 0, len);
        }
    }
}

字符操作流

Reader(字符输入流):

public void close() : 关闭此输入流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用此方法,释放系统资源。

public abstract int read() : 从输入流中读取下一个数据字节。值字节返回int ,范围为0255 。如果由于到达流末尾而没有可用字节,则返回值-1 。此方法将阻塞,直到输入数据可用,检测到流的末尾或抛出异常。子类必须提供此方法的实现.

public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。

Writer(字符输出流):

public void close() : 关闭此输出流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用此方法,释放系统资源。

public void flush() : 刷新此输出流并强制任何缓冲的输出字节被写出。

public void write(byte[] b) : 将 b.length字节从指定的字节数组写入此输出流。

public void write(byte[] b, int off, int len) : 从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。

public abstract void write(int b) : 将指定的字节输出流。

public class ReaderDemo {
    @Test
    public void test() throws IOException {
        FileReader reader = new FileReader("E://old/battleField.png");
        FileWriter writer = new FileWriter("E://new/battleField.png");
        char c[] = new char[1024];
        int len;
        while ((len = reader.read(c)) != -1)
        {
            writer.write(c, 0, len);
            writer.flush();
        }
    }
}

NIO应该如何使用

public class NIODemo {
    @Test
    public void test() throws IOException {
        FileChannel channelIn = new FileInputStream("E://old/battleField.png").getChannel();
        FileChannel channelOut = new FileOutputStream("E://new/battleField.png").getChannel();
        channelIn.transferTo(0, channelIn.size(), channelOut);
    }
}

感谢您的阅读,如果本篇文章对您有帮助,欢迎点赞,关注,您的阅读是我莫大的鼓励!

你可能感兴趣的:(JavaSE,java,IO,NIO,文件读取,输入输出)