day19【JUnit單元測試、NIO】

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 方式可以設置阻塞還是非阻塞    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章