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以下的

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