首页 > 其他分享 >JTCR-探究 NIO-20

JTCR-探究 NIO-20

时间:2024-04-24 14:35:08浏览次数:21  
标签:Files JTCR 20 NIO 文件 static 缓冲区 Path 方法

nio 支持面向缓冲区、基于通道(channel-based)的 I/O 操作方法。

JDK9 开始,包 java.nio 及其子包位于 java.base 模块。

nio 子系统不是为了取代面向流的 I/O 类。

NIO 基础

nio 系统构建在缓冲区(buffers)和通道之上。缓冲区用于存放数据,通道表示一个已打开的与 I/O 设备的连接。通过使用缓冲区,按需存入或取出数据。

所有的 buffers 都是 Buffer 类的子类,Buffer 类定义了 buffers 的核心功能:当前位置、limit 和容量。当前位置表示缓冲区中下一个将读取、写入数据的索引位置,limit 表示缓冲区最后一个有效索引的下一个索引位置,容量表示缓冲区可存放的元素数量。通常,limit 和容量相同。Buffer 也支持 mark()reset() 方法。

Buffer 类的子类如下

  • ByteBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • CharBuffer
  • FloatBuffer
  • DoubleBuffer
  • MappedByteBuffer

MappedByteBuffer 类用于将一个文件映射到缓冲区。

上述类都提供了 get() 从缓冲区获取数据,put() 将数据存放到缓冲区。如果缓冲区只读,put() 方法不可用。allocate() 用于手动分配缓冲区,wrap() 将数组包装成缓冲区,slice() 创建缓冲区子序列。

通道表示一个已打开(open connection)的与 I/O 源或目标的连接。通道实现了 Channel 接口,该接口扩展了 Closeable 接口,从而扩展了 AutoCloseable 接口。可以被 try-with-resources 语句管理。

获得通道的一种方式是调用 getChannel() 方法,支持该方法的 I/O 类如下

  • DatagramSocket
  • FileInputStream
  • FileOutputStream
  • RandomAccessFile
  • ServerSocket
  • Socket

调用 getChannel() 返回的信道类型取决于调用对象的类型,比如 FileInputStream、FileOutputStream 和 RandomAccessFile 调用该方法返回 FileChannel 类型,Socket 调用该方法返回 SocketChannel 类型。

获得通道的另一个方法是使用 Files 类提供的静态方法。调用 newByteChannel() 返回字节信道 SeekableByteChannel 类型,SeekableByteChannel 接口由 FileChannel 实现。

信道提供的 read()write() 方法用于执行 I/O 操作。

所有信道提供了访问和控制信道的方法。FileChannel 提供获取、设置当前索引的方法,文件信道之间传输信息,获得当前信道大小,锁信道等方法。FileChannel 提供的静态方法 open() 打开一个文件然后返回关联的信道,提供的 map() 方法将文件映射到缓冲区。

nio 使用的另两个实体是 charsets 和 selectors,charset 定义了字符到字节的映射关系,可以使用编码器将字符编码得到字节,可以使用解码器将字节解码得到字符。

selector 支持基于键的、非阻塞的多 I/O。

NIO.2

Path 接口封装文件路径,它继承的接口有:Watchable、Iterable<Path> 和 Comparable<Path>。其中,Watchable 接口描述监控状态是否发生改变的对象。

Path getName(int index) 方法返回 Path 接口中文件路径中单个路径元素,索引 0 表示根路径,依此类推。int getNameCount() 方法返回 Path 接口中路径元素的个数。Path resolve(Path path) 方法返回 path 的绝对路径。

JDK11 开始,静态工厂方法 of() 接收一个路径名或 URI,返回 Path 对象。

File 对象的 toPath() 方法可以将 File 对象转成 Path 对象,Path 对象的 toFile() 方法可以将 Path 对象转成 File 对象。

Files 类的静态方法用于处理 Path 对象表示的文件。它的 list()walk()lines()find() 方法返回 Stream 对象。JDK11 开始,引入的 readString() 方法将文件内容以字符串的方式返回,writeString() 方法将 CharSequence 对象写入文件。

Files 类部分方法的参数为 OpenOption 接口,该接口表示以何种方式打开文件。它的一个实现类为 StandardOpenOption 类,定义的枚举值如下

含义
APPEND 在文件末尾开始写入
CREATE 文件不存在时创建
CREATE_NEW 仅当文件不存在时创建
DELETE_ON_CLOSE 文件关闭时删除
DSYNC 改变文件内容时直接写入存储设备
READ 只读
SPARSE 表示文件系统稀疏存储文件
SYNC 改变文件内容或其元数据时直接写入存储设备
TRUNCATE_EXISTING 已存在的文件打开时,清空文件内容
WRITE

在 JDK11 之前,获得 Path 对象可以通过调用 Paths 类的 get() 方法。

// 拼接所有参数返回对应的 Path 对象
static Path get(String pathName, String... parts);

static Path get(URI uri);

JDK11 引入 Path 的 of() 方法用于获得 Path 对象,上述的 get() 方法在 JDK11 之后不推荐使用。

Path 对象仅表示一个文件的路径,不代表打开或者创建文件。

nio 使用多个接口表示文件属性,处于顶层的接口是 BasicFileAttributes 接口,它有两个子接口:DosFileAttributes 接口表示 FAT 文件系统的文件属性、PosixFileAttributes 接口表示符合 POSIX 标准的文件属性。

Files 类的静态方法 readAttributes() 返回表示文件属性的对象。它的形式之一为

/* 
	返回的文件属性对象与 path 指定路径的文件关联,具体的文件属性类型由
	attrType 决定,取值为 BasicFileAttributes.class、
	DosFileAttributes.class 和 PosixFileAttributes.class 三者之一
	opts 默认值为符号链接
*/
static <A extends BasicFileAttributes>
  A readAttributes(Path path, Class<A> attrType, LinkOption... opts);

Files 类的 getFileAttributeView() 方法返回的对象也可以访问文件属性。nio 定义的属性视图接口包括:AttributeViewBasicFileAttributeViewDosFileAttributeViewPosixFileAttributeView 接口等。

Files 类的部分静态方法也可以访问文件属性。

不是所有的文件系统支持全部的文件属性。

使用 FileSystemFileSystems 类可以访问文件系统,FileSystems 类的 newFileSystem() 返回一个新的文件系统。FileStore 类封装文件存储系统。

NIO 系统的使用

nio 的使用划分为 3 个类别

  • 基于 channel 的 I/O
  • 基于流的 I/O
  • 路径和文件系统操作

基于 channel 的 I/O

调用 Files.newByteChannel(Path path) 返回一个与 path 指定文件关联的 SeekableByteChannel 接口对象,默认情况下该对象的实际类型为实现了 SeekableByteChannel 接口的 FileChannel 类,因此可以转换为该类类型。SeekableByteChannel 接口封装了用于文件操作的 channel。

// path 表示文件, how 表示打开文件的方式,默认情况下以只读的方式打开文件
static SeekableByteChannel newByteChannel(Path path, OpenOption... how)

channel 的使用需要一个缓冲区,要么通过将一个已有数组包装为缓冲区要么动态分配一个缓冲区。ByteBufferallocate() 方法获得一个缓冲区。

static ByteBuffer allocate(int capacity)

channel 调用 read() 方法从文件中读取数据填入缓冲区。

// 返回实际读取的字节个数,读取文件末尾时返回 -1
int read(ByteBuffer buf)
public static void m() {
  int count = -1;
  // 获取与文件关联的 channel
  try (SeekableByteChannel fC = Files.newByteChannel(Path.of("test.txt"))) {
    // 获取缓冲区
    var buf = ByteBuffer.allocate(128);
    do {
      // channel 从文件读取数据直到填满缓冲区
      count = fC.read(buf);
      // 将缓冲区指针从末尾回退到起始位置
      buf.rewind();
      for (int i = 0; i < count; i++) {
        System.out.print((char)buf.get());
      }
    } while (count != -1);
    System.out.println();
  }
}

使用 channel 的另一种方式是将文件内容映射到一个缓冲区,然后使用这个缓冲区即可达到使用文件的目的。

该方式的步骤和前一种方法不同在于,newByteChannel() 方法返回的对象需要转换成 FileChannel 类,然后调用该类的 map() 方法达到映射的目的。

// start 表示从文件中开始映射的位置,size 表示映射的字节数量
MappedByteBuffer map(FileChannel.MapMode how, long start, long size)

how 的取值有如下三种

  • MapMode.READ_ONLY 只读
  • MapMode.READ_WRITE 读写
  • MapMode.PRIVATE 将文件内容私有复制到缓冲区,缓冲区内容的改变不会影响文件的内容

map() 方法返回的 MappedByteBuffer 类是 ByteBuffer 的子类。

public static void m() {
  try(var fC = (FileChannel)Files.newByteChannel(Path.of("test.txt"))) {
    // 获取关联的文件字符个数
    long size = fC.size();
    var buf = fC.map(FileChannel.MapMode.READ_ONLY, 0, size);
    for (int i = 0; i < size; i++) {
      System.out.print((char)buf.get());
    }
    System.out.println();
  }
}

通过 channel 将数据写入文件和从文件读取数据有一些不同之处。调用 newByteChannel(Path path, OpenOption... how) 方法时将 how 指定为 StandardOpenOption.WRITE 表示以写入的方式打开文件;指定为 StandardOpenOption.CREATE 表示当文件不存在时创建文件。newByteChannel() 方法返回的对象需要转换成 FileChannel 类型,然后调用 channel 的 write() 方法。该方法接收一个缓冲区,将缓冲区中的内容写入到与 channel 关联的文件中。

public static void m() {
  try (var fC = (FileChannel)Files.newByteChannel(Path.of("test.txt"),
                                                 StandardOpenOption.WRITE,
                                                 StandardOpenOption.CREATE)) {
    var buf = ByteBuffer.allocate(26);
    for (int i = 0; i < 26; i++) {
      buf.put((byte)('a' + i));
    }
    buf.rewind();
    // 关联的文件已存在时,文件的前 26 个字节被覆盖重写,其他字节不变
    fC.write(buf);
  }
}

注意:每次操作缓冲区时,不论读还是写,当前缓冲区的指针都会移动。

将 channel 关联的文件与缓冲区映射后,缓冲区中的内容会自动写入到文件中。

public static void m() {
  try (var fC = (FileChannel)Files.newByteChannel(Path.of("test.txt"),
                                                 StandardOpenOption.WRITE,
                                                 StandardOpenOption.CREATE,
                                                 StandardOpenOption.READ)) {
    // 必须以读写的方式映射才能写入
    var buf = fC.map(FileChannel.MapMode.READ_WRITE, 0, 26);
    for (int i = 0; i < 26; i++) {
      buf.put((byte)('a' + i));
    }
  }
}

Files 类的 copy() 方法用于复制文件。

static Path copy(Path src, Path dest, CopyOption... how)

其中,how 决定了文件该如何复制。下面 how 的三种取值对于任何文件系统都适用

  • StandardCopyOption.COPY_ATTRIBUTES 文件的属性也一起复制
  • StandardLinkOption.NOFLLOW_LINKS 不使用符号链接
  • StandardCopyOption.REPLACE_EXISTING 重写已存在的文件
public static void m(String[] args) {
  var source = Path.of(args[0]);
  var target = Path.of(args[1]);
  Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
}

基于流的 I/O

Files 类的 newInputStream()newOutputStream() 方法接收一个 Path 对象,返回与该对象表示的文件关联的流。返回的流具有 nio 提供的特性。

static InputStream newInputStream(Path path, OpenOption... how)

how 的默认取值是 StandardOpenOption.READ。

public static void m() {
  try (var is = Files.newInputStream(Path.of("test.txt"))) {
    int i = -1;
    do {
      i = is.read();
      if (i != -1) {
        System.out.print((char)i)
      }
    } while (i != -1);
  }
}
static InputStream newOutputStream(Path path, OpenOption... how)

how 的默认取值为 StandardOpenOption.WRITE、StandardOpenOption.CREATE 和 StandardOpenOption.TRUNCATE_EXISTING。

public static void m() {
  try (var bos = new BufferedOutputStream(new OutputStream(Path.of("test.txt")))) {
    for (int i = 0; i < 26; i++) {
      bos.wirte((byte)('a' + i));
    }
  }
}

路径和文件系统操作

Path 对象的部分方法可以获取文件的路径相关属性、Files 类的部分静态方法接收一个 Path 类型参数,获取该 path 表示的文件属性。Files.readAttributes(Path) 方法返回一个 BasicFileAttributes 对象,该对象可以获取 path 表示的文件属性。

Files 类的静态方法可以读取表示目录的 Path 对象的目录内容。

static DirectoryStream<Path> newDirectoryStream(Path dir)

dir 表示目录时,newDirectoryStream() 方法返回的 DirectoryStream<Path> 对象用于获取目录中的内容。DirectoryStream<Path> 实现了 AutoCloseable 和 Iterable<Path> 接口。DirectoryStream<Path> 实现的迭代器仅能使用一次。

public static void m() {
  try (var ds = Files.newDirectoryStream(Path.of("/dir"))) {
    for (Path e : ds) {
      var attr = Files.readAttributes(e, BasicFileAttributes.class);
      if (attr.isDirectory()) {
        System.out.print("dir: ");
      } else {
        System.out.print("     ");
      }
      System.out.println(e.getName(1));
    }
  }
}

过滤目录中的内容有两种方式。第一种

static DirectoryStream<Path> newDirectoryStream(Path dir, String wildcard)

根据 wildcard 表示的文件名或者一个通配符来过滤目录中的内容。

// 保留 dir 目录中以 a 或 b 开头,以 java 或 c 为后缀的文件
Files.newDirectoryStream(Path.of("/dir"), "{a,b}*.{java, c}")

第二种

static DirectoryStream<Path>
  newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)

DirectoryStream.Filter 接口有 boolean accept(T e) 方法,方法返回 true 则保留内容,返回 false 则去除。

public static void m() {
  var how = new DirectoryStream.Filter<Path>() {
    // 过滤掉不可写的文件
    public boolean accept(Path name) throws IOException {
      if (Files.isWritable(name)) {
        return true;
      }
      return false;
    }
  };
  try (var ds = Files.newDirectoryStream(Path.of("/dir"), how)) {
    for (Path e : ds) {
      System.out.println(e.getName(1));
    }
  }
}

Filter 类的 walkFileTree() 方法用于遍历一个目录中所有的文件和子目录及子目录中的文件和目录。之前只遍历一层目录。

static Path walkFileTree(Path root, FileVisitor<? super Path> fv)

fv 是 FileVisitor 接口类型对象,控制目录树如何遍历。

interface FileVisitor<T>

该接口有如下方法

  • FileVisitResult postVisitDirectory(T dir, IOException e) 访问目录后调用
  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) 访问目录前调用
  • FileVisitResult visitFile(T file, BasicFileAttributes attrs) 访问文件时调用
  • FileVisitResult visitFileFailed(T dir, IOException e) 访问文件失败时调用

上述方法返回 FileVisitResult 枚举类型,它定义了四个枚举变量

  • CONTINUE 返回该值表示继续遍历
  • SKIP_SIBLINGS 在 preVisitDirectory() 方法中返回时跳过该目录的兄弟节点,阻止 postVisitDirectory() 方法的调用
  • SKIP_SUBTREE 在 preVisitDirectory() 方法中返回时仅跳过目录和子目录
  • TERMINATE 返回时停止遍历

SimpleFileVisitor 类实现了 FileVisitor 接口,自定义遍历时进行操作只需重写该类的特定方法。

class AFV extends SimpleFileVisitor<Path> {
  public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) 
    throws IOException  {
    System.out.println(path);
    return FileVisitResult.CONTINUE;
  }
}

class Demo {
  public static void main(String[] args) {
    Files.walkFileTree(Path.of("/dir"), new AFV());
  }
}

参考

[1] Herbert Schildt, Java The Complete Reference 11th, 2019.

标签:Files,JTCR,20,NIO,文件,static,缓冲区,Path,方法
From: https://www.cnblogs.com/xdreamc/p/17506464.html

相关文章

  • 1名工程师轻松管理20个工作流,创业企业用Serverless 让数据处理流程提效
    北京语势科技有限公司成立于2023年6月,语势科技定位为“智能投资时代的主题入口”,在资管行业从以机构为核心转向以用户为核心的变革时代,通过打造主题投资引擎,赋能普惠投资一体化,打造以投资者和资管机构为主题和核心、自然语言交互形式为入口的“新桥梁”。语势科技日均处理万条金......
  • PeLK:101 x 101 的超大卷积网络,同参数量下反超 ViT | CVPR 2024
    最近,有一些大型内核卷积网络的研究,但考虑到卷积的平方复杂度,扩大内核会带来大量的参数,继而引发严重的优化问题。受人类视觉的启发,论文提出了外围卷积,通过参数共享将卷积的复杂性从\(O(K^{2})\)降低到\(O(\mathrm{log}K)\),有效减少90%以上的参数数量并设法将内核尺寸扩大到......
  • 2024/4/23中考一模vp游记
    你说得对,但是这次和月考不一样,这次考试当天我才知道要考试。day1晚上四节自习课考两场试。典中典之第一场语文。考试前打桌足,守门员踢进自家球门\(6\)个。并且连输两把。/ll\(5:50\)开始考试。但是我\(5:49\)进考场,然后找不到座位又回教室看了座位表。幸好一车人迟到所......
  • 挑战前端基础120题--垃圾回收机制?
    1.什么是垃圾回收机制?简称GC,内存管理自动运行,找到不被使用的变量,进行垃圾回收释放内存;补充点:JS变量分为:全局变量&局部变量;全局变量的生命周期是一直存在直到页面被销毁。局部变量一般定义在函数内部,他的生命周期从函数被调用开始,直到函数运行结束。等到局部变量不被使用,就......
  • The 2022 ICPC Asia Xian Regional Contest / ICPC 西安 2022 (ABDHJKL)
    本文搬运自本人的知乎文章。https://zhuanlan.zhihu.com/p/588162564好久没有在补题之后写题解的习惯了。但是最近感觉有些题目的思路即使在题目通过后仍然难以理清,因此觉得需要写些东西帮助自己整理思路,另外也方便以后翻看积累到的技巧。J.StrangeSum题目链接Problem-J......
  • 2022 China Collegiate Programming Contest (CCPC) Mianyang | 2022 CCPC 绵阳(MAED
    搬运自本人知乎文章。https://zhuanlan.zhihu.com/p/588646549M.Rock-Paper-ScissorsPyramid题目链接Problem-M-Codeforces题意有一个长度为\(n\)的石头剪刀布序列,每个元素是RPS(石头、布、剪刀)中的一个,我们需要用这个序列构造一个三角,三角的底层为这个序列,第\(i(......
  • [CSP-J2019] 数字游戏
    [CSP-J2019]数字游戏题目描述小K同学向小P同学发送了一个长度为8的01字符串来玩数字游戏,小P同学想要知道字符串中究竟有多少个1。注意:01字符串为每一个字符是0或者1的字符串,如101为一个长度为3的01字符串。输入格式输入文件只有一行,一个长度为8的0......
  • 【2024-04-23】夫妻默契
    20:00一本书,当未读之前,你会感到书是那么.....但是当我们对书的内容真正有了透彻的了解,抓住了全书的要点,掌握了全书的精神实质以后,就会感到书本变薄了。                                       ......
  • 第二届先进传感与智能系统国际会议(ICASIS 2024)
    第二届先进传感与智能系统国际会议(ICASIS2024)日期:2024年6月22-23日地点:中国昆明会议亮点:前沿议题:聚焦先进传感技术、智能传感、物联网、大数据分析与人工智能等核心话题。全球交流:与全球顶级学者、行业领袖面对面,共同探讨行业趋势与未来挑战。有中国工程院外籍院士Prof.......
  • 第20章 高级Web服务特性
    1准备工作添加SuppliersController控制器。[ApiController][Route("api/[controller]")]publicclassSuppliersController:ControllerBase{privateDataContext_context;publicSuppliersController(DataContextdataContext)......