JUnit单元测试
NIO:Buffer--缓冲区
Channel -- 通道
Selector-- 选择器(多路复用器)
一。JUnit单元测试
JUnit单元测试框架的作用:
@Before :用来修饰方法,该方法会在每一个测试方法执行之前执行一次。@After :用来修饰方法,该方法会在每一个测试方法执行之后执行一次。@BeforeClass :用来静态修饰方法,该方法会在所有测试方法之前执行一次,而且只执行一次。@AfterClass :用来静态修饰方法,该方法会在所有测试方法之后执行一次,而且只执行一次
* @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
同步与异步:
同步是一种可靠的有序运行机制,当我们进行同步操作时候,后续的任务是等待当前调用返回,才会进行下一步,
异步:则相反,其他任务不需要等待当前调用返回,通常依靠事件,回调等机制来实现任务间次序关系
阻塞 和 非阻塞
BIO,NIO,AIO的介绍
BIO(传统的IO): 同步阻塞的IO
NIO(New新的IO): 同步阻塞的也可以是同步非阻塞,由Buffer(缓冲区),Channel(通道),Selector(选择器)
NIO2(也叫AIO): 异步非阻塞的IO
三。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的实现连接
//创建阻塞的服务器通道
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();
}
}
}
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()方法设置、