Java=JUnit单元测试,NIO

JUnit单元测试

NIO:Buffer--缓冲区

         Channel -- 通道

         Selector-- 选择器(多路复用器)

一。JUnit单元测试

单元测试概念
单元:在 Java 中,一个类、一个方法就是一个单元
单元测试:程序员编写的一小段代码,用来对某个类中的某个方法进行功能测试或业务逻辑测试。
 

JUnit单元测试框架的作用:

用来对类中的方法功能进行有目的的测试,以保证程序的正确性和稳定性。
  能够让方法独立运行起来。
 
JUnit单元测试框架的使用步骤:
1.编写业务类,在业务类中编写业务代码,
2.编写测试类,在测试类中编写测试方法,在测试方法中编写测试代码来测试
 
测试方法的注意事项:
 必须是public修饰的,没有返回值,没有参数
必须使用JUint的注解@Test修饰
 
JUint常用注解(JUnit4.x版本)
@Before :用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
 @After :用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
 @BeforeClass :用来静态修饰方法,该方法会在所有测试方法之前执行一次,而且只执行一次。
@AfterClass :用来静态修饰方法,该方法会在所有测试方法之后执行一次,而且只执行一次
 
JUint常用注解(JUnit5.x版本)
 
* @BeforeEach :用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
* @AfterEach :用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
* @BeforeAll :用来静态修饰方法,该方法会在所有测试方法之前执行一次。
* @AfterAll :用来静态修饰方法,该方法会在所有测试方法之后执行一次。
 
示例:
 
/**业务类:实现加减乘除运算 */ 
public class Cacluate { 
/*业务方法1:求a和b之和 */
 public int sum(int a,int b){ 
     return a + b + 10; 
  }


/*业务方法2:求a和b之差 */
 public int sub(int a,int b){
     return a - b; 
  } 
}

public class TestCacluate {

 static Cacluate c = null; 

 @BeforeClass // 用来静态修饰方法,该方法会在所有测试方法之前执行一次。
 public static void init(){ 
   System.out.println("初始化操作");
   // 创建Cacluate对象 
    c = new Cacluate(); 
}

@AfterClass // 用来静态修饰方法,该方法会在所有测试方法之后执行一次。
public static void close(){ 
   System.out.println("释放资源"); 
   c = null; 
}
 @Before // 用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
 public void init(){ 
   System.out.println("初始化操作"); 
    // 创建Cacluate对象 
   c = new Cacluate(); 
 }
@After // 用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
 public void close(){ 
   System.out.println("释放资源"); 
   c = null; 
}

@Test 
public void testSum(){ 
   int result = c.sum(1,1);
    /* 断言:预习判断某个条件一定成立,如果条件不成立,则直接奔溃。
    assertEquals方法的参数 (String message, double expected, double actual) message: 消息      字符串 expected: 期望值 actual: 实际值 */ 
    // 如果期望值和实际值一致,则什么也不发生,否则会直接奔溃。 
   Assert.assertEquals("期望值和实际值不一致",12,result); 
   System.out.println(result); 
}

@Test 
public void testSub(){
     // 创建Cacluate对象 // 
     Cacluate c = new Cacluate(); 
     int result = c.sub(1,1); 
     // 如果期望值和实际值一致,则什么也不发生,否则会直接奔溃。
     Assert.assertEquals("期望值和实际值不一致",0,result);
    System.out.println(result);
  }

}

二。NIO

同步与异步:

同步是一种可靠的有序运行机制,当我们进行同步操作时候,后续的任务是等待当前调用返回,才会进行下一步,

异步:则相反,其他任务不需要等待当前调用返回,通常依靠事件,回调等机制来实现任务间次序关系

阻塞 和 非阻塞

在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如ServerSocket新连接建立完毕,或者数据读取,写入操作完成
 
而非阻塞 则是不管IO操作是否结束,直接返回,相应操作在后台继续处理
 
 
NIO之所以同步,是因为他的accept,read,write 方法的内核IO操作都会阻塞当前线程
 

BIO,NIO,AIO的介绍

BIO(传统的IO): 同步阻塞的IO
NIO(New新的IO): 同步阻塞的也可以是同步非阻塞,由Buffer(缓冲区),Channel(通道),Selector(选择器)  
NIO2(也叫AIO): 异步非阻塞的IO    

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

 

三。Buffer类(缓冲区)

Buffer是一个对象,它对某种基本类型的数组进行了封装,NIO开始使用的Channel通道 就是通过Buffer来读写数据的
 
在NIO中,所有的数据都是用Buffer处理的,它是NIO读写数据的中转池,Buffer实质上是一个数组,通常是一个字节数组,但也可以是其他数组,但一个缓冲区不仅仅是一个数组,重要的是它提供了对数组的结构化访问,而且还可以跟踪系统的读写进程。
 
使用Buffer读写数据一般遵循以上四个步骤:
 
a.写入缓冲区(把数据保存到数组中)
b.调用flip方法(切换缓冲区的写默写为读模式)
c.读缓冲区(把数组中的数据读取出来)
d.调用clear或者compact方法(清空缓冲区或者清除缓冲区中已经读取过的数据)    

Buffer的种类

ByteBuffer 字节缓冲区(字节数组)【最常用】
CharBuffer 字符缓冲区(字符数组)
DoubleBuffer Double缓冲区(小数数组)
FloatBuffer Float缓冲区(小数数组)
IntBuffer 整型缓冲区(整型数组)
LongBuffer 长整型缓冲区(长整型数组)
ShortBuffer 短整型缓冲区(短整型数组)

.ByteBuffer的三种创建方式

在堆中创建缓冲区称为:间接缓冲区
在系统内存创建缓冲区称为:直接缓冲区
间接缓冲区的创建和销毁效率要高于直接缓冲区
间接缓冲区的工作效率要低于直接缓冲区
a.public static allocate(int capacity); 在堆区申请一个固定字节大小的ByteBuffer缓冲区
b.public static allocatDirect(int capacity);在系统的内存中申请一个固定字节大小的ByteBuffer缓冲区 c.public static wrap(byte[] arr);把一个字节数组直接包装成ByteBuffer缓冲区 
    
public class ByteBuffer01 {
    public static void main(String[] args) {
        //创建一个ByteBuffer
        //1.allocate
        ByteBuffer buffer1 = ByteBuffer.allocate(10); //在JVM的堆中,间接缓冲区
        //2.allocatDirect
        ByteBuffer buffer2 = ByteBuffer.allocateDirect(10); //直接和操作系统申请,直接缓冲区
        //创建和销毁角度来看, buffer1效率更高
        //操作缓冲区角度俩看, buffer2效率更好
        //3.wrap
        byte[] bs = new byte[10];
        ByteBuffer buffer3 = ByteBuffer.wrap(bs);
        //buffer3属于间接缓冲区
    }
}    

ByteBuffer的三种添加数据方式

a.public ByteBuffer put(byte b); 添加单个字节
b.public ByteBuffer put(byte[] bs);添加字节数组
c.public ByteBuffer put(byte[] bs,int startIndex,int len):添加一个字节数组的一部分   
    
public class ByteBuffer02 {
    public static void main(String[] args) {
        //1.创建一个ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.打印
        System.out.println(Arrays.toString(buffer.array()));
        //3.添加数据
        //a.添加一个字节
        buffer.put((byte)10);
        buffer.put((byte)20);
        buffer.put((byte)30);
        System.out.println(Arrays.toString(buffer.array()));
        //b.添加一堆字节
        byte[] bs1 = {40,50,60};
        buffer.put(bs1);
        System.out.println(Arrays.toString(buffer.array()));
        //c.添加一堆字节的一部分
        byte[] bs2 = {70,80,90};
        buffer.put(bs2,1,2);
        System.out.println(Arrays.toString(buffer.array()));
    }
}    

4.ByteBuffer的容量-capacity

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

什么是容量(capacity):
	是指Buffer最多包含元素的个数,并且Buffer一旦创建容量无法更改
public int capacity(); 获取Buffer的容量

public class ByteBuffer03 {
    public static void main(String[] args) {
        //1.创建一个ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.获取容量
        int capacity = buffer.capacity();
        System.out.println("容量为:"+capacity);
    }
}        

5.ByteBuffer的限制-limit

限制 limit 是指:第一个不应该读取或写入元素的 index 索引。缓冲区的限制 (limit) 不能为负,并且不能大于容
量。
有两个相关方法:
public int limit() :获取此缓冲区的限制。
public Buffffer limit(int newLimit) :设置此缓冲区的限制。
什么是限制: 是指第一个不能操作的元素索引,限制的取值范围(0-capacity)   
限制作用: 相当于人为"修改"缓冲区的大小(实际上缓冲区大小没有改变,只是可访问的元素的个数变了)    
public class ByteBuffer04 {
    public static void main(String[] args) {
        //1.创建一个ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.打印
        System.out.println(Arrays.toString(buffer.array()));
        //3.获取限制
        System.out.println("当前缓冲区的限制:"+buffer.limit());
        //4.修改限制
        buffer.limit(3);
        System.out.println("将缓冲区的限制改为3");
        System.out.println(Arrays.toString(buffer.array()));
        //5.添加
        buffer.put((byte)10);
        buffer.put((byte)20);
        buffer.put((byte)30);
        buffer.put((byte)40);//索引是3
        System.out.println(Arrays.toString(buffer.array()));
    }
}  

6.ByteBuffer的位置-position

位置 position 是指:当前可写入的索引。位置不能小于 0 ,并且不能大于 " 限制 "
有两个相关方法:
public int position() :获取当前可写入位置索引。
public Buffffer position(int p) :更改当前可写入位置索引。
什么是位置: 将要写入/读取的元素的索引,位置取值范围(0-capacity/limit)
    
public int position(); 获取当前位置
public void positon(int newPosition);修改当的位置    

public class ByteBuffer05 {
    public static void main(String[] args) {
        //1.创建一个ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.获取数据
        System.out.println("当前容量:"+ buffer.capacity());
        System.out.println("当前限制:"+ buffer.limit());
        System.out.println("当前位置:"+ buffer.position());
        //3.添加数据
        buffer.put((byte)10); //0
        buffer.put((byte)20); //1
        buffer.put((byte)30); //2
        buffer.put((byte)40); //3
        System.out.println(Arrays.toString(buffer.array()));
        System.out.println("当前容量:"+ buffer.capacity());
        System.out.println("当前限制:"+ buffer.limit());
        System.out.println("当前位置:"+ buffer.position());
        //4.修改位置
        System.out.println("修改位置为2");
        buffer.position(2);
        //5.添加数据
        buffer.put((byte)50);
        System.out.println(Arrays.toString(buffer.array()));
        System.out.println("当前容量:"+ buffer.capacity());
        System.out.println("当前限制:"+ buffer.limit());
        System.out.println("当前位置:"+ buffer.position());
    }
}

7.ByteBuffer的标记-mark

标记 mark 是指:当调用缓冲区的 reset() 方法时,会将缓冲区的 position 位置重置为该索引。不能为 0 ,不能大
position
相关方法:
public Buffffer mark() :设置此缓冲区的标记为当前的 position 位置
什么是标记: 给当前的position记录下来,当调用reset(重置)时,position会回到标记,标记范围(0-position)
 
public class ByteBuffer06 {
    public static void main(String[] args) {
        //1.创建一个ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(10);
        //2.添加数据
        buffer.put((byte)10);
        buffer.put((byte)20);
        buffer.put((byte)30);
        //position是 3
        //标记
        buffer.mark(); //记录当前位置 3
        buffer.put((byte)40);
        buffer.put((byte)50);
        buffer.put((byte)60);
        buffer.put((byte)70);
        buffer.put((byte)80);
        System.out.println(Arrays.toString(buffer.array()));
        //3.重置
        buffer.reset(); //把位置修改为刚刚的标记
        buffer.put((byte)99);
        buffer.put((byte)99);
        buffer.put((byte)99);
        System.out.println(Arrays.toString(buffer.array()));
    }
}    

ByteBuffer的其他方法

a.public int remaining():获取position与limit之间的元素数。
b.public boolean isReadOnly():获取当前缓冲区是否只读。  
c.public boolean isDirect();获取当前缓冲区是否为直接缓冲区。
d.public Buffer clear(); 清空缓冲区(还原缓冲区的状态) 
    将position设置为:0
    将限制limit设置为容量capacity
    并且会丢弃标记
e.public Buffer flip(); 切换读写模式(缩小范围)
    将limit设置为当前position位置 
    将当前position位置设置为0
    并且丢弃标记
f.public Buffer rewind();重绕此缓冲区。    
    将position位置设置为0
    限制limit不变
    丢弃标记  

四。Channel通道

Channel介绍和分类

什么是Channel: Channel是一个读写数据的类,和我们学的IO流类似,最大的不同在于IO流有Input和Output之分
    ,但是通道没有输入和输出通道之分,都叫Channel
通道(Channel的分类):
	FileChannel 文件通道,读写文件的
    DatagramChannel   UPD协议通道(通过UDP协议收发数据)
    SocketChannel    TCP协议中客户端的通道(给客户端读写数据用的)
    ServerSocketChannel TCP协议中服务器端通道(给服务器端读写数据用的)   

FileChannel类的基本使用

 
public class FileChannelDemo01 {
    public static void main(String[] args) throws IOException {
        //复制文件
        //1.创建文件对象
        File srcFile = new File("G:\\upload\\111.png"); //源文件
        File destFile = new File("copy.png");
        //2.创建文件的输入输出流
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);
        //3.通道
        FileChannel inChannel = fis.getChannel();
        FileChannel outChannel = fos.getChannel();
        //4.复制文件
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int len = 0;
        while ((len = inChannel.read(buffer)) != -1) {
            //切换模式
            buffer.flip();
            //读缓冲区数据,写入到文件中
            outChannel.write(buffer);
            //清空
            buffer.clear();
        }
        //5.是否资源
        outChannel.close();
        inChannel.close();
        fos.close();
        fis.close();
    }
}

3.FileChannel结合MappedByteBuffer实现高效读写

public class FileChannelDemo02 {
    public static void main(String[] args) throws Exception {
        //1.创建两个文件
        // 只读模式 r
        // 读写模式 rw
        RandomAccessFile srcFile = new RandomAccessFile("H:\\BaiduNetdiskDownload\\cxf_web\\day03.zip", "r");
        RandomAccessFile destFile = new RandomAccessFile("copy.zip", "rw");
        //2.获取通道
        FileChannel inChannel = srcFile.getChannel();
        FileChannel outChannel = destFile.getChannel();
        //3.获取文件的大小
        int size = (int) inChannel.size();//
        //4.建立映射字节缓冲区
        //map(模式,开始索引,字节数);
        MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
        //5.复制 耗时:10949毫秒
        long start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            byte b = inMap.get(i);
            outMap.put(i, b);
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-start)+"毫秒");
        //6.释放资源
        outChannel.close();
        inChannel.close();
    }
}

注意: 
	a.MappedByteBuffer只适用于复制2G以下的文件
    b.如果是2G以上文件,分多次复制(参考案例:复制2GB以上文件)       

4.SocketChannel和ServerSocketChannel的实现连接

ServerSocketChannel的创建(阻塞方式)
 
 
//创建阻塞的服务器通道
public class ServerSocketChannelDemo {
    public static void main(String[] args) throws IOException {
        //1.创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //2.绑定本地的某个端口
        serverSocketChannel.bind(new InetSocketAddress(8888));
        System.out.println("服务器已经启动...");
        //3.接收客户端通道
        SocketChannel socketChannel = serverSocketChannel.accept();
        //4.后续代码
        System.out.println("后续代码...");
    }
}

ServerSocketChannel的创建(非阻塞方式)

/**
 * 同步非阻塞的服务器通道..
 */
public class ServerSocketChannelDemo02 {
    public static void main(String[] args) throws IOException, InterruptedException {
        //1.创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //设置为同步非阻塞的服务器通道
        serverSocketChannel.configureBlocking(false);
        //2.绑定本地的某个端口
        serverSocketChannel.bind(new InetSocketAddress(8888));
        System.out.println("服务器已经启动...");
        while (true) {
            //3.接收客户端通道
            SocketChannel socketChannel = serverSocketChannel.accept();
            //4.后续代码
            System.out.println("后续代码...");
            //5.判断
            if (socketChannel != null) {
                System.out.println("和客户端进行交互...");
            }else{
                System.out.println("暂时没有客户端,2秒后继续查看...");
                Thread.sleep(2000); //模拟服务器去做其他任务
            }
        }
    }
}

SocketChannel的创建

//阻塞式的客户端
public class SocketChannelDemo01 {
    public static void main(String[] args) throws IOException {
        //1.创建SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();
        //2.去连接服务器
        boolean b = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
        //相当于以前 Socket socket = new Socket("127.0.0.1",8888);
        //3.后续代码
        System.out.println("后续代码...");
    }
}


/**
 * 非阻塞式的客户端
 */
public class SocketChannelDemo02 {
    public static void main(String[] args) throws InterruptedException, IOException {
        //1.创建SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();
        //设置,设置为非阻塞的
        socketChannel.configureBlocking(false);
        while (true) {
            //2.去连接服务器
            try {
                boolean b = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
                //相当于以前 Socket socket = new Socket("127.0.0.1",8888);
                //3.后续代码
                System.out.println("后续代码...");
                //4.判断
                if (b) {
                    System.out.println("和服务器进行交互...");
                }
            }catch (Exception e){
                System.out.println("两秒后重写连接...");
                Thread.sleep(2000);
            }
        }
    }
}

5.SocketChannel和ServerSocketChannel的实现通信

 
public class ServerSocketChannelDemo {
    public static void main(String[] args) throws IOException {
        //1.创建ServerSocketChannel对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9999));
        //2.接收客户端
        SocketChannel socketChannel = serverSocketChannel.accept();
        System.out.println("有客户端连接了...");
        //3.读取数据
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        int len = socketChannel.read(byteBuffer);
        //4.打印数据
        byteBuffer.flip(); //先把byteBuffer切换为读模式
        String str = new String(byteBuffer.array(), 0, len);
        System.out.println("客户端说:"+str);
        //5.回数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("Hello,我是服务器..".getBytes());
        //切换为读模式
        buffer.flip();
        socketChannel.write(buffer);
        //6.释放资源
        socketChannel.close();
        serverSocketChannel.close();
    }
}

public class SocketChannelDemo {
    public static void main(String[] args) throws IOException {
        //1,创建SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();
        //2.去连接
        boolean b = socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
        //3.判断
        if (b) {
            //4.发送数据
            System.out.println("连接服务器成功....");
            ByteBuffer byteBuffer = ByteBuffer.wrap("Hello,我是客户端...".getBytes());
            socketChannel.write(byteBuffer);
            //5.读取数据
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int len = socketChannel.read(buffer);
            buffer.flip();
            System.out.println(new String(buffer.array(),0,len));
            //6.释放资源
            socketChannel.close();
        }
    }
}
other================================================================================
 
 
 public static void main(String[] args) throws  Exception{


        RandomAccessFile srcFile = new RandomAccessFile("b.png", "r");
        RandomAccessFile targetFile = new RandomAccessFile("cpy.png", "rw");
        //获取通道
        FileChannel srcFileChannel = srcFile.getChannel();
        FileChannel targetFileChannel = targetFile.getChannel();
        //获取文件大小
        long size = srcFileChannel.size();
        //创建内存缓冲区
        MappedByteBuffer srcmap = srcFileChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
        MappedByteBuffer targetmap = targetFileChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
        for (int i=0;i<size;i++){
            byte be = srcmap.get(i);
            targetmap.put(be);
        }
        srcFileChannel.close();
        targetFileChannel.close();


    }
客户端
public class zclient {
    public static void main(String[] args) throws Exception{
        SocketChannel socketChannel = SocketChannel.open();
        boolean connect = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
        if (connect){
            ByteBuffer byteBuffer = ByteBuffer.wrap("客户端:服务器,我是客户端!".getBytes());
            socketChannel.write(byteBuffer);

            ByteBuffer allocate = ByteBuffer.allocate(1024);
            int len = socketChannel.read(allocate);
            allocate.flip();
            String s = new String(allocate.array(), 0, len);
            System.out.println("客户端:我是服务器,"+s);
        }
    }
}

服务器端:
public class zserver {
    public static void main(String[] args) throws Exception{
        ServerSocketChannel socketServerChannel = ServerSocketChannel.open();
        socketServerChannel.bind(new InetSocketAddress(8888));
        SocketChannel socketChannel = socketServerChannel.accept();
        ByteBuffer allocate = ByteBuffer.allocate(1024);
        int len = socketChannel.read(allocate);
        allocate.flip();
        String s = new String(allocate.array(), 0, len);
        System.out.println("服务器:"+s);

        ByteBuffer wrap = ByteBuffer.wrap("服务器:我是服务器,我来了".getBytes());
        socketChannel.write(wrap);
        System.out.println("服务器关闭");
        socketChannel.close();
        socketServerChannel.close();



    }
}

注意:在创建ByteBuffer的时候,如果用的allocate 或者 allocateDirect()生成ByteBuffer的时候需要 使用 flip()方法进行设置读写
如果用的wrap()初始化的ByteBuffer则不用使用flip()方法设置、
 
 
 
 
发布了117 篇原创文章 · 获赞 20 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/u010581811/article/details/105123557