1.9IO流

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
    • ObjectInputStream:将从流中反序列化得到对象数据

(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
    • 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:文件目录类

//构造函数
1File(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( );        创建多级目录
11public 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的区别

BIONIO
本地磁盘面向流,单向面向缓冲区,双向
网络同步阻塞,无选择器同步不阻塞(需手动设置),有选择器

阻塞: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();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值