I/O也叫做输入/输出,在java编程语言中,I/O更被看作是一种流;JavaI/O的体系设计与Linux内核I/O有着密不可分的关系;
为了操作系统的安全考虑,Linux进程是无法直接操作I/O设备的,必须通过内核来协助完成I/O动作,而内核会为每个I/O设备维护一个缓冲区Buffer,又叫做缓冲区;
数据从I/O设备拷贝到buffer缓冲区(等待数据阶段);
数据再从缓冲区拷贝到Linux进程(拷贝数据阶段);
此过程可分为五种模型:
**阻塞I/O模型(Blocking I/O):**在Linux中默认情况下所有请求都是阻塞的,一个典型的读操作如下图:
**非阻塞I/O(Non-Blocking I/O):**应用会不断询问内核是否准备好了数据,系统不会阻塞用户进程,而是立即返回,从用户进程角度讲,并不需要等待可以立即返回结果;
I/O 复用(I/O Multiplexing):
信号驱动 I/O(Signal Driven I/O):
异步 I/O(Asynchrnous 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 体系。
又可按照工作职能分为:
处理流: 真正直接处理 I/O 数据的类, 包括文件流、 数组流、 字符串流、 管道流;
装饰流: 用来装饰加工节点流的, 比如封装某些功能, 包括缓冲流、 转换流、 数据流、 对象流;
这些标准输入现在正逐渐被NIO取代
**NIO 出现了(有的也称之为 New I/O),NIO 不但新增加了许多全新的类,而且还对原 java.io 包中的很多类进行了改写,NIO 主要包含三个核心部分:Channel、Buffer 和 **
Selector。
**Channel(通道):**NIO 中所有的读写操作都是从 Channel(通道)开始的,顾名思义,Channel 就是一条能够让数据通过的管道(像喝奶茶的吸管那样),它最重要的实可
以分为两大类:用于本地文件的 Channel 和用于网络的 Channel。
**Buffer(缓冲区):**Buffer 是专门用来缓存输入或输出的数据,分为输入缓冲区和输出缓冲区,它可以解决高速设备与低速设备速度的不匹配问题,也可以减少读写次数,
Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过 Buffer。
Selector(选择器):数据总是从 Channel 读取到 Buffer,或者从 Buffer 写入到Channel,单个线程可以监听多个 Channel——Selector 就是这个线程背后的实现机制。
Selector 通过单线程处理多个 Channel,而 Selector(线程)之间需要传递数据的话,需要用到 Pipe,它是用于 Selector 之间数据传递的一种「单向」「管道」。
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 中。
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);
}
}
InputStream(字节输入流):
public void close() :
关闭此输入流并释放与此流相关联的任何系统资源。当完成流的操作时,必须调用此方法,释放系统资源。
public abstract int read() :
从输入流中读取下一个数据字节。值字节返回int
,范围为0
至255
。如果由于到达流末尾而没有可用字节,则返回值-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
,范围为0
至255
。如果由于到达流末尾而没有可用字节,则返回值-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();
}
}
}
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);
}
}
感谢您的阅读,如果本篇文章对您有帮助,欢迎点赞,关注,您的阅读是我莫大的鼓励!