java Nio


java Nio

img

1.非直接缓存

2.直接缓冲

电脑蓝屏上面的内容丢失;

3.通道:Channelimg

远古的IO操作:由cpu作为中央处理器,如果应用程序需要读取写入物理硬盘,调用cpu的接口进行读写操作,这样会会使cpu性能急速的下降,不能再更好的处理其他任务了。

改进之后的IO流

img

直接存储器存储DMA

当应用程序发送一个读写请求时,DMA会向CPU申请权限,如果CPU同意,那么接下来的读写IO接口全都由DMA(直接存储器DMA)负责处理操作,建立DMA总线,每一次读写操作都会建立一次连接线;

优点:

? CPU不需要干预,CPU能做更多的事情

缺点:

? 如果是非常大的应用程序,发起大量的读写请求时,需要建立大量的DMA总线,如果总线过多会出现总线冲突的问题,会影响到性能。

再次更新后的IO通道:

img

CPU是中央处理器,而在通道哪一块它也是一块处理器,是独立的,是用于IO操作的,拥有自己传输方式,它附属于CPU。

实际上IO流和通道方式基本上没有什么区别,但是如果是大量的io请求,通道方法会优于io流,因为cpu的利用率更高,连请求权限都不需要申请直接读写操作。

java中使用通道

  • 通道

    源节点和目标节点的连接通道,在java Nio中负责缓冲区数据的传输。通道Channel 本身不存储数据,因此需要配合缓冲区进行传输。

    • ava.nio.channels;

      继承

      img

    通道的间接实现Channels

    java.nio.channels.FileChannel 文件通道
    java.nio.channels.SocketChannel 网络通道
    java.nio.channels.ServerSocketChannel  服务端通道
    java.nio.channels.DatagramChannel 数据通道
    

获取通道有三种方式:

  1. jdk1.4

    通过使用:getChannel()方法

  • 本地IO通道:

    FileInputStream
    FileOutputStream
    RandomAccessFile
    
  • 网路IO通道

    Socket
    ServerScoket
    DatagramSocket
    

? 2.jdk 1.7 对 Nio 改动升级 Nio 2.

? 各个通道之间提供了 open()方法获取通道

? 3.File工具类的nweByteChannel()获取通道

本地IO通道

FileInputStream 文件输入流
FileOutputStream 文件输出流
RandomAccessFile 随机访问文件流

代码实践:

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * 通道(Channel): 源节点和目标节点的连接通道
 */
public class ChannelDemo {

    /**
     * 直接缓冲区,
     * 在复制过程过程中,文件已经复制完毕了,但是java的垃圾回收机制没有及时回收,导致内存映射文件一直占用资源,偶尔会出现
     */
    @Test
    public void directBuffer() throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("3.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.READ);

        // 内存映射文件
        MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());

        byte[] bytes = new byte[inMap.limit()];
        inMap.get(bytes); // 读取
        outMap.put(bytes);// 写入

        inChannel.close();
        outChannel.close();
    }

    /**
     * 非直接缓冲区文件复制
     */
    @Test
    public void inputAndOutput() throws IOException {
        // 利用通道完成读写操作
        FileInputStream fileInputStream = new FileInputStream("1.png");
        FileOutputStream fileOutputStream = new FileOutputStream("2.png");

        // 获取通道
        FileChannel inputChannel = fileInputStream.getChannel();
        FileChannel outputChannel = fileOutputStream.getChannel();

        // 通道不能单独使用,需要配合缓冲区使用
        ByteBuffer buffer = ByteBuffer.allocate(1024); //创建非直接缓冲区

        //将通道中的数据存入缓冲区中
        while (inputChannel.read(buffer) != -1) { // 循环读取
            buffer.flip();//更换为读取模式

            // 将缓冲区的数据写入通道中
            outputChannel.write(buffer);

            buffer.clear();//清空缓冲区
        }

        outputChannel.close();
        inputChannel.close();
        outputChannel.close();
        inputChannel.close();
    }

}

通道之间的数据传输:

	transferFrom()

?	transferTo()

代码:

/**
 * 通道之间的传输
 * */
@Test
public void channel() throws IOException {
    FileChannel inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
    FileChannel outChannel = FileChannel.open(Paths.get("4.png"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.READ);

    // 输入通道 到 输出通道,位置:0开始, 要传输的最大字节数,目标通道
    inChannel.transferTo(0,inChannel.size(),outChannel);
    outChannel.transferFrom(inChannel,0,inChannel.size());
    outChannel.close();
    inChannel.close();

}

分散(Scatter)和与聚集(Gather)

分散读取

img

通道的数据 依次填满到缓冲区中

@Test
public void access() throws IOException {
    //创建一个随机访问文件流,以从具有指定名称的文件中读取和写入(可选)。
    RandomAccessFile raf = new RandomAccessFile("F:\\01-javaSE\\Vim编辑器基本命令.md", "rw");

    // 获取管道
    FileChannel channel = raf.getChannel();

    // 创建多个缓冲区
    ByteBuffer allocate1 = ByteBuffer.allocate(10);
    ByteBuffer allocate2 = ByteBuffer.allocate(514);
    ByteBuffer allocate3 = ByteBuffer.allocate(1024);

    // 分散读取文件
    ByteBuffer[] bbs= {allocate1,allocate2,allocate3};
    channel.read(bbs);// 读入缓冲区中
    
    //查看缓冲区内容
    for (ByteBuffer bb :
            bbs) {
        bb.flip();//读模式
        System.out.println("-----------------------容量为:>"+bb.capacity()+"的内容");
        System.out.println(new String(bb.array(),0,bb.limit()));
    }
    
    channel.close();
    raf.close();
}

输出内容:

-----------------------容量为:>10的内容
# i	Vim基
-----------------------容量为:>514的内容
本命令

## vi/vim基本命令表

下面为vi编辑器常用的命令,仅供参考。 相关资料:[http://www.vim.org/docs.php]
vi/vim帮助文档:[http://vimdoc.sourceforge.net/htmldoc/help.html]

|    命令     | 描述                                                     |
| :---------: | :------------------------------------------------------- |
|     vi      | 从控制台进入vi编辑器                                     |
| vi filename | 创建名为filename的文件并进入vi?
-----------------------容量为:>1024的内容
?辑器                     |
| 命令行模式  |                                                          |
|      i      | 从光标所在字符前插入                                     |
|      a      | 从光标所在的字符后插入                                   |
|      o      | 从光标所在行的下面插入空白行                             |
|      I      | 从光标所在行的行首插入                                   |
|      A      | 从光标所在行的行末插入                                   |
|      O      | 从光标所在行的上面插入空白行                             |
|      s      | 删除光标所在字符进入插入模式                             |
|      S      | 删除光标所在行进入插入模式                               |
|     Esc     | 插入模式切换到命令行模式                                 |
|      k      | 类似方向键上                                             |
|      j      | 类似方向键下  

Process finished with exit code 0

聚集写入

img

按照顺序依次写入通道中

    /**
     *  聚集写入
     * */
    @Test
    public void Gather() throws IOException {
        //创建一个随机访问文件流,以从具有指定名称的文件中读取和写入(可选)。
        RandomAccessFile raf = new RandomAccessFile("G:\\Desktop\\shop-1.3.sql", "rw");
        // 获取管道
        FileChannel channel = raf.getChannel();

        // 创建多个缓冲区
        ByteBuffer allocate1 = ByteBuffer.allocate(10);
        ByteBuffer allocate2 = ByteBuffer.allocate(514);
        ByteBuffer allocate3 = ByteBuffer.allocate(1024);
        // 分散读取文件
        ByteBuffer[] bbs= {allocate1,allocate2,allocate3};
        channel.read(bbs);// 读入缓冲区中
        for(ByteBuffer bb: bbs){
            bb.flip();
            System.out.println("----------->容量为:"+bb.capacity()+"缓冲区");
            System.out.println(new String(bb.array(),0,bb.limit()));
        }

        RandomAccessFile rw = new RandomAccessFile("1.sql", "rwd");
        //
        FileChannel fileChannel = rw.getChannel();
        fileChannel.write(bbs);

        fileChannel.close();
        rw.close();
        channel.close();
        raf.close();
    }

使用 RandomAccessFile rw = new RandomAccessFile("1.sql", "rw"); ,不知道为什么,大部分写入不成功。

操作字符集:charset

  • 编码:字符串 -> 字节数组
  • 解码:字节数组 -> 字符串
/**
 * 字符集
 * */
@Test
public void charset(){
    Map map = Charset.availableCharsets();//此方法返回的映射对于当前 Java 虚拟机中可用的每个字符集都有一个条目
    Set> set = map.entrySet(); // 返回此映射中包含的映射的Set视图
    for (Map.Entry entry: set){
        System.out.println(entry.getKey()+ "="+entry.getValue());
    }

java 可以进行编码和解码的字符集

Big5=Big5
Big5-HKSCS=Big5-HKSCS
CESU-8=CESU-8
EUC-JP=EUC-JP
EUC-KR=EUC-KR
GB18030=GB18030
GB2312=GB2312
GBK=GBK
IBM-Thai=IBM-Thai
IBM00858=IBM00858
......

根据字符集进行编码和解码

/**
 *  编码器 -> 编码
 * */
@Test
public void charsetCoding() throws CharacterCodingException {
    Charset gbk = Charset.forName("GBK"); //获取字符集,进行对应规则编码
    CharsetEncoder encoder = gbk.newEncoder(); //为这个字符集构造一个新的编码器
    // 字符缓冲区
    CharBuffer cb = CharBuffer.allocate(1024);
    cb.put("这个一段字符进行编码");
    cb.flip(); //读模式,不是读模式无法编码,解码

    ByteBuffer bb = encoder.encode(cb); // 进行编码,其实就是转成字节
    // 查看编码后的内容
    System.out.println("编码------------------》");
    for (int i = 0; i < bb.limit(); i++) {
        System.out.println(bb.get());

    }
    bb.flip(); //读模式
    // 解码
    CharsetDecoder decoder = gbk.newDecoder();//为这个字符集构造一个新的解码器
    CharBuffer cbcoder = decoder.decode(bb);

    // 查看解码后的内容
    System.out.println("解码------------------》");
    for (int i = 0; i < cbcoder.limit(); i++) {
        System.out.println(cbcoder.get());
    }
    
}

如果字符集和GBK 解码时 UTF-8 就会出现乱码,使用GBK解码就正常显示

网路IO通道

Socket 客户端
ServerScoket 服务端
DatagramSocket 数据报套接字

相关