day19【JUnit單元測試、NIO】
反饋和複習
1.我的IP(192.168.1.100)和老師(192.168.1.8)前面都差不多,就後面的不太一樣
2.爲什麼我和同學TCP連接無法建立
IP有外網IP和內網IP之分
複習:
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的雙向通信】
今日內容
這兩天的內容以瞭解爲主【NIO,AIO】
今天內容:
1.Junit單元測測【重點,簡單】
2.NIO 【瞭解】
Buffer -- 緩衝區
Channel -- 通道
Selector -- 選擇器(多路複用器)
第一章 Junit單元測試【重點】
1.什麼是單元測試
a.什麼叫單元測試
i.單元,在java中一個單元可以指某個方法,或者某個類
ii.測試,寫一段代碼對象單元進行測試
b.Junit是專門爲單元測試提供一個第三方框架
作用: 讓一個普通方法獨立運行(替代main方法)
2.Junit的使用步驟
-
下載
www.junit.org下載即可 但是我們不需要去下載,因爲開發工具中自帶(IDEA)
-
具體使用步驟
a.編寫一個被測試類(業務類) /** * 被測試類,業務類 */ public class Dog { public int getSum(int a, int b, int c) { int sum = a + b + c; return sum; } } b.編寫測試類 c.在測試類使用Junit單元測試框架來測試 /** * 測試類 */ public class TestDog { @Test public void test01(){ //測試代碼 Dog dd = new Dog(); int sum = dd.getSum(1, 2, 3); System.out.println("結果爲:"+sum); } @Test public void test02(){ //測試代碼 Dog dd = new Dog(); int sum = dd.getSum(11, 22, 33); System.out.println("結果爲:"+sum); } }
-
運行Junit測試
在需要獨立運行的方法上加上一個註解 @Test
3.單元測試中其他四個註解【理解】
-
Junit4.x
@Before 表示該方法會自動在"每個"@Test方法執行之前執行 @After 表示該方法會自動在"每個"@Test方法執行之後執行 @BeforeClass 表示該方法會自動在"所有"@Test方法執行之前執行 @AfterClass 表示該方法會自動在"所有"@Test方法執行之後執行 注意: @BeforeClass和@AfterClass必須修飾靜態方法 /** * 測試類 */ public class TestDog { // @Before // public void aa(){ // System.out.println("aa..."); // } // @After // public void bb(){ // System.out.println("bb..."); // } @BeforeClass public static void aa(){ System.out.println("aa..."); } @AfterClass public static void bb(){ System.out.println("bb..."); } @Test public void test01(){ //測試代碼 Dog dd = new Dog(); int sum = dd.getSum(1, 2, 3); System.out.println("結果爲:"+sum); } @Test public void test02(){ //測試代碼 Dog dd = new Dog(); int sum = dd.getSum(11, 22, 33); System.out.println("結果爲:"+sum); } }
-
Junit5.x
@BeforeEach: 相當於@Before @AfterEach: 相當於@After @BeforeAll: 相當於@BeforeClass @AfterAll: 相當於@AfterClass 注意: @BeforeAll和@AfterAll必須修飾靜態方法 講義中有一段代碼: 斷言: Assert.assertEquals("異常信息",得到結果,預期的結果); 作用: 比較 "得到結果" 和 "預期的結果" 如果一樣,什麼都不做,如果不一樣拋出異常,並且封裝異常的信息 案例: @Test public void test01(){ //測試代碼 Dog dd = new Dog(); int sum = dd.getSum(1, 2, 3); //使用斷言 Assert.assertEquals("結果不一致呀",sum,10); }
第二章 NIO介紹(瞭解)
1.阻塞與非阻塞
阻塞: 完成某個任務時,任務沒有結束之前,當前線程無法向下繼續執行
非阻塞: 完成某個任務時,不需要等待任務結束,當前線程立即可以繼續向下執行,後期我們再通過其他代碼獲取任務結果
2.同步與異步
同步:
同步可能是阻塞的,也可能非阻塞的
"同步阻塞": 完成某個任務時,任務沒有結束之前,當前線程無法向下執行
"同步非阻塞": 完成某個任務時,不需要等待任務結束,當前線程立即可以繼續向下執行,
後期需要我們自己寫其他代碼獲取結果
異步:
異步一般來說都是非阻塞
"異步非阻塞": 完成某個任務時,不需要等待任務結束,當前線程立即可以繼續向下執行,
後期我們不需要自己寫其他代碼來獲取結果,任務完成之後自動會通過其他機制把結果傳遞給我們
舉例子:
燒水案例: 普通水壺(水開了冒氣) 響水壺(水開了,嗚嗚嗚~)
a.使用普通水壺燒水,燒水過程中我在旁邊看着,別的事不能幹!!! 【同步阻塞】
b.使用普通水壺燒水,燒水過程中我去抽菸,抽根菸回來看一下,再抽根菸再回來看一下!! 【同步非阻塞】
c.使用響水壺,燒水過程中直接去玩LOL,也不需要玩一局看一下,
水開後水壺會以嗚嗚聲音方式告訴我結果!! 【異步非阻塞】
3.BIO,NIO,AIO的介紹
BIO(傳統的IO): 同步阻塞的IO
NIO(New新的IO): 同步阻塞的也可以是同步非阻塞,由Buffer(緩衝區),Channel(通道),Selector(選擇器)
NIO2(也叫AIO): 異步非阻塞的IO
第三章 NIO-Buffer類(瞭解)
1.Buffer的介紹和種類
-
什麼是Buffer
Buffer稱爲緩衝區,本質就是一個數組
-
Buffer的一般操作步驟
a.寫入緩衝區(把數據保存到數組中) b.調用flip方法(切換緩衝區的寫默寫爲讀模式) c.讀緩衝區(把數組中的數據讀取出來) d.調用clear或者compact方法(清空緩衝區或者清除緩衝區中已經讀取過的數據)
-
Buffer的種類
ByteBuffer 字節緩衝區(字節數組)【最常用】 CharBuffer 字符緩衝區(字符數組) DoubleBuffer Double緩衝區(小數數組) FloatBuffer Float緩衝區(小數數組) IntBuffer 整型緩衝區(整型數組) LongBuffer 長整型緩衝區(長整型數組) ShortBuffer 短整型緩衝區(短整型數組)
2.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屬於間接緩衝區
}
}
3.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
什麼是容量(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
什麼是限制: 是指第一個不能操作的元素索引,限制的取值範圍(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
什麼是位置: 將要寫入/讀取的元素的索引,位置取值範圍(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
什麼是標記: 給當前的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()));
}
}
8. 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(通道)(瞭解)
1. Channel介紹和分類
什麼是Channel: Channel是一個讀寫數據的類,和我們學的IO流類似,最大的不同在於IO流有Input和Output之分
,但是通道沒有輸入和輸出通道之分,都叫Channel
通道(Channel的分類):
FileChannel 文件通道,讀寫文件的
DatagramChannel UPD協議通道(通過UDP協議收發數據)
SocketChannel TCP協議中客戶端的通道(給客戶端讀寫數據用的)
ServerSocketChannel TCP協議中服務器端通道(給服務器端讀寫數據用的)
2. 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();
}
}
}
總結
能夠使用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 方式可以設置阻塞還是非阻塞