NIO,Buffer

NIO概述

  • 同步与异步(synchronous/asynchronous) : 同步是⼀种可靠的有序运⾏机制,当我们进⾏同步操作时,后续的任务是等待当前调⽤返回,才会进⾏下⼀步;⽽异步则相反,其他任务不需要等待当前调⽤返回,通常依靠事件、回调等机制来实现任务间次序关系
    • 同步 : 调⽤⽅法之后 , 必须要得到⼀个返回值 例如 : 买⽕⻋票, ⼀定要买到票 , 才能继续下⼀步
    • 异步 : 调⽤⽅法之后 , 不需要有返回值 , 但是会有回调函数 , 回调函数指的是满⾜条件之后会⾃动执⾏的⽅法 例如:买⽕⻋票, 不⼀定要买到票 , 我可以交代售票员 , 当有票的话, 你就帮我出张票
  • 阻塞与⾮阻塞:在进⾏ 阻塞 操作时,当前线程会处于阻塞状态,⽆法从事其他任务,只有当条件就绪才能继续,⽐如ServerSocket 新连接建⽴完毕,或者数据读取、写⼊操作完成;⽽⾮阻塞 则是不管 IO 操作是否结束,直接返回,相应操作在后台继续处理
  • 阻塞 : 如果没有达到⽅法的⽬的 , 就会⼀直停在那⾥ ( 等待 ) , 例如 : ServerSocket accept() ⽅法
  • ⾮阻塞 : 不管⽅法有没有达到⽬的 , 都直接往下执⾏ ( 不等待)
  • IO: 同步阻塞
  • NIO: 同步⾮阻塞
  • AIO: 异步⾮阻塞

Buffer类

概述:Buffer是⼀个对象,它是对某种基本类型的数组进⾏了封装

  • Buffer主要有如下几种: 
    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer
  • 创建ByteBuffer 
  • ⽅式⼀:在堆中创建缓冲区:allocate(int capacity)
  • ⽅式⼆ : 在系统内存创建缓冲区: allocatDirect(int capacity)
  • ⽅式三:通过数组创建缓冲区:wrap(byte[] arr)

代码演示: 

⽅式⼀:在堆中创建缓冲区: allocate(int capacity)
public static void main ( String [] args ) {
// 创建堆缓冲区
ByteBuffer byteBuffer = ByteBuffer . allocate ( 10 );
}
⽅式⼆ : 在系统内存创建缓冲区: allocatDirect(int capacity)
public static void main ( String [] args ) {
// 创建直接缓冲区
ByteBuffer byteBuffer = ByteBuffer . allocateDirect ( 10 );
}
  • 在堆中创建缓冲区称为:间接缓冲区
  • 在系统内存创建缓冲区称为:直接缓冲区
  • 间接缓冲区的创建和销毁效率要⾼于直接缓冲区
  • 间接缓冲区的⼯作效率要低于直接缓冲区
⽅式三:通过数组创建缓冲区:wrap(byte[] arr)
public static void main ( String [] args ) {
byte [] byteArray = new byte [ 10 ];
ByteBuffer byteBuffer = ByteBuffer . wrap ( byteArray );
}

 此种⽅式创建的缓冲区为:间接缓冲区

添加数据-put

讲解

  • public ByteBuffer put(byte b):向当前可⽤位置添加数据。
  • public ByteBuffer put(byte[] byteArray) :向当前可⽤位置添加⼀个byte[] 数组
  • public ByteBuffer put(byte[] byteArray,int offset,int len):添加⼀个 byte[] 数组的⼀部分
public class Bufferlei {
    public static void main(String[] args) {
        //Buff主要有如下几种:
        /*ByteBUffer
        CharBuffer
        DoubleBUffer
        FloatBuffer
        IntBuffer
        LongBuffer
        ShortBuffer
         */
        //ByteBuffer堆中缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //添加数据
        byteBuffer.put((byte) 1);
        byteBuffer.put((byte) 2);
        byteBuffer.put((byte) 3);
        //ByteBUffer转换为普通字节数组
        byte [] bytes = byteBuffer.array();
        System.out.println(Arrays.toString(bytes));
        //创建系统内存创建缓冲区
        ByteBuffer b1 = ByteBuffer.allocateDirect(10);
        b1.put((byte) 3);
        b1.put((byte) 2);
        b1.put((byte) 1);
        byte[] b = byteBuffer.array();
        System.out.println(Arrays.toString(b));
        //创建数组缓冲区
        byte[] b2 = {5,6,7};
        ByteBuffer b3 = ByteBuffer.wrap(b2);
        byte []b4 = b3.array();
        System.out.println(Arrays.toString(b4));


    }
}

容量-capacity

讲解

Buffer 的容量 (capacity) 是指: Buffer 所能够包含的元素的最⼤数量。定义了Buffer 后,容量是不可变的。
public class Test02 {
    public static void main(String[] args) {
        /*
        * 容量-capacity
        * Buffer所能包含的元素的最大数量,定义了BUffer后,容量是不可变的
        *
        * */
        ByteBuffer b1 = ByteBuffer.allocate(10);
        System.out.println("容量"+b1.capacity());//10 之后不可改变
        byte [] bytes = {97,98,99,100};
        ByteBuffer b2 = ByteBuffer.wrap(bytes);
        System.out.println("容量:"+b2.capacity());//容量4,之后不可改变



    }
}

限制-limit

讲解:

  • 限制limit是指:第⼀个不应该读取或写⼊元素的index索引。缓冲区的限制(limit)不能为负,并且不能⼤于容量。
  • 有两个相关⽅法:
    • public int limit() :获取此缓冲区的限制。
    • public Buffer limit(int newLimit) :设置此缓冲区的限制
public class Test03 {
    public static void main(String[] args) {
        /*
        * 限制-limit
        *
        * */

        ByteBuffer b1 = ByteBuffer.allocate(10);
        //添加数据
        b1.put((byte) 10);

        //获取限制
        int limit1 = b1.limit();
        System.out.println("limitl:"+limit1);//10

        //设置限制
        b1.limit(3);
        b1.put((byte) 20);
        b1.put((byte) 30);
        //b1.put((byte) 40);
        // b1.put((byte)40);// 报异常,因为限制
        //位置索引为3,所以再存14就会报异常:BufferOverflowException
        System.out.println(b1);


    }
}

位置-position

讲解
  • 位置position是指:当前可写⼊的索引。位置不能⼩于0,并且不能⼤于"限制"
  • 有两个相关⽅法:
    • public int position() :获取当前可写⼊位置索引。
    • public Buffer position(int p) :更改当前可写⼊位置索引。
public class Test04 {
    public static void main(String[] args) {
        //位置-position
        //他是指当前可写入的索引 位置不能小于0;并且不能大于限制
        //int position()获取当前写入位置索引
        //Buffer position(int p ) 更改当前写入位置的索引

        ByteBuffer b1 = ByteBuffer.allocate(10);
        //添加数据
        b1.put((byte) 10);
        //获取当前位置索引
        int position = b1.position();
        System.out.println("position"+position);//1
        //设置当前位置索引
        b1.position(5);
        b1.put((byte) 22);
        b1.put((byte) 33);
        System.out.println("position"+b1.position());//7
        System.out.println(Arrays.toString(b1.array()));


    }
}

标记 -mark
讲解
  • 标记mark是指:当调⽤缓冲区的reset()⽅法时,会将缓冲区的position位置重置为该索引。
  • 相关方法:
    • public Buffer mark() :设置此缓冲区的标记为当前的position位置。
    • public Buffer reset() : 将此缓冲区的位置重置为以前标
public class Test05 {
    public static void main(String[] args) {
        //标记-mark
        //Buffer mark()设置此缓冲区的标记当前的position位置
        //Buffer reset()将此缓冲区的位置重置为以前标记的位置
        ByteBuffer b1 = ByteBuffer.allocate(10);
        //添加元素
        b1.put((byte) 11);
        //获取当前位置索引
        int position = b1.position();
        System.out.println("position:"+position);

        //标记当前位置索引
        b1.mark();
        //添加元素
        b1.put((byte) 22);
        b1.put((byte) 33);
        //获取当前位置索引
        System.out.println("position:"+b1.position());
        System.out.println(Arrays.toString(b1.array()));

        //重置当前位置索引
        b1.reset();
        //获取当前位置索引
        System.out.println("position:"+b1.position());
        //添加元素
        b1.put((byte) 44);
        System.out.println(Arrays.toString(b1.array()));
       


    }
}

其他方法

 //其他方法
        /*
        * int remaining()获取position与limit之间的元素数
        * boolean isReadOnly()获取当前缓冲区是否只读
        * boolean isDirect()获取当前缓冲区是否为直接缓冲区
        * Buffer rewind() 重绕缓冲区
        * 将position位置设置为:0
        * 限制limit不变
        * 丢弃标记
        *
        * Buffer clear()还原缓冲区的状态
        * 将limit设置为当前position位置;
        - 将当前position位置设置为0;
        - 丢弃标记。
        *
        * */

public static void main(String[] args) {
 //创建对象
 ByteBuffer buffer =ByteBuffer.allocate(10);
 //添加元素
 buffer.put((byte)11);
 buffer.put((byte)22);
 buffer.put((byte)33);
 //限制
 buffer.limit(6);
 //容量是10 位置是3 限制是6
 System.out.println("容量是" +buffer.capacity() + " 位置是" +buffer.position() + " 限制是" +buffer.limit());
 
 //还原
 buffer.clear();
 //容量是10 位置是0 限制是10
 System.out.println("容量是" +buffer.capacity() + " 位置是" +buffer.position() + " 限制是" +buffer.limit());
 }






public static void main(String[] args) {
 //创建对象
 ByteBuffer buffer =
ByteBuffer.allocate(10);
 //添加元素
 buffer.put((byte)11);
 buffer.put((byte)22);
 buffer.put((byte)33);
 //限制
 buffer.limit(6);
 //容量是10 位置是3 限制是6
 System.out.println("容量是" +
buffer.capacity() + " 位置是" +
buffer.position() + " 限制是" + buffer.limit());
 //切换
 buffer.flip();
 //容量是10 位置是0 限制是3
 System.out.println("容量是" +
buffer.capacity() + " 位置是" +
buffer.position() + " 限制是" + buffer.limit());
 }
Channel(通道)

Channel的概述

Channel (通道): Channel 是⼀个对象,可以通过它读取和写⼊数据, 可以把它看做是 IO 中的流,不同的是: Channel 是双向的, Channel 对象既可以调⽤读取的⽅法 , 也可以调⽤写出的⽅法 。
Channel 的分类
  • FileChannel:从⽂件读取数据的 输⼊流和输出流
  • DatagramChannel:读写UDP⽹络协议数据 DatagramPackge
  • SocketChannel:读写TCP⽹络协议数据 Socket
  • ServerSocketChannel:可以监听TCP连接 ServerSocket

FileChannel类的基本使用

获取FileChannel类的对象

  • java.nio.channels.FileChannel (抽象类):⽤于读、写⽂件的通道
  • FileChannel 是抽象类,我们可以通过 FileInputStream 和FileOutputStream的 getChannel() ⽅法⽅便的获取⼀个它的⼦类对象
FileInputStream fi = new FileInputStream ( new File ( src ));
FileOutputStream fo = new FileOutputStream ( new File ( dst ));
// 获得传输通道 channel
FileChannel inChannel = fi . getChannel ();
FileChannel outChannel = fo . getChannel ();
使⽤ FileChannel 类完成⽂件的复制
public class Test06 {
    public static void main(String[] args) throws IOException {
        //Channel概述
        /*
        * FileCh annel类的基本使用
        * */
    //使用FileChannel类完成文件的复制
        FileInputStream fis = new FileInputStream("C:\\Users\\25120\\IdeaProjects\\Test\\Hello.txt");
        FileOutputStream fos = new FileOutputStream("C:\\Users\\25120\\IdeaProjects\\Test\\Hello1.txt");
        //获得FileChannel管道对象
        FileChannel c1 = fis.getChannel();
        FileChannel c2 = fis.getChannel();
        //创建ByteBuffer数组
        ByteBuffer b = ByteBuffer.allocate(1000);
        //循环读取数据
        while ((c1.read(b))!=-1){
            //重置postion为0,limit为postion的位置
            b.flip();
            //写出数据
            c2.write(b);
            //还原
            b.clear();
        }
        //释放资源
        c2.close();
        c1.close();
        fos.close();
        fis.close();




    }
}
FileChannel 结合 MappedByteBuffer 实现⾼效读
MappedByteBuffer 类的概述
  • 上例直接使⽤ FileChannel 结合 ByteBuffer 实现的管道读写,但并不能提⾼⽂件的读写效率。
  • ByteBuffer 有个抽象⼦类: MappedByteBuffer ,它可以将⽂件直接映射⾄内存,把硬盘中的读写变成内存中的读 , 所以可以提⾼⼤⽂件的读写效率。
  • 可以调⽤FileChannelmap()⽅法获取⼀个MappedByteBuffer,map()⽅法的原型MappedByteBuffer map(MapMode mode, longposition, long size);
说明:将节点中从 position 开始的 size 个字节映射到返回 MappedByteBuffer 中。

ServerSocketChannelSocketChannel创建连接

1.客户端:SocketChannel类⽤于连接的客户端,它相当于:Socket

SocketChannel socket = SocketChannel.open()

2.调⽤SocketChannel的实例⽅法 connect(SocketAddress add)连接服务器:
socket . connect ( new InetSocketAddress ( "localhost" , 8888 ));

客户端代码:

public class Test07 {
    public static void main(String[] args) throws IOException {
        //ServerSocketChannel和SocketChannel创建连接
        //1.先调用SocketChannel的open()方法打开通道
        //SocketChannel soket = SocketChannel.open()
        //2.调用SocketChannel的实例方法connect(SocketAddress add)连接服务器
        //socket.connect(new InetSocketAddress("localhost",8888))
        //示例:客户端连接服务器
        SocketChannel socket = SocketChannel.open();
        socket.connect(new InetSocketAddress("127.0.0.1",8888));


    }
}

服务器端:ServerSocketChannel类⽤于连接的服务器端,它相当于:ServerSocket

调⽤ ServerSocketChannel 的静态⽅法 open() 就可以获得ServerSocketChannel对象 , 但并没有指定端⼝号 , 必须通过其套接字的bind ⽅法将其绑定到特定地址,才能接受连接。
ServerSocketChannel serverChannel = ServerSocketChannel . open ()
调⽤ ServerSocketChannel 的实例⽅法bind(SocketAddress add):绑定本机监听端⼝,准备接受连接。
构造⽅法: InetSocketAddress(int port) :指定本机监听 端⼝
serverChannel . bind ( new InetSocketAddress ( 8888 ));
调⽤ ServerSocketChannel 的实例⽅法 accept() :等待连接。
SocketChannel accept = serverChannel . accept ();
System . out . println ( " 后续代码 ..." );

服务器代码(默认-阻塞默认):

我们可以通过ServerSocketChannelconfigureBlocking(boolean b)⽅法设置accept()是否阻

public class Test08 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //ServerSocketChanne创建连接
        //服务器端:ServerSocketChannel类用于连接的服务器

        //绑定本机监听端口,准备接受连接
        ServerSocketChannel socketChan1 = ServerSocketChannel.open();
        //socketChan1.bind(new InetSocketAddress(8888));
        //调用ServerSocketChannel的实例方法accept();等待链接
        //System.out.println("服务器等待客户端连接");

        //SocketChannel accept = socketChan1.accept();
        //System.out.println("后续代码.....");

        socketChan1.bind(new InetSocketAddress(8888));
        //设置非阻塞连接
        socketChan1.configureBlocking(false);//写成false叫非阻塞,写成true叫阻塞
        while (true) {
            //获取客户端连接
            SocketChannel sc = socketChan1.accept();
            if (sc != null) {
                //不等于null说明连接上了客户端
                System.out.println("连接上了,,,");
                break;
            }//读取数据操作

            else {
                //没连接上客户端
                System.out.println("打会游戏");
                Thread.sleep(2000);
            }

        }

    }
}

NIO网络编程收发信息

使用ServerSocketChannel代替之前的ServerSocket,来完成网络编程的收发数据

书写服务器代码
public class Test09 {
    public static void main(String[] args) throws IOException {
        //NIO网络编程收发信息
        //书写服务器代码

        //创建对象
        ServerSocketChannel ssc  =ServerSocketChannel.open();
        //服务器绑定端口
        ssc.bind(new InetSocketAddress(6666));
        //连接上客户端
        SocketChannel sc = ssc.accept();
        //服务器端接受数据
        //创建数组
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //接收数据
        int len = buffer.remaining();
        //打印结构
        System.out.println(new String(buffer.array(),0,len));
        //关闭资源
        sc.close();

    }
}

书写客户端代码:

public class Test10 {
    public static void main(String[] args) throws IOException {
        //书写客户端
        //创建
        SocketChannel sc = SocketChannel.open();
        //连接服务器
        sc.connect(new InetSocketAddress("127.0.0.1",6666));

        //客户端发数据
        //创建数组
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //数组中添加数据
        buffer.put("你好呀数据库".getBytes());
        //切换
        buffer.flip();
        //发出数据
        sc.write(buffer);
        //关流
        sc.close();


    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值