day24【模擬服務器、軟件架構、Junit、NIO】課上

1.模擬服務器(擴展)

需求:在瀏覽器中訪問當前項目下的資源 : web/index.html ,我們自己書寫服務器,將當前項目下的index.html頁面中的所有內容響應

代碼演示:

package com.itheima.sh.server_01;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/*
    模擬服務器
 */
public class ServerDemo01 {
    public static void main(String[] args) throws Exception {
        //1.創建服務器套接字對象
        ServerSocket ss = new ServerSocket(8888);
        //讓服務器一直運行
        while (true) {
            //2.偵聽並獲取客戶端套接字
            Socket s = ss.accept();
            //一張圖片開啓一個線程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //3.獲取從通道中讀取瀏覽器發送數據的字節輸入
                        InputStream is = s.getInputStream();
                        //
                        //4.將字節輸入流轉換爲字符輸入緩衝流
                        BufferedReader br = new BufferedReader(new InputStreamReader(is));
                        //5.讀取第一行數據 GET /web/index.html HTTP/1.1
                        String line = br.readLine();
                        //6.將字符串變量line進行按照空格切割
                        String[] arr = line.split(" ");//{"GET","/web/index.html","HTTP/1.1"}
                        //7.取出數組的第二個數據並去掉前面的/
                        // arr[1] 就是"/web/index.html"
                        String path = arr[1].substring(1);//"web/index.html"
//        System.out.println("path = " + path);
                        //8.根據瀏覽器訪問服務器的資源路徑創建字節輸入流
                        //使用輸入流關聯該index.html頁面讀取到內存中
                        FileInputStream fis = new FileInputStream(path);
                        //9.使用客戶端套接字對象關聯通道獲取字節輸出流
                        //將內存中的數據寫到通道中
                        OutputStream os = s.getOutputStream();
                        //由於這裏是想本地的內容響應給瀏覽器,那麼瀏覽器接收數據之前需要一些特殊的信息(響應頭)
        /*
            響應頭中包含響應的http協議版本HTTP/1.1
            服務器返回的狀態碼 200
            狀態值:OK
         */
                        os.write("HTTP/1.1 200 OK\r\n".getBytes());
                        //Content-Type:text/html表示響應文本的類型
                        os.write("Content-Type:text/html\r\n".getBytes());
                        // 必須要寫入空行,否則瀏覽器不解析
                        os.write("\r\n".getBytes());
                        //10.使用輸入流讀取index.html頁面中的數據到內存中
                        byte[] buf = new byte[1024];
                        int len;
                        while ((len = fis.read(buf)) != -1) {
                            //向通道中寫數據
                            os.write(buf, 0, len);
                        }
                        os.close();
                        fis.close();
                        br.close();
                        is.close();
                        s.close();
                    } catch (Exception e) {

                    }
                }
            }).start();

        }


    }
}

2.軟件架構(理解)

CS:Client Server 客戶端 服務器端

舉例:

QQ  愛奇藝 微信 迅雷 淘寶。。。。

有點:

​ 1)客戶端可以幫助服務器承載一些壓力

​ 2)用戶體驗好一些,高清。。。不太卡

缺點:

​ 1)用戶按照一個客戶端,升級麻煩,安裝一些無關緊要的軟件

​ 2)佔用用戶的空間

​ 3)維護起來不方便,成本大

BS:Browser Server 瀏覽器 服務器

舉例:

淘寶 京東 12306 唯品會。。。。

​ 優點:

​ 1)維護起來方便,只有服務器端,用戶只需要安裝一個瀏覽器即可,維護成本低

​ 2)不佔用用戶的空間,不用升級

​ 缺點:

​ 1)服務器壓力大

​ 2)用戶看視頻不那麼高清

其實BS中也屬於一種特殊cs

3.JUnit單元測試(掌握)

概念

junit是單元測試,你想測哪個方法就寫一個對應的測試方法,然後用junit運行。每個方法之間是獨立的,非常靈活。而且測試方法一般不會直接寫在原類中,而是單獨的測試類,這樣測試代碼就完全與邏輯代碼分開了。

​ Junit是Java語言編寫單元測試框架。Junit屬於第三方工具,一般情況下需要導入jar包,而多數Java開發環境都集成了Junit。

使用方式

1.使用之前的方式執行某個類的非靜態方法

package com.itheima.sh.junit_02;

public class JunitDemo01 {
    public static void main(String[] args) {
        //創建對象
        JunitDemo01 jd = new JunitDemo01();
        jd.show();
    }
    //定義非靜態方法
    public void show(){
        System.out.println("show....");
    }
}

2.使用junit方式執行非靜態方法

1.由於junit屬於第三方的測試框架,使用之前需要導包,因爲idea中已經集成了junit測試框架,我們在使用的時候直接導入包即可

2.首先要測試運行的方法上書寫一個測試註解

@Test

3.第一次使用由於沒有導入包,所以會報錯,在報錯位置按alt+enter萬能鍵就會導入包

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

4.運行被@Test註解修飾的方法:

在要運行的方法名上右鍵—run即可

3.使用Junit測試框架運行某個方法的注意事項

1.被測試的方法要求必須是public 修飾 無返回值 沒有參數

public void show(){
        System.out.println("show....");
    }

2.被測試的方法所屬類不要單獨命名爲Test因爲會和Junit測試框架中名字衝突。建議類名定義規範:

​ ProductDaoTest ProductServiceTest xxxTest

4.其他註解

package com.itheima.sh.junit_02;

import org.junit.*;

/*
    其他註解:
    1.@Before : 只要運行一次測試方法就會在之前執行一次
    2.@After: 只要運行一次測試方法就會在之後執行一次
    3.@BeforeClass : 修飾靜態方法,在運行測試方法之前執行,並且只執行一次
    4.@AfterClass : 修飾靜態方法,在運行測試方法之後執行,並且只執行一次
 */
public class Junit01Test {

    @BeforeClass
    public static void beforeClass(){
        System.out.println("beforeClass.....");
    }
    @AfterClass
    public static void afterClass(){
        System.out.println("afterClass.....");
    }

    @Before
    public void before(){
        System.out.println("before.....");
    }
    @After
    public void after(){
        System.out.println("after.....");
    }
    //測試方法
    @Test
    public void show1(){
        System.out.println("show1....");
    }

    @Test
    public void show2(){
        System.out.println("show2....");
    }
}
//beforeClass.....
//before.....
//show1....
//after.....
//before.....
//show2....
//after.....
//afterClass.....

小結:

其他註解:
1.@Before : 只要運行一次測試方法就會在之前執行一次
2.@After: 只要運行一次測試方法就會在之後執行一次
3.@BeforeClass : 修飾靜態方法,在運行測試方法之前執行,並且只執行一次
4.@AfterClass : 修飾靜態方法,在運行測試方法之後執行,並且只執行一次

斷言技術

1.作用:可以判斷期望值和實際值是否相等,不相等則報錯,相等則正常運行。

2.使用方式:

Assert.assertEquals(args1, args2);
	args1 :表示期望值
    args2: 實際值

3.代碼演示

package com.itheima.sh.junit_02;

import org.junit.Assert;
import org.junit.Test;

/*
    斷言技術
 */
public class Junit02Test {

    @Test
    public void show(){
        //需求:調用方法求和值
        int sum = getSum(10, 20);
        //使用斷言技術斷言和值是否正確
        //10表示期望值  sum表示實際值
        Assert.assertEquals(30,sum);
    }

    private int getSum(int a, int b) {

        return a*b;
    }
}

小結:

斷言技術:以判斷期望值和實際值是否相等,不相等則報錯,相等則正常運行。

Assert.assertEquals(args1, args2);
	args1 :表示期望值
    args2: 實際值

4.NIO(掌握)

1.概念介紹

之前學習的IO稱爲BIO(blocking IO)具有阻塞功能。效率低。

NIO:New IO 新的IO,從jdk1.4開始有的,也可以理解爲Non-blocking IO 非阻塞的IO.提高效率。

NIO包括三個組件:

1.緩衝區(Buffer):專門用來存儲數據的

2.通道(Channel):連接客戶端和服務器,該通道中只能傳輸Buffer

3.選擇器(Selector):使用選擇器可以完成監聽多個通道

三者缺一不可。

2.Java NIO 傳統IO(BIO)的主要區別

在這裏插入圖片描述
BIO:

在這裏插入圖片描述

NIO:

在這裏插入圖片描述

3.Buffer緩衝區(很重要)

1.Buffer表示緩衝區,用來存儲數據的,屬於抽象類,我們一般使用子類

2.子類:

ByteBuffer(使用它)
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer 

我們重點學習ByteBuffer

1.ByteBuffer字節緩衝區。

1.1獲取ByteBuffer字節緩衝區的對象

  • 使用靜態方法:

    1.static ByteBuffer allocate(int capacity) 分配一個新的字節緩衝區。 
        	capacity 緩衝區的大小,就是字節數組大小。容量給定之後,不能變,底層是一個數組
    2.static ByteBuffer allocateDirect(int capacity) 分配新的直接字節緩衝區
    3.static ByteBuffer wrap(byte[] array)byte 數組包裝到緩衝區中。 創建的緩衝區位於堆內存中,稱爲非直接緩衝區
        
        說明:
            1)allocate和wrap方法創建的緩衝區是位於堆內存中,稱爲間接緩衝區(非直接緩衝區)
            2)allocateDirect方法創建的緩衝區位於不是jvm內存中,位於系統內存中,操作效率更高,何時讀寫數據以及釋放資源我們無法控制,由系統控制。稱爲直接緩衝區
    

    補充方法:

    獲取緩衝區容量大小:

    int capacity() 返回此緩衝區的容量。 
    
  • 代碼演示:

    package com.itheima.sh.buffer_03;
    
    import java.nio.ByteBuffer;
    
    /*
        創建緩衝區方式:
        1.static ByteBuffer allocate(int capacity) 分配一個新的字節緩衝區。
        	capacity 緩衝區的大小,就是字節數組大小。容量給定之後,不能變,底層是一個數組
        2.static ByteBuffer allocateDirect(int capacity) 分配新的直接字節緩衝區
    
        3.static ByteBuffer wrap(byte[] array)  將 byte 數組包裝到緩衝區中。 創建的緩衝區位於堆內存中,稱爲非直接緩衝區
    
        說明:
            1)allocate方法創建的緩衝區是位於堆內存中,稱爲間接緩衝區(非直接緩衝區)
            2)allocateDirect方法創建的緩衝區位於不是jvm內存中,位於系統內存中,操作效率更高,何時讀寫數據以及釋放資源我們無法控制,由系統控制
                稱爲直接緩衝區
    
     */
    public class BufferDemo01 {
        public static void main(String[] args) {
            //1.static ByteBuffer allocate(int capacity) 分配一個新的字節緩衝區。
            ByteBuffer byteBuffer = ByteBuffer.allocate(10);
            //查看容量 int capacity() 返回此緩衝區的容量。
            int capacity = byteBuffer.capacity();
            System.out.println("capacity = " + capacity);//10
    
            //2.static ByteBuffer allocateDirect(int capacity) 分配新的直接字節緩衝區
            ByteBuffer byteBuffer1 = ByteBuffer.allocateDirect(10);//10
            System.out.println(byteBuffer1.capacity());
    
            //3.static ByteBuffer wrap(byte[] array)  將 byte 數組包裝到緩衝區中。 創建的緩衝區位於堆內存中,稱爲非直接緩衝區
            byte[] buf = {65,66,97};
            ByteBuffer byteBuffer2 = ByteBuffer.wrap(buf);
            System.out.println(byteBuffer2.capacity());//3
    
        }
    }
    
    
  • 非直接緩衝區

在這裏插入圖片描述

  • 直接緩衝區

在這裏插入圖片描述

  • 間接緩衝區的創建和銷燬效率要高於直接緩衝區,但是間接緩衝區的工作效率要低於直接緩衝區

1.2常用方法

1.put array() int limit() capacity()

package com.itheima.sh.buffer_03;

import java.nio.ByteBuffer;
import java.util.Arrays;

/*
    方法:
    1.添加數據:abstract  ByteBuffer put(byte b)
    2.快速查看數據,將字節緩衝區變爲字節數組: byte[] array()
    3.int limit() :返回此緩衝區的限制。
    4.Buffer limit(int newLimit)  :
        設置此緩衝區的限制.參數:newLimit表示指定的限制的索引,從limit開始後面的位置不能操作(包括newLimit索引對應的位置)
        0=<limit<=capacity
 */
public class BufferDemo02 {
    public static void main(String[] args) {
        //1.創建非直接緩衝區
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //2.添加數據
        byteBuffer.put((byte) 11);
        byteBuffer.put((byte) 22);
        byteBuffer.put((byte) 33);
        //3. 快速查看數據,將字節緩衝區變爲字節數組: byte[] array()
        byte[] arr = byteBuffer.array();
        //獲取容量
        int capacity = byteBuffer.capacity();
        //[11, 22, 33, 0, 0, 0, 0, 0, 0, 0]
        System.out.println(Arrays.toString(arr));
        System.out.println("容量:"+capacity);//容量:10

        //3.int limit() :返回此緩衝區的限制。
        int limit = byteBuffer.limit();//默認的限制是和容量相等10
        System.out.println("limit = " + limit);

        //4.Buffer limit(int newLimit)  :設置的限制的索引以及該索引後面的空間都不能添加數據了
//        byteBuffer.limit(5);//5索引以及後面的空間不能添加數據了

        byteBuffer.put((byte) 44);
        byteBuffer.put((byte) 55);
        System.out.println(Arrays.toString(byteBuffer.array()));
//        byteBuffer.put((byte) 66);


        //更改limit
        byteBuffer.limit(0);
        byteBuffer.put((byte) 66);

    }
}

小結:

1.添加數據:abstract ByteBuffer put(byte b)
2.快速查看數據,將字節緩衝區變爲字節數組: byte[] array()
3.int limit() :返回此緩衝區的限制。
4.Buffer limit(int newLimit) :
設置此緩衝區的限制.參數:newLimit表示指定的限制的索引,從limit開始後面的位置不能操作(包括newLimit索引對應的位置)
0=<limit<=capacity

在這裏插入圖片描述

2.position() :位置代表將要存放的元素的索引。位置不能小於0,也不能大於limit.

 int position() 返回此緩衝區的位置。 
 Buffer position(int newPosition) 設置此緩衝區的位置。 
package com.itheima.sh.buffer_03;

import java.nio.ByteBuffer;
import java.util.Arrays;

/*
    1.position()	:位置代表將要存放的元素的索引。位置不能小於0,也不能大於limit.
 */
public class BufferDemo03 {
    public static void main(String[] args) {
        //1.創建緩衝區
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //position位置:0,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        //2.添加數據
        byteBuffer.put((byte) 11);
        //定義字節數組
        byte[] buf = {22,33,44};
        byteBuffer.put(buf);
        //position位置:4,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());

        // position(int newPosition) 設置此緩衝區的位置。
        byteBuffer.position(6);
        byteBuffer.put((byte) 55);
        //position位置:7,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        //[11, 22, 33, 44, 0, 0, 55, 0, 0, 0]
        System.out.println(Arrays.toString(byteBuffer.array()));
    }
}

小結:

position>=0 position <=limit

每次添加數據都會向position位置添加,然後position向後移動

圖解:

在這裏插入圖片描述

3.標記 (mark)與重置(reset) : 標記是一個索引,通過 Buffer 中的 mark() 方法指定 Buffer 中一個特定的 position,之後可以通過調用 reset() 方法恢復到這個 position標記。

代碼演示:

package com.itheima.sh.buffer_03;

import java.nio.ByteBuffer;
import java.util.Arrays;

/*
    1.標記 (mark)與重置(reset) : 標記是一個索引,通過 Buffer 中的 mark()
    方法指定 Buffer 中一個特定的 position,之後可以通過調用 reset() 方法恢復到這個 position標記。
 */
public class BufferDemo04 {
    public static void main(String[] args) {
        //1.創建緩衝區
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //position位置:0,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        //2.添加數據
        byteBuffer.put((byte) 11);
        //定義字節數組
        byte[] buf = {22,33,44};
        byteBuffer.put(buf);
        //position位置:4,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        //記錄當前position的位置是4
        byteBuffer.mark();
        //繼續添加
        byteBuffer.put((byte) 55);
        byteBuffer.put((byte) 66);
        //[11, 22, 33, 44, 55, 66, 0, 0, 0, 0]
        System.out.println(Arrays.toString(byteBuffer.array()));
        //position位置:6,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        //重置,就是將當前的position恢復到mark的位置
        byteBuffer.reset();
        //在添加數據77
        byteBuffer.put((byte) 77);
        //[11, 22, 33, 44, 77, 66, 0, 0, 0, 0]
        System.out.println(Arrays.toString(byteBuffer.array()));
        //position位置:5,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
    }
}

在這裏插入圖片描述

0 <= mark <= position <= limit <= capacity

4.Buffer clear():還原緩衝區的狀態。 清空緩衝區並返回對緩衝區的引用 數據沒有被刪除,數據還在緩衝區中。

  • 將position設置爲:0
  • 將限制limit設置爲容量capacity
  • 丟棄標記mark
package com.itheima.sh.buffer_03;

import java.nio.ByteBuffer;
import java.util.Arrays;

/*
  Buffer clear():還原緩衝區的狀態。** **清空緩衝區並返回對緩衝區的引用** 數據沒有被刪除,數據還在緩衝區中。
    - 將position設置爲:0
    - 將限制limit設置爲容量capacity
    - 丟棄標記mark
 */
public class BufferDemo05 {
    public static void main(String[] args) {
        //創建緩衝區
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //添加數據
        byteBuffer.put((byte) 11);
        byteBuffer.put((byte) 22);
        byteBuffer.put((byte) 33);
        byteBuffer.put((byte) 44);
        //position位置:4,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        //設置limit
        byteBuffer.limit(4);
        //添加數據
//        byteBuffer.put((byte) 55);
        //position位置:4,限制limit:4,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        /*
            調用clear方法:
            1)將position變爲0
            2)limit變爲capacity位置
            3)清除標記
            數據還在數組中
         */
        byteBuffer.clear();
        //position位置:0,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
        //[11, 22, 33, 44, 0, 0, 0, 0, 0, 0]
        System.out.println(Arrays.toString(byteBuffer.array()));
    }
}

5.flip():縮小limit的範圍。 切換。在讀寫數據之間要調用這個方法。

  • 將limit設置爲當前position位置
  • 將當前position位置設置爲0
  • 丟棄標記
package com.itheima.sh.buffer_03;

import java.nio.ByteBuffer;
import java.util.Arrays;

/*
    flip():縮小limit的範圍。 切換。在讀寫數據之間要調用這個方法。切換讀取模式
        - 將limit設置爲當前position位置
        - 將當前position位置設置爲0
        - 丟棄標記
 */
public class BufferDemo06 {
    public static void main(String[] args) {
        //創建緩衝區
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //添加數據
        byteBuffer.put((byte) 11);
        byteBuffer.put((byte) 22);
        byteBuffer.put((byte) 33);
        byteBuffer.put((byte) 44);
        //position位置:4,限制limit:10,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());

        //調用 flip():方法開始讀取數據
        byteBuffer.flip();
        //position位置:0,限制limit:4,容量:10
        System.out.println("position位置:"+byteBuffer.position()+",限制limit:"+byteBuffer.limit()+",容量:"+byteBuffer.capacity());
    }
}

圖解:

在這裏插入圖片描述

小結:

flip():縮小limit的範圍。 切換。在讀寫數據之間要調用這個方法。切換讀取模式

​ 將limit設置爲當前position位置
​ 前position位置設置爲0

​ 丟棄標記

6.**獲取Buffer中的數據: **

abstract  byte get() 讀取單個字節。讀取此緩衝區當前位置的字節,然後該位置position遞增。
ByteBuffer get(byte[] dst)批量讀取多個字節到 dst 中,position移動到最後.需要定義一個空的字節數組
abstract  byte get(int index)讀取指定索引位置的字節(不會移動 position)

代碼演示:

package com.itheima.sh.buffer_03;

import java.nio.ByteBuffer;
import java.util.Arrays;

/*
    獲取數據方法:
        abstract  byte get() 讀取單個字節。讀取此緩衝區當前位置的字節,然後該位置position遞增。
        ByteBuffer get(byte[] dst)批量讀取多個字節到 dst 中,position移動到最後.需要定義一個空的字節數組
        abstract  byte get(int index)讀取指定索引位置的字節(不會移動 position)
 */
public class BufferDemo07 {
    public static void main(String[] args) {
        //創建緩衝區
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        //添加數據
        byteBuffer.put((byte) 11);
        byteBuffer.put((byte) 22);
        byteBuffer.put((byte) 33);
        byteBuffer.put((byte) 44);
        //切換爲讀取數據模式
        byteBuffer.flip();
        // abstract  byte get() 讀取單個字節。讀取此緩衝區當前位置的字節,然後該位置position遞增。
       /* byte b = byteBuffer.get();
        System.out.println("b = " + b);*/
       //使用循環控制讀取數據 byteBuffer.limit() 就是4
        /*for(int i=0;i<byteBuffer.limit();i++){//i表示控制次數
            byte b = byteBuffer.get();
            System.out.println("b = " + b);
        }*/
        //  ByteBuffer get(byte[] dst)批量讀取多個字節到 dst 中,position移動到最後.需要定義一個空的字節數組
//        byte[] buf = new byte[byteBuffer.limit()];//長度是到limit這裏就是4
//        byteBuffer.get(buf);//該方法結束將數據放到數組中
//        //[11, 22, 33, 44]
//        System.out.println(Arrays.toString(buf));

        //abstract  byte get(int index)讀取指定索引位置的字節(不會移動 position)
        for(int index=0;index<byteBuffer.limit();index++){//index表示索引
            byte b = byteBuffer.get(index);
            System.out.println("b = " + b);
        }


    }
}

小結:

獲取數據方法:

abstract byte get() 讀取單個字節。讀取此緩衝區當前位置的字節,然後該位置position遞增。
ByteBuffer get(byte[] dst)批量讀取多個字節到 dst 中,position移動到最後.需要定義一個空的字節數組
abstract byte get(int index)讀取指定索引位置的字節(不會移動 position)

4.Channel通道(很重要)

1.概念

通過channel通道對持久設備和內存建立連接,思想類似於之前學習的BIO.但是通道不能直接傳輸數據,只是負責建立通道,傳輸數據必須存儲到Buffer中,然後傳遞buffer緩衝區。channel屬於雙向的,可以讀寫都位於一個通道中。

2.分類

FileChannel:是操作本地文件的通道

SocketChannel:相當於之前BIO對應的Socket表示客戶端通道

ServerSocketChannel:相當於之前BIBO對應的ServerSocket表示服務器端通道

DatagramChannel:UDP協議

3.獲取通道

1.FileChannel:是操作本地文件的通道

​  FileInputStream : 獲取的是FileChannel通道

FileOutputStream:獲取的是FileChannel通道

 RandomAccessFile:獲取的是FileChannel通道

​ RandomAccessFile表示隨機訪問的文件類,可以指定操作模式:

RandomAccessFile(String name, String mode) 
    	參數:
    		name:操作文件的路徑
            mode:操作文件的模式:
                	"r" 讀模式
                    "rw" 讀寫模式

分別使用上述三個類中的**FileChannel getChannel()**方法就可以獲取 FileChannel 通道

4.FileChannel 的使用

代碼演示:

package com.itheima.sh.channel_04;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/*
FileChannel講解:
    使用步驟:
        1)使用FileInputStream對象調用FileChannel   getChannel() 獲取輸入的 FileChannel
        2)使用FileOutputStream對象調用 FileChannel getChannel() 獲取輸出的 FileChannel
    需求:使用FileChannel將D:\test\故事.txt複製到項目根目錄下爲故事.txt
            說明:源文件故事.txt文件大小是134字節
 */
public class ChannelDemo01 {
    public static void main(String[] args) throws Exception{
        //1.創建字節輸入流關聯數據源文件 D:\test\故事.txt
        FileInputStream fis = new FileInputStream("D:\\test\\故事.txt");

        //2.創建字節輸出流對象關聯目的地文件 目錄下爲故事.txt
        FileOutputStream fos = new FileOutputStream("故事.txt");
        //3.分別獲取通道即FileChannel getChannel()
        //3.1獲取讀取數據的通道
        FileChannel inChannel = fis.getChannel();
        //3.2獲取讀寫數據的通道
        FileChannel outChannel = fos.getChannel();
        //4.創建字節緩衝區ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //5.使用FileChannel對象調用FileChannel類中的讀取數據方法放到緩衝區中
        //abstract  int read(ByteBuffer dst)
        while((inChannel.read(buffer))!=-1){
            //切換讀取模式
            buffer.flip();
            //6.使用FileChannel類中的寫方法將緩衝區的數據寫到目的地文件中
            //abstract  int write(ByteBuffer src)
            outChannel.write(buffer);
            //清空
            buffer.clear();
        }
        //7.關閉資源
        outChannel.close();
        inChannel.close();
        fos.close();
        fis.close();

    }
}

小結:

1)使用FileInputStream對象調用FileChannel getChannel() 獲取輸入的 FileChannel
2)使用FileOutputStream對象調用 FileChannel getChannel() 獲取輸出的 FileChannel

5.FileChannel結合MappedByteBuffer實現高效讀寫

1.MappedByteBuffer 屬於ByteBuffer 子類,創建的緩衝區稱爲直接緩衝區,位於系統內存中,操作效率要高很多。

需求:將D:\test\故事.txt賦值到項目根目錄

代碼演示:

package com.itheima.sh.channel_04;

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/*
    FileChannel結合MappedByteBuffer實現高效讀寫
    使用步驟:
    1.獲取FileChannel通道
        使用:隨機操作文件的類使用:RandomAccessFile(String name, String mode)
    2.使用通道對象調用FileChannel中的映射方法獲取MappedByteBuffer
        abstract  MappedByteBuffer map(FileChannel.MapMode mode, long position, long size) 將此通道的文件區域直接映射到內存中。
             mode:表示映射模式:
                static FileChannel.MapMode READ_ONLY 只讀映射模式。
                static FileChannel.MapMode READ_WRITE 讀取/寫入映射模式。
             position:表示開始操作的位置
             size:要映射的區域大小,我們可以使用FileChannel中的方法獲取:
             abstract  long size() 返回此通道的文件的當前大小。
      返回值:
        MappedByteBuffer的父類是ByteBuffer,那麼MappedByteBuffer的對象可以使用ByteBuffer方法

 */
public class ChannelDemo02 {
    public static void main(String[] args) throws Exception{
        //1.創建隨機訪問文件的類的對象
        RandomAccessFile read = new RandomAccessFile("D:\\test\\故事.txt", "r");//r讀模式
        RandomAccessFile write = new RandomAccessFile("故事.txt", "rw");//rw是讀寫模式

        //2.獲取FileChannel通道
        FileChannel inChannel = read.getChannel();
        FileChannel outChannel = write.getChannel();

        //abstract  long size() 返回此通道的文件的當前大小。
        long size = inChannel.size();

        //3.獲取MappedByteBuffer緩衝區
        // abstract  MappedByteBuffer map(FileChannel.MapMode mode, long position, long 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();
            //放到outMap
            outMap.put(b);
        }
        
        outChannel.close();
        inChannel.close();
        write.close();
        read.close();
        

    }
}

小結;只能操作2G以下的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章