1.9IO流
1.概念
Java程序需要和外部进行数据交互时,需要用到IO流。这个外部可以是很多介质
- 磁盘
- 网络
- 数据库
- 硬件
2.架构图
(1)InputStream
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y0zoxema-1686273663845)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220121165608323.png)]
- InputStream:接口,以下为其实现类
- ByteArrayInputStream:读取数据(字节/字节数组)到内部缓冲区。内部缓冲区(默认大小为
32
字节)+动态增长。与BufferedInputStream的区别为构造函数传参不同,BufferedInputStream为装饰流传的是流。而ByteArrayInputStream传的是字节数组,最多一次性读32字节到内部缓冲区。 - FileInputStream:对磁盘文件的字节流读取,
如果文件不存在,则会报错
- SocketInputStream:对Socket的字节流读取。(Socket中getInputStream()得到的是其实现类)
- FilterInputStream:装饰流
- BufferedInputStream:缓冲字节输入流。创建实例时会自动的一次性读取
8KB
大小的字节到内部缓冲区(缓存)。当执行read操作时再将缓冲区数据读取到内存中。这样做能够减少和介质之间的交互,减低IO次数 - DataInputStream:允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型,即允许应用程序以与机器无关方式从底层输入流中读取Java 基本数据类型(不需要通过介质)
- InflaterInputStream
- ZipInputStream:解压zip
- BufferedInputStream:缓冲字节输入流。创建实例时会自动的一次性读取
- ObjectInputStream:将从流中反序列化得到对象数据
- ByteArrayInputStream:读取数据(字节/字节数组)到内部缓冲区。内部缓冲区(默认大小为
(2)OutputStream
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sjgHdhUI-1686273663846)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220121164858960.png)]
- OutputStream:接口
- ByteArrayOutputStream:写数据(字节/字节数组)到内部缓冲区。内部缓冲区(默认大小为32字节)+动态增长
- FileOutputStream:对磁盘文件的字节流写操作,
如果文件不存在,则会去自动创建
- SocketOutputStream:对Socket的字节流写入(Socket中getOutputStream()得到的是其实现类)
- FilterOutputStream:装饰流
- BufferedOutputStream:缓冲字节输出流。write()时写数据到缓冲区(默认大小为8K),
flush()
时将数据从缓冲区导出。 - DataOutputStream:允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型,即允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型(不需要通过介质)
- DeflaterInputStream
- ZipOutputStream:压缩zip
- BufferedOutputStream:缓冲字节输出流。write()时写数据到缓冲区(默认大小为8K),
- ObjectOutputStream:将对象数据序列化到流
(3)Reader
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySC5uSUx-1686273663847)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220121165813163.png)]
- Reader
- CharArrayReader:对标ByteArrayInputStream,字节变字符
- InputStreamReader:转换类,将字节流转换为字符流
- BufferedReader:对标BufferedInputStream,字节变字符
(4)Writer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T2SIaNbX-1686273663847)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220121165757045.png)]
- Writer
- CharArrayWriter:对标ByteArrayOutputStream,字节变字符
- OutputStreamWriter:转换类,将字节流转换为字符流
- BufferedWriter:对标BufferedOutputStream,字节变字符
(5)File:文件目录类
//构造函数
1)File(String pathName) pathName:文件/目录路径
//相关api
1) public boolean exists( ) 判断文件或目录是否存在
2) public boolean isFile( ) 判断是文件还是目录
3) public boolean isDirectory( ) 判断是文件还是目录
4) public String getName( ) 返回文件名或目录名
5) public String getPath( ) 返回文件或目录的路径。
6) public long length( ) 获取文件的长度
7) public String[] list( ) 将目录中所有文件名保存在字符串数组中返回
8) public boolean renameTo( File newFile ); 重命名文件
9) public void delete( ); 删除文件
10) public boolean mkdirs( ); 创建多级目录
11)public boolean createNewFile(); 创建文件
①创建文件或目录的代码框架
/**
* @author zhangshiqin
* @date 2022/1/25 - 13:56
* @description:
*/
public class FileTest {
public static void main(String[] args) {
FileTest fileTest = new FileTest();
String fileName = FILE_PATH+SLASH+"test.txt";
fileTest.createFile(fileName);
}
public static final String FILE_PATH = "/temp/dir";
public static final String SLASH = "/";
//在服务器上创建一个目录和文件
public void createFile(String fileName){
//0.准备一个目录
File dir = new File(FILE_PATH);
//1.准备一个文件
File file = new File(fileName);
//1.判断目录是否已经创建
if (!dir.exists()) {
System.out.println("目录:"+FILE_PATH+"不存在,准备新建...");
//2.开始新建目录
if (dir.mkdirs()) {
System.out.println("目录:"+FILE_PATH+"新建成功...");
this.createFile(file);
}
}else{
this.createFile(file);
}
}
private void createFile(File file){
//3.判断文件是否存在
if (!file.exists()) {
System.out.println("文件:"+file.getName()+"不存在,准备新建...");
//4.开始创建文件
try {
if (file.createNewFile()) {
System.out.println("文件:"+file.getName()+"新建成功...");
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
}
3.IO模型
(1)IO和NIO的区别
BIO | NIO | |
---|---|---|
本地磁盘 | 面向流,单向 | 面向缓冲区,双向 |
网络 | 同步阻塞,无选择器 | 同步不阻塞(需手动设置),有选择器 |
阻塞:read时读不到数据,阻塞。read时write没关闭,一直等待阻塞。
(2)BIO模型:一个线程对应一个客户端,如果不是一直在读写造成资源浪费
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5zgy6TSM-1686273663847)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220207093040252.png)]
(3)NIO模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aaGcf8VQ-1686273663848)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220207094212790.png)]
- 一个线程一个选择器,一个选择器多个通道 ,一个通道(相当于流)一个缓冲区(可以是数组)
- 选择器根据事件来决定切换到哪一个通道
- 通道是双向读写的
4.NIO
(1)Buffer
①分类:七种基本类型(除Boolean)Buffer,底层为数组(ByteBuffer -> byte[] buffer)
②四大核心属性:
索引 | 说明 |
---|---|
capacity | 缓冲区数组的总长度 |
position | 下一个要操作的数据元素的位置,默认为0 |
limit | 缓冲区数组中不可操作的元素的位置:limit<=capacity |
mark | 用于记录当前position的前一个位置,默认是-1 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rzKBHJDZ-1686273663848)(C:\Users\10059\AppData\Roaming\Typora\typora-user-images\image-20220207112814858.png)]
③使用:
创建Buffer:
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//分配内存在JVM的堆中
ByteBuffer byteBufferDirect = ByteBuffer.allocateDirect(1024);//分配内存在操作系统内存,效率高耗时高
通道/客户端向Buffer写数据:
* Channel:fileChannel.read(buffer)
* Client:buffer.put()
通道/客户端向Buffer读数据:
* Channel:fileChannel.write(buffer)
* Client:buffer.get()
读写转换:
buffer.flip();
//底层原理:
{
limit = position;
position = 0;
mark = -1;
}
判断是否还有可操作数据:
while(buffer.hasRemaining()){
//todo
}
刷新缓冲区:Buffer类的刷新缓冲区不会清空缓冲区(也就是自身)的数据
buffer.clear();
buffer.compact();
//底层原理:
//clear
{
limit = capacity
position = 0
mark = -1
}
//compact:拷贝剩余数据到起始位置,并将position设置为最后一个剩余元素的下一个位置
{
limit = capacity
position = 最后一个剩余元素的下一个位置
mark = -1
}
其它方法:
buffer.array();//返回底层数组
buffer.asReadOnlyBuffer();//将Buffer设置成只读Buffer
buffer.isReadOnly();//判断Buffer是否为只读Buffer
(2)Channel
①分类:
- 本地磁盘:FileChannel
- 获取此对象的方式:1.RandomAccessFile 2.FileInputStream.getChannel()/FileOutputStream
- 网络:SocketChannel,ServerSocketChannel
②使用:
向Buffer写数据:
可以理解成,从服务端接收到的数据,读到buffer里面。相对于buffer来说,被输入了数据,也就是通道向buffer写数据
int read(ByteBuffer dist)
long read(ByteBuffer[] dsts, int offset, int length)
long read(ByteBuffer[] dsts)
从Buffer读数据:
int write(ByteBuffer src)
long write(ByteBuffer[] srcs, int offset, int length)
long write(ByteBuffer[] srcs)
当前Channel复制数据到目标Channel/从目标Channel复制数据到当前Channel:
long transferTo(long position, long count,WritableByteChannel target)
long transferFrom(ReadableByteChannel src,long position, long count)
ServerSocketChannel设置非阻塞模式:
ssc.configureBlocking(false);
ServerSocketChannel注册到选择器:
ssc.register(Selector s,SelectionKey key,@Nullable Buffer buffer)
key是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:
1. SelectionKey.Connect
2. SelectionKey.Accept
3. SelectionKey.Read
4. SelectionKey.Write
(3)Selector
①Selector的API
创建selector对象:
Selector.open()
监听所有已注册的Channel:
s.select();//阻塞
s.select(long ms);//阻塞,超时
s.selectNow();//不阻塞立即返回
返回监听到的所有已就绪事件
s.selectedKeys();
返回所有注册事件(就绪+未就绪)
s.keys();
②SelectionKey的API
SelectionKey为Selector对当前注册上的Channel所关心的事件
返回与之关联的Selector、Channel、Buffer
Set<SelectionKey> readyKeys = s.selectedKeys();
Set<SelectionKey> allKeys = s.keys();
key.selector();
key.channel();
key.attachment();
检测channel中什么事件或操作已经就绪
key.isAcceptable();
key.isConnectable();
key.isReadable();
key.isWritable();
lectionKey为Selector对当前注册上的Channel所关心的事件
返回与之关联的Selector、Channel、Buffer
Set<SelectionKey> readyKeys = s.selectedKeys();
Set<SelectionKey> allKeys = s.keys();
key.selector();
key.channel();
key.attachment();
检测channel中什么事件或操作已经就绪
key.isAcceptable();
key.isConnectable();
key.isReadable();
key.isWritable();