NIO:缓冲区(Buffer);通道(Channel);Files 类常用方法;Path 与 Paths

NIO

Java NIO ( New IO )是从 Java 1.4 版本开始引入的一个新的 IO API , 可以替代标准的 Java IO API 。NIO 与原来的 IO 有同样的作用和目的,但是使用的方式完全不同, NIO 支持面向缓冲区的、基于通道的 IO 操作。 NIO 将以更加高效的方式进行文件的读写操作。

一、缓冲区(Buffer)

1、Buffer概述

	缓冲区( Buffer ):一个用于特定基本数据类型的容器,底层由数组实现,用来存储数据。	
	非直接缓冲区:将缓冲区建立在JVM的内存中
    直接缓冲区:将缓冲区建立在物理内存中
	根据数据类型不同 (boolean 除外 ) ,有以下 Buffer子类:
    ByteBuffer(最常用)
    CharBuffer
    ShortBuffer
    IntBuffer
    LongBuffer
    FloatBuffer
    DoubleBuffer
    这些类都采用相似的方法进行管理数据,只是各自管理的数据类型不同。
    XxxBuffer xxxBuffer=XxxBuffer.allocate(int capacity)//创建一个容量为capacity的非直接缓冲区
    XxxBuffer xxxBuffer1 = XxxBuffer.allocateDirect(int capacity);//创建一个容量为capacity的直接缓冲区

2、缓冲区基本属性

   容量 (capacity) : 表示 Buffer 最大数据容量,缓冲区容量不能为负,并且创建后不能更改。

   限制 (limit) : 第一个不能读取或写入的数据的索引,即位于 limit 后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量。

   位置 (position) : 下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制

   标记 (mark) 与 重置 (reset) : 标记是一个索引,通过 Buffer 中的 mark() 方法指定 Buffer 中一个特定的 position ,之后可以通过调用 reset() 方法恢复到这个 position.
    
标记、位置、限制、容量遵守以下不变式:
0 <= mark <= position <= limit <= capacity

3、缓冲区常用方法

put(byte b) : 将给定单个字节写入缓冲区的当前位置
put(byte[] src) :将 src 中的字节写入缓冲区的当前位置
put(int index, byte b) :将指定字节写入缓冲区的索引位置 ( 不会移动 position)
get() : 读取缓冲区中的数据
get(byte[] dst) :批量读取多个字节到 dst 中
get(int index) :读取指定索引位置的字节 ( 不会移动 position)
flip(); 切换读取数据模式
rewind() : 可重复读
clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,只是将缓冲区所有基本属性回到初始状态
mark() : 标记一个位置
reset():回到上次标记位置
hasRemaining();//判断是否还有下一个可读元素
remaining()//获取还有几个元素可读

4、演示

import java.nio.ByteBuffer;

public class Blog {
    public static void main(String[] args) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);//创建一个容量为10的缓冲区
        int capacity = byteBuffer.capacity();//获取容量
        System.out.println(capacity);//10
        int position = byteBuffer.position();//获取当前指针位置
        System.out.println(position);//0
        int limit = byteBuffer.limit();//获取当前限制位置
        System.out.println(limit);//10

        byteBuffer.put("abcde".getBytes());//存入数据
        int position1 = byteBuffer.position();//获取当前指针位置
        System.out.println(position1);//5

        byteBuffer.flip();//切换到读取模式
        int position2 = byteBuffer.position();//获取当前指针位置
        System.out.println(position2);//0
        int limit1 = byteBuffer.limit();//获取当前限制位置
        System.out.println(limit1);//5

        byteBuffer.mark();//标记当前指针位置
        //读取数据
        while (byteBuffer.hasRemaining()){
            byte b = byteBuffer.get();//获取数据
            System.out.println(b);//91~101
        }
        
        byteBuffer.reset();//让指针回到上次标记位置
        //重复读取数据
        while (byteBuffer.hasRemaining()){
            byte b = byteBuffer.get();//获取数据
            System.out.println(b);//91~101
        }
        
        byteBuffer.rewind();//重绕此缓冲区,将位置设为为0,取消设置的mark,可实现重新读取
    }
}

在这里插入图片描述

二、通道(Channel)

1、Channel概述

Java 为 Channel 接口提供的最主要实现类如下:

本地文件传输通道:

FileChannel :用于读取、写入、映射和操作文件的通道

网络数据传输的通道:

DatagramChannel :通过 UDP 读写网络中的数据通道

SocketChannel :通过 TCP 读写网络中的数据。

ServerSocketChannel :可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个SocketChannel

2、获取通道

方式一:使用支持通道的类的对象调用getChannel() 方法

支持通道的类:
	本地I/O:
        FileInputStream:
        FileInputStream fileInputStream = new FileInputStream("MyTest.java");
        FileChannel channel = fileInputStream.getChannel();
        
		FileOutputStream:
        FileOutputStream fileOutputStream = new FileOutputStream("MyTest.java");
        FileChannel channel1 = fileInputStream.getChannel();

        RandomAccessFile:
        RandomAccessFile randomAccessFile = new RandomAccessFile("MyTest.java", "rw");
        FileChannel channel2 = randomAccessFile.getChannel();
	网络 I/O:
        DatagramSocket
        Socket
        ServerSocket

方式二:使用 Files 类的静态方法newByteChannel() 获取字节通道

SeekableByteChannel inChannel = Files.newByteChannel(Paths.get("MyTest.java"), StandardOpenOption.READ);
SeekableByteChannel outChannel = Files.newByteChannel(Paths.get("MyTest2.java"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
//模式:
//StandardOpenOption.READ  可读
//StandardOpenOption.WRITE  可写
//StandardOpenOption.CREATE 文件不存在就创建,存在就覆盖
//StandardOpenOption.CREATE_NEW 文件不存在就创建,存在就报错

方式三:通道的静态方法 open() 打开并返回指定通道

FileChannel inChannel = FileChannel.open(Paths.get("MyTest.java"),StandardOpenOption.READ);
FileChannel outChannl = FileChannel.open(Paths.get("MyTest2.java"),StandardOpenOption.WRITE, StandardOpenOption.CREATE);

3、通道的数据传输

将 Buffer 中数据写入 Channel:  inChannel.read(byteBuffer)
	
从 Channel 读取数据到 Buffer:  outChannel.write(byteBuffer);

4、NIO复制文件

A:使用非直接缓冲区复制文件

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

public class CopyFile1 {
    public static void main(String[] args) throws IOException {
        FileInputStream in = new FileInputStream("MyTest.java");
        FileOutputStream out = new FileOutputStream("MyTest3.java");
        //获取通道
        FileChannel inChannel = in.getChannel();
        FileChannel outChannel = out.getChannel();
        //创建非直接缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 8);
        //将通道数据放进缓冲区
        while ((inChannel.read(byteBuffer))!=-1){
            byteBuffer.flip();//切换到读取模式
            outChannel.write(byteBuffer);//将缓冲区数据写入通道
            byteBuffer.clear();//清空缓冲区
        }
        //释放资源
        in.close();
        out.close();
        inChannel.close();
        outChannel.close();
    }
}

B:使用直接缓冲区复制文件

import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class CopyFile2 {
    public static void main(String[] args) throws IOException {
        //获取通道
        FileChannel inChannel = FileChannel.open(Paths.get("MyTest.java"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("MyTest4.java"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
        //操作内存映射文件
        MappedByteBuffer inByteBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outByteBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
        //直接使用缓冲区读写文件
        byte[] bytes = new byte[inByteBuffer.limit()];
        inByteBuffer.get(bytes);
        outByteBuffer.put(bytes);
        //释放资源
        inChannel.close();
        outChannel.close();
    }
}

5、通道之间的数据传输

/*通道之间的数据传输,使用直接缓冲区的方式
         * transferFrom()
         * transferTo()*/
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class MyTest {
    public static void main(String[] args) throws IOException {
        FileChannel inChannel = FileChannel.open(Paths.get("MyTest.java"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("MyTest5.java"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        //以下两种数据传输方式任选一种即可
        //将inChannel复制到outChannel
        inChannel.transferTo(0, inChannel.size(), outChannel);
        //outChannel的数据来自inChannel
        //outChannel.transferFrom(inChannel, 0, inChannel.size());
    }
}

三、Files 类常用方法

1、复制文件的方法

static long copy(InputStream in, Path target, CopyOption... options) 
//将所有字节从输入流复制到文件。 
Files.copy(new FileInputStream("demo.txt"), Paths.get("demo55.txt"), StandardCopyOption.REPLACE_EXISTING);

static long copy(Path source, OutputStream out) 
//将从文件到输出流的所有字节复制到输出流中。
Files.copy(Paths.get("demo55.txt"),new FileOutputStream("demo66.txt"));

static Path copy(Path source, Path target, CopyOption... options) 
//将一个文件复制到目标文件。  
Files.copy(Paths.get("demo.txt"), Paths.get("demo77.txt"), StandardCopyOption.REPLACE_EXISTING);
//StandardCopyOption.REPLACE_EXISTING 可选参数,复制文件,如果文件存在就覆盖

2、其他方法

Path createDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录

Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件

void delete(Path path) : 删除一个文件

Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置

ong size(Path path) : 返回 path 指定文件的大小
	
static Path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)  可以将List集合中的数据写到文件中

Files用于判断的方法:
	 boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
	 boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
	 boolean isExecutable(Path path) : 判断是否是可执行文件
	 boolean isHidden(Path path) : 判断是否是隐藏文件
	 boolean isReadable(Path path) : 判断文件是否可读
	 boolean isWritable(Path path) : 判断文件是否可写
	 boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
Files用于操作内容的方法:
	SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
	DirectoryStream newDirectoryStream(Path path) : 打开 path 指定的目录
	InputStream newInputStream(Path path, OpenOption…how): 获取 InputStream 对象
	OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象	 

四、Path 与 Paths

java.nio.file.Path 接口代表一个平台无关的平台路径,描述了目录结构中文件的位置。

Paths 提供的get()方法用来获取Path对象

Path get(String first,String… more): 用于将多个字符串串连成路径

Path 常用方法:
boolean endsWith(String path) : 判断是否以 path 路径结束
boolean startsWith(String path) : 判断是否以 path 路径开始
boolean isAbsolute() : 判断是否是绝对路径
Path getFileName() : 返回与调用 Path 对象关联的文件名
Path getName(int idx) : 返回的指定索引位置 idx 的路径名称
int getNameCount() : 返回 Path 根目录后面元素的数量
Path getParent() :返回 Path 对象包含整个路径,不包含 Path 对象指定的文件路径
Path getRoot() :返回调用 Path 对象的根路径
Path resolve(Path p) : 将相对路径解析为绝对路径
Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
String toString() : 返回调用 Path 对象的字符串表示形式
发布了55 篇原创文章 · 获赞 23 · 访问量 4348

猜你喜欢

转载自blog.csdn.net/y_Engineer/article/details/97618461
今日推荐