美文网首页
3/26day19_JUnit单元测试_NIO

3/26day19_JUnit单元测试_NIO

作者: 蹦蹦跶跶的起床啊 | 来源:发表于2020-03-27 20:47 被阅读0次

day19_JUnit单元测试_NIO

复习

1.网络编程三要素
    协议(TCP),IP地址,端口号
2.Socket类
   构造方法:
        public Socket(服务器的IP地址,服务器的端口号);
   成员方法:
        public void close();
        public OutputStream getOutputStream();//从客户端 到 服务器的流
        public InputStream getInputStream();//从服务器 到 客户端流
        
        public void shutDownOutput(); // 关闭输出流
        public void shutDownInput(); // 关闭输入流
3.ServerSocket类    
        public ServerSocket(服务器的端口号);
        public Socket accept(); //接收连接到服务器的客户端对象

4.重点案例【TCP的双向通信】

今日内容


Junit单元测试[重点]

单元测试概念

  • 什么叫单元测试
    • 单元, 在JAVA中一个单元可以指某个方法, 或者某个类
    • 测试, 写一段代码对象单元进行测试
  • Junit 是专门对单元测试提供一个第三方框架
    • 作用: 让一个普通方法独立运行(替换了main方法)

单元测试的使用

  • Junit单元测试框架的使用步骤

    • 编写业务类,在业务类中编写业务方法。比如增删改查的方法
    • 编写测试类,在测试类中编写测试方法,在测试方法中编写测试代码来测试。
    • 测试类的命名规范:以Test开头,以业务类类名结尾,使用驼峰命名法
      • 每一个单词首字母大写,称为大驼峰命名法,比如类名,接口名...
      • 从第二单词开始首字母大写,称为小驼峰命名法,比如方法命名
      • 比如业务类类名:ProductDao,那么测试类类名就应该叫:TestProductDao
    • 测试方法的命名规则:以test开头,以业务方法名结尾
      • 比如业务方法名为:save,那么测试方法名就应该叫:testSave
  • 运行测试

    • 选中方法名 --> 右键 --> Run '测试方法名' 运行选中的测试方法

    • 选中测试类类名 --> 右键 --> Run '测试类类名' 运行测试类中所有测试方法

    • 选中模块名 --> 右键 --> Run 'All Tests' 运行模块中的所有测试类的所有测试方法

  • 测试方法注意事项

    • 必须是public修饰的,没有返回值,没有参数

    • 必须使用JUnit的注解@Test修饰

Junit常用注解

  • Junit4.x

    • @Before:用来修饰方法,该方法会在每一个测试方法执行之前执行一次。(在@Test方法之前)
    • @After:用来修饰方法,该方法会在每一个测试方法执行之后执行一次。(在@Test方法之后)
    • @BeforeClass:只能用来静态修饰方法,该方法会在所有测试方法之前执行一次,而且只执行一次。
    • @AfterClass:只能用来静态修饰方法,该方法会在所有测试方法之后执行一次,而且只执行一次。
  • Junit5.x(方法作用通4.x, 只是更换了名字)

    • BeforeEach
      • AfterEach
      • BeforeAll
        • AfterAll

NIO 介绍

阻塞与非阻塞

  • 阻塞: 在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如Socket新连接建立完毕,或者数据读取、写入操作完成;

  • 非阻塞: 是不管IO操作是否结束,直接返回,相应操作在后台继续处理

同步与异步

可参考
https://www.cnblogs.com/IT-CPC/p/10898871.html

  • 同步:
    • 同步可能是阻塞的, 也可能是非阻塞的
    • 同步阻塞: 是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步;
    • 同步非阻塞:完成某个任务时, 不需要等待任务结束, 当前线程立即可以向下执行.后期需要我们自己写其他代码获取结果
  • 异步:
    • 异步一般来说都是非阻塞的
    • 异步非阻塞: 完成某个任务时, 不需要等待任务结束, 当前线程立即可以继续向下执行. 后期不需要自己写其他代码来获取结果, 任务完成之后自动会通过其他机制把结果返回.
public class TestDogDemo {
    @BeforeClass
    public void aa() {
        System.out.println("Before的aaa");
    }
    @AfterClass
    public void bb() {
        System.out.println("After的bbb");
    }
/*    @Before
    public void aa() {
        System.out.println("Before的aaa");
    }
    @After
    public void bb() {
        System.out.println("After的bbb");
    }*/

    @org.junit.Test
    public void test01() {
        Dog dd = new Dog();
        System.out.println("第一个"+dd.getSum(1, 2, 3));
    }
    @org.junit.Test
    public void test02() {
        Dog dd = new Dog();
        System.out.println("第二个"+dd.getSum(1, 2, 3));
    }

}

BIO, NIO, AIO的介绍

  • BIO(传统的IO): 同步阻塞的IO

  • NIO(New新的IO): 同步阻塞的, 也可以是同步非阻塞.

    NIO之所以是同步,是因为它的accept/read/write方法的内核I/O操作都会阻塞当前线程

  • NIO2(也叫AIO)(Asynchronous IO): 异步非阻塞的IO.可以简单理解为,应用操作直接返回,而不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后续工作。

NIO的三个主要组成部分:Buffer(缓冲区)、Channel(通道)、Selector(选择器)


NIO-Buffer类(缓冲区)

Buffer的介绍和种类

  • Buffer概念

    Buffer称为缓冲区, 本质是一个数组.

    Buffer是一个对象,它对某种基本类型的数组进行了封装。NIO开始使用的Channel(通道)就是通过 Buffer 来读写数据的。
    在NIO中,所有的数据都是用Buffer处理的,它是NIO读写数据的中转池。Buffer实质上是一个数组,通常是一个字节数据,但也可以是其他类型的数组。但一个缓冲区不仅仅是一个数组,重要的是它提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。

  • Buffer的使用步骤

    1. 写入缓冲区(把数据保存到数组中)
    2. 调用flip() 方法. 用于切换缓冲区的读写模式
    3. 读缓冲区(从数组中把数据读取出来)
    4. 调用claer()或者compact()方法(清空缓冲区/清除已经读过的数据)

    步骤解释:当向 Buffer 写入数据时,Buffer 会记录下写了多少数据。一旦要读取数据,需要通过 flip() 方法将 Buffer 从写模式切换到读模式。在读模式下,可以读取之前写入到 Buffer 的所有数据。
    一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用 clear() 或
    compact() 方法。clear() 方法会清空整个缓冲区。compact() 方法只会清除已经读过的数据。任何未读的数据都被
    移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

  • Buffer 的种类

    • ByteBuffer 字节缓冲区(字节数组)[最常用]
    • CharBuffer 字符缓冲区(字符数组)
    • DoubleBuffer Double缓冲区(小数数组)
    • FloatBuffer Float缓冲区(小数数组)
    • mIntBuffer 整型缓冲区(整型数组)
    • LongBuffer 长整形(长整形数组)
    • Sho rtBuffer 短整型(短整型数组)

ByteBuffer的三种创建方式(最好配合Channel使用)

  • 方式一 public static allocate(int capaticy); 在堆内存区申请一个固定字节大小的ByteBuffer缓冲区(分配速度快, 读写速度慢)

    public static void main(String[] args) {
            //创建堆缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(10);
    }
    
  • 方式二 public static allocatDirect(int capaticy); 在系统内存(直接内存)中申请一个固定字节大小的ByteBuffer缓冲区(分配速度慢, 读写速度快)

    public static void main(String[] args) {
            //创建直接缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10);
    }
    

    在堆中创建缓冲区称为:间接缓冲区

    在系统内存创建缓冲区称为:直接缓冲区

    间接缓冲区的创建和销毁效率要高于直接缓冲区

    间接缓冲区的工作效率要低于直接缓冲区

  • 方式三 public static wrap(byte[] arr); 把一个字节数组直接包装成此种方式创建的缓冲区为:间接缓冲区

     public static void main(String[] args) {
            byte[] byteArray = new byte[10];
            ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
     }
    

ByteBuffer的三种添加数据方式

  • public ByteBuffer put(byte b); 添加单个字节
  • public ByteBuffer put(byte[] byteArray); 添加字节数组
  • public ByteBuffer put(byte[] byteArray,int offset,int len); 添加一个byte[]数组的一部分
public static void main(String[] args) {
    ByteBuffer buffer = ByteBuffer.allocate(10);
    System.out.println(Arrays.toString(buffer.array()));
    buffer.put((byte) 12);
    System.out.println(Arrays.toString(buffer.array()));
    byte[] bs = {20, 50, 22};
    buffer.put(bs);
    System.out.println(Arrays.toString(buffer.array()));
    byte[] bs1 = {88, 33, 11};
    buffer.put(bs1, 0, 2);
    System.out.println(Arrays.toString(buffer.array()));
}

ByteBuffer的容量- capacity

  • Buffer的容量(capacity)是指:Buffer所能够包含的元素的最大数量。定义了Buffer后,容量是不可变的。

  • public int capacity(); 获取Buffer的容量

ByteBuffer的限制-limit

  • 限制limit是指:第一个不应该读取或写入元素的index索引。缓冲区的限制(limit)不能为负,并且不能大于容量。(返回第一个不能操作的数据的索引)

  • 可以传入参数(参数范围为底层数组长度0~capacity), 指定从哪个索引处开始不能再进行操作

  • 有两个相关方法

    public int limit():获取此缓冲区的限制。
    public Buffer limit(int newLimit):设置此缓冲区的限制。

ByteBuffer的位置-position

  • 位置position是指:当前可写入的索引。位置不能小于0,并且不能大于"限制"。(最小为0 ,最大为capacity/limit)

  • 两个方法:

    public int position():获取当前可写入位置索引。
    public Buffer position(int p):更改当前可写入位置索引。

ByteBuffer的标记-mark

  • 标记mark是指:当调用缓冲区的reset()方法时,会将缓冲区的position位置重置为该mark处索引。不能为0,不能大于position。

ByteBuffer的其他方法

  • public int remaining():获取position与limit之间的元素数。
  • public boolean isReadOnly():获取当前缓冲区是否只读。
  • public boolean isDirect():获取当前缓冲区是否为直接缓冲区。
  • public Buffer clear():还原缓冲区的状态。
    • 将position设置为:0
    • 将限制limit设置为容量capacity;
    • 丢弃标记mark。
  • public Buffer flip():缩小limit的范围。
    • 将limit设置为当前position位置;
    • 将当前position位置设置为0;
    • 丢弃标记。
  • public Buffer rewind():重绕此缓冲区。
    • 将position位置设置为:0
    • 限制limit不变。
    • 丢弃标记。

Channel(通道)(相当于取代了输入/输出流)

Channel的概述

  • Channel(通道):Channel是一个对象类,可以通过它读取和写入数据。可以把它看做是IO中的流类似. IO流油Input和Output之分, 通道没有输入输出之分,只有一种就是Channel通道

Channel的分类

  • FileChannel 文件通道, 读写文件的
  • DatagramChannel UDP协议通道(通过UDP协议收发数据)
  • SocketChannel TCP协议中客户端的通道(给客户端读写数据用)
  • ServerSocketChannel TCP协议中服务器端通道(给服务器端读写数据用)

FileChannel类的基本使用

  • java.nio.channels.FileChannel (抽象类):用于读、写文件的通道。
  • FileChannel是抽象类,我们可以通过FileInputStream和FileOutputStream的getChannel()方法方便的获取
    一个它的子类对象。
public static void main(String[] args) throws Exception{
    //复制文件
    //创建文件对象
    File file = new File("1.png");
    File newFile = new File("new1.png");
    //创建文件的输入输出流
    FileInputStream fis = new FileInputStream(file);
    FileOutputStream fos = new FileOutputStream(newFile);

    FileChannel inchannel = fis.getChannel();
    FileChannel outchannel = fos.getChannel();

    ByteBuffer buffer = ByteBuffer.allocate(1024);
    int len = 0;
    while ((len = inchannel.read(buffer)) != -1) {
        //切换模式
        buffer.flip();
        //读取缓冲区数据,写入到文件中
        outchannel.write(buffer);
        //清空
        buffer.clear();
    }
    outchannel.close();
    inchannel.close();
    fos.close();
    fis.close();
}

FileChannel结合MappedByteBuffer实现高效读写

  • MappedByteBuffer继承自ByteBuffer

  • MappedByteBuffer 好处, 对大文件读写效率高(内部采用的就是直接内存(系统内存)

  • 获取MappedByteBuffer采用 FileChannel提供的map()方法

  • FileChannel提供了map()方法把文件映射到虚拟内存,通常情况可以映射整个文件,如果文件比较大,可以进行分段映射。

    • map()方法中的参数详解

      FileChannel中的几个变量

      • MapMode mode:内存映像文件访问的方式,共三种:
        1. MapMode.READ_ONLY:只读,试图修改得到的缓冲区将导致抛出异常。
        2. MapMode.READ_WRITE:读/写,对得到的缓冲区的更改最终将写入文件;但该更改对映射到同一文件的其他程序不一定是可见的。
        3. MapMode.PRIVATE:私用,可读可写,但是修改的内容不会写入文件,只是buffer自身的改变,这种能力称之为”copy on write”。
      • position:文件映射时的起始位置。
      • allocationGranularity:Memory allocation size for mapping buffers,通过native函数initIDs初始化。
  • 注意:

    • FileInputStream从头读, 一直读到尾巴

    • RandomAccessFile, 从中间读, 也就是根据指定的下标进行读取, 既可以读, 也可以写

  • 代码实现

    public static void main(String[] args) throws Exception{
        //创建文件, 只读模式, 和读写模式
        RandomAccessFile srcFile = new RandomAccessFile("1.txt", "r");
        RandomAccessFile destFile = new RandomAccessFile("new1.txt", "rw");
        //获取通道
        FileChannel inChannel = srcFile.getChannel();
        FileChannel outChannel = destFile.getChannel();
        int size = (int)inChannel.size();
    
        MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
    
        for (int i = 0; i < size; i++) {
            byte b = inMap.get(i);
            outMap.put(i,b);
        }
    
        outChannel.close();
        inChannel.close();
    }
    

ServerSocketChannel和SocketChannel创建连接

  • ServerSocketChannel 提供两种模式, 阻塞和非阻塞.

  • ServerSocketChannel 主要是接受连接使用, 读写操作更多的是使用SocketChannel

  • 注意 socketChanne.read(buffer). 这句话相当于是 从channel读, 向创建好的buffer中写

  • ServerSocketChannel的创建方式

    • 方式一, 创建同步阻塞

      //创建阻塞的
      public static void main(String[] args) throws IOException {
          //创建ServerSocketChannel
          ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
          //绑定本地端口
          serverSocketChannel.bind(new InetSocketAddress(888));
          System.out.println("服务器启动了");
          //接收客户端通道
          SocketChannel socketChannel = serverSocketChannel.accept();
      }
      
  • 方式二, 创建同步非阻塞

    public static void main(String[] args) throws IOException, InterruptedException {
        //创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //设置为同步非阻塞服务器通道
        serverSocketChannel.configureBlocking(false);
        //绑定本地端口
        serverSocketChannel.bind(new InetSocketAddress(888));
        System.out.println("服务器启动了");
        while (true) {
            //接收客户端通道
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel != null) {
                System.out.println("连接上了客户端");
            } else {
                System.out.println("无客户端连接");
                Thread.sleep(2000);
            }
        }
    }
    
  • SocketChannel的创建方式

    • 方式一 创建阻塞式客户端

      public static void main(String[] args) throws IOException {
          //创建对象
          SocketChannel socketChannel = SocketChannel.open();
          //连接服务器IP和端口号
          socketChannel.connect(new InetSocketAddress("localhost", 888));
          System.out.println("接下来写后续代码");
      }
      
  • 方式二 创建非阻塞式客户端

    public static void main(String[] args) throws IOException, InterruptedException {
        //创建对象
        SocketChannel socketChannel = SocketChannel.open();
        while (true) {
            try {
                //连接服务器IP和端口号
                boolean b = socketChannel.connect(new InetSocketAddress("localhost", 888));
                if (b) {
                    System.out.println("可以和服务器进行交互");
                }
            } catch (Exception e) {
                System.out.println("两秒后再次尝试和服务器连接");
                Thread.sleep(2000);
            }
        }
    }
    

ServerSocketChannel和SocketChannel收发信息

public class SocketChannelDemo {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        if (socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999))) {
            //发送数据
            System.out.println("连接服务器成功");
            ByteBuffer bb = ByteBuffer.wrap("哈哈哈, 我是客户端第一次连接哦".getBytes());
            socketChannel.write(bb);
            //读取数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int len = socketChannel.read(buffer);
            buffer.flip();
            System.out.println(new String(buffer.array(), 0, len));
            //释放资源
            socketChannel.close();
        }
    }
}

public class ServerSocketChannelDemo {
    public static void main(String[] args) throws IOException {
        //创建对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9999));
        //接受客户端
        SocketChannel socketChannel = serverSocketChannel.accept();
        System.out.println("客户端连接上了");
        //读取数据
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        int len = socketChannel.read(byteBuffer);
        //打印数据
        byteBuffer.flip();
        String str = new String(byteBuffer.array(), 0, len);
        System.out.println("客户端写的为"+str);
        //回数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("我是服务器哦".getBytes());
        socketChannel.write(buffer.flip());
        //释放资源
        socketChannel.close();
    }
}

今日总结

能够使用Junit进行单元测试【重点】
    a.Junit是第三方的单元测试框架
    b.@Test 将一个普通方法可以独立运行
    c.@Before @After @BeforeClass @AfterClass
    
能够说出阻塞和非阻塞的概念
    阻塞: 任务没有执行完毕,线程无法向下继续执行
    非阻塞: 无论任务是否执行完毕,线程继续向下执行    
能够说出同步和异步的概念
    同步,可以是阻塞的,可以非阻塞
        同步阻塞的,任务没有执行完毕,线程无法向下继续执行
        同步非阻塞,无论任务是否执行完毕,线程继续向下执行,后期我们需要自己去写代码来获取任务的结果
    异步,一般都是非阻塞的
        异步非阻塞,无论任务是否执行完毕,线程继续向下执行.后期任务执行完毕会想办法通知我们
能够创建和使用ByteBuffer
   创建:
        allocate(字节数); //JVM的堆中申请的空间,间接
        allocatDirect(字节数); // 系统的内存中申请的空间,直接
        wrap(字节数组); // JVM的堆中申请的空间,间接
    使用:
        put(字节/字节数组/字节数组的一部分);
        capacity,limit,position,mark,reset,rewind,flip,clear
        
能够使用MappedByteBuffer实现高效读写
     使用文件对象 RandomAccessFile 不能使用普通的File
            
能够使用ServerSocketChannel和SocketChannel实现连接并收发信息
     ServerSocketChannel: 服务器端,可以是同步阻塞的也可以是同步非阻塞的
     SocketChannel: 客户端,可以是同步阻塞的也可以是同步非阻塞的
     通过configureBlocking 方式可以设置阻塞还是非阻塞    

具体的MappedByteBuffer可以看:https://blog.csdn.net/qq_41969879/article/details/81629469

相关文章

  • 3/26day19_JUnit单元测试_NIO

    day19_JUnit单元测试_NIO 复习 今日内容 Junit单元测试[重点] 单元测试概念 什么叫单元测试单...

  • java之NIO基础

    本文大纲如下: 1、什么是NIO 2、为什么使用NIO 3、NIO的基本使用 4、BIO、NIO、AIO区别以及总...

  • OIO、NIO复制文件的比较

    1.OIO(BIO)复制文件代码 2.NIO复制文件代码一 3.NIO复制文件代码二 3.NIO复制文件代码三 4...

  • Java NIO

    1、IO和NIO的区别? 1)IO面向流、NIO面向缓冲;2)IO是阻塞IO、NIO是非阻塞IO;3)无 与 选择...

  • Android单元测试

    本文主要内容 1、单元测试介绍 2、java单元测试 3、android单元测试 4、常用方法介绍 1、单元测试介...

  • 网络编程之NIO聊天室

    1.创建Nio服务端 2、创建NIO客户端 3、NIO客户端线程处理类 4、创建NioClient多个客户端实现聊天

  • 9.第一个数据库迁移

    1.单元测试用例添加类ItemModelTest 运行单元测试报错: 3.运行单元测试python manage....

  • Java之NIO(非阻塞IO)

    【1】NIO的与IO的区别: 总的来说java 中的IO 和NIO的区别主要有3点:1)IO是面向流的,NIO是面...

  • 软件测试结束的标准是什么?

    单元测试退出标准 1)单元测试用例设计已经通过评审 2)核心代码100%经过CodeReview 3)单元测试功能...

  • 《徐昊-TDD项目实战70讲》学习笔记 -- Day 7

    07|TDD中的测试(3):集成测试还是单元测试? 集成测试还是单元测试? TDD 中的单元测试 在 TDD 的语...

网友评论

      本文标题:3/26day19_JUnit单元测试_NIO

      本文链接:https://www.haomeiwen.com/subject/pniruhtx.html