文章来源:http://cucaracha.iteye.com/blog/2041847

对文件来说,可能最常用的操作就是创建、读取和写出。NIO.2 提供了丰富的方法来完成这些任务。本文从简单的小文件操作开始,最后以缓冲和非缓冲流的操作结束。

流分为输入流和输出流(可以输出到任何地方,比如硬盘或内存)。流支持不同类型的数据,比如字符串、字节、原始数据类型、本地化字符、对象等。使用非缓冲流,读和写的操作直接依赖底层文件系统,使用缓冲流,数据从内存的缓冲区读取,只有缓冲区空了之后才会调用本地方法进行读取。同样,缓冲输出流也是先将数据写出缓冲区,当缓冲区满了之后才会使用本地方法写出。如果没有等到缓冲区满就需要写出数据,那么可以使用 flush 方法。

使用标准参数 OpenOption

在 NIO.2 中,创建、读取和写出都支持一个可选的 OpenOption 参数,使用它来设置怎样打开和读取文件。实际上,OpenOption 是 java.nio.file 包中的一个接口,它有两个实现:LinkOption (还记得著名的 NOFOLLOW_LINKS 枚举常量吗?)和 StandardOpenOption 类。StandardOpenOption 提供了以下的枚举常量:

  • READ - 打开文件进行读取访问
  • WRITE - 打开文件进行写出访问
  • CREATE - 如果文件不存在则创建新文件
  • CREATE_NEW - 创建新文件,如果文件已存在,则抛出异常
  • APPPEND - 在文件末尾添加数据(与 WRITE 和 CREATE 结合使用)
  • DELETE_ON_CLOSE - 当流关闭的时候删除文件(用于删除临时文件)
  • TRUNCATE_EXISTING - 将文件截断为 0 个字节(与 WRITE 结合使用)
  • SPARSE - 新创建的文件是 Sparse 文件
  • SYNC - 保持文件内容和元数据同步
  • DSYNC - 保持文件内容异步

在本文中,将会演示一些上面的枚举常量的用法。

创建新文件

可以使用 Files.createFile() 方法来创建新文件,这个方法接受一个 Path 类型的参数,并且接受一个可选的 FileAttribute<?> 参数用于在创建文件的时候设置文件属性。调用后返回新创建的文件。下面的代码段将演示在  C:\rafaelnadal\tournaments\2010 目录(此目录必须存在)下创建 SonyEricssonOpen.txt 文件(文件必须不存在,如果已经存在,则会抛出 FileAlreadyExistsException 异常),并使用默认属性。

  1. Path newfile = FileSystems.getDefault().
  2. getPath("C:/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt");
  3. try {
  4. Files.createFile(newfile);
  5. } catch (IOException e) {
  6. System.err.println(e);
  7. }

也可以在创建文件的时候设置属性,下面的代码演示了如何在  POSIX 文件系统上创建文件,并设置访问权限:

  1. Path newfile = FileSystems.getDefault().
  2. getPath("/home/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt");
  3. Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
  4. FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
  5. try {
  6. Files.createFile(newfile, attr);
  7. } catch (IOException e) {
  8. System.err.println(e);
  9. }

随后你将会看到,这并不是创建文件的唯一方式。

写出小文件

NIO.2 提供了优雅的方式来写出小的二进制或文本文件。使用 Files.write() 方法写出文件,这个方法会打开文件用于写出(如果文件不存在会先创建文件)或者将常规文件截取为 0 字节后写出。当所有字节和行写完后,这个方法会关闭文件(即使出现 I/O 错误或异常也会关闭)。简单说来,这个方法相当于使用了  CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 参数。

调用 write() 写出字节

可以使用 Files.write() 来写出字节,这个方法接受一个 Path 类型的参数,一个用于写出的字节数组,和一个可选的文件打开参数(OpenOption)。这个方法会返回写出文件的 Path 对象。

下面的代码段将演示如何写出字节数组,并且使用了默认的打开参数。(文件名为 ball.png,将会被写出到 C:\rafaelnadal\photos 目录):

  1. Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png");
  2. byte[] ball_bytes = new byte[]{
  3. (byte)0x89,(byte)0x50,(byte)0x4e,(byte)0x47,(byte)0x0d,(byte)0x0a,(byte)0x1a,(byte)0x0a,
  4. (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x0d,(byte)0x49,(byte)0x48,(byte)0x44,(byte)0x52,
  5. (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10,
  6. (byte)0x08,(byte)0x02,(byte)0x00,
  7. (byte)0x49,(byte)0x45,(byte)0x4e,(byte)0x44,(byte)0xae,(byte)0x42,(byte)0x60,(byte)0x82
  8. };
  9. try {
  10. Files.write(ball_path, ball_bytes);
  11. } catch (IOException e) {
  12. System.err.println(e);
  13. }

现在,你检查目录,就可以看到创建的 ball.png 文件。

如果你想使用字节的方式来写出字符串(String),那么先要将字符串转换为字节数组,下面的代码将在  C:\rafaelnadal\wiki 目录下写出 wiki.txt 文件:

  1. Path rf_wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
  2. String rf_wiki = "Rafael \"Rafa\" Nadal Parera (born 3 June 1986) is a Spanish professional
  3. tennis " + "player and a former World No. 1. As of 29 August 2011 (2011 -08-29)[update], he is
  4. ranked No. 2 " + "by the Association of Tennis Professionals (ATP). He is widely regarded as
  5. one of the greatest players " + "of all time; his success on clay has earned him the nickname
  6. \"The King of Clay\", and has prompted " + "many experts to regard him as the greatest clay
  7. court player of all time. Some of his best wins are:";
  8. try {
  9. byte[] rf_wiki_byte = rf_wiki.getBytes("UTF-8");
  10. Files.write(rf_wiki_path, rf_wiki_byte);
  11. } catch (IOException e) {
  12. System.err.println(e);
  13. }

调用 write() 按行写出

可以使用 Files.write() 方法按行写出文件,方法会在每行的结尾根据系统添加一个行结束符(line.separator 系统属性)。这个方法接受一个 Path 类型的参数,一个可迭代的字符序列集合,一个用于编码的参数和一个可选的文件打开参数。这个方法会返回写出的文件 Path 对象。

下面的代码将演示如何按行写出文件(实际上,会在上节创建的 wiki.txt 文件后面添加内容)。

  1. Path rf_wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
  2. Charset charset = Charset.forName("UTF-8");
  3. ArrayList<String> lines = new ArrayList<>();
  4. lines.add("\n");
  5. lines.add("Rome Masters - 5 titles in 6 years");
  6. lines.add("Monte Carlo Masters - 7 consecutive titles (2005-2011)");
  7. lines.add("Australian Open - Winner 2009");
  8. lines.add("Roland Garros - Winner 2005-2008, 2010, 2011");
  9. lines.add("Wimbledon - Winner 2008, 2010");
  10. lines.add("US Open - Winner 2010");
  11. try {
  12. Files.write(rf_wiki_path, lines, charset, StandardOpenOption.APPEND);
  13. } catch (IOException e) {
  14. System.err.println(e);
  15. }

读取小文件

NIO.2 提供了快速读取小文件的方法。使用  Files.readAllBytes() 和Files.readAllLines() 方法读取文件。这个方法在读取完成后会自动关闭流(即使出现 I/O 异常或错误也会关闭)。

调用 readAllBytes() 方法读取数据

Files.readAllBytes() 方法将整个文件读入字节数组中。下面的代码将演示读取前面创建的  ball.png 文件(文件必须存在)到字节数组:

  1. Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png");
  2. try {
  3. byte[] ballArray = Files.readAllBytes(ball_path);
  4. } catch (IOException e) {
  5. System.out.println(e);
  6. }

如果你想验证返回的字节是否正确,你可以将返回的字节写出到相同目录的 bytes_to_ball.png 文件中:

  1. Files.write(ball_path.resolveSibling("bytes_to_ball.png"), ballArray);

或者可以通过下面的 ImageIO 方式写出 PNG 图片文件:

  1. BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(ballArray));
  2. ImageIO.write(bufferedImage, "png", (ball_path.resolveSibling("bytes_to_ball.png")).toFile());

readAllBytes() 方法也可以用于读取文本文件,这时,读取的字节需要转换成字符串,看看下面的例子(你可以使用任何其它编码):

  1. try {
  2. byte[] wikiArray = Files.readAllBytes(wiki_path);
  3. String wikiString = new String(wikiArray, "ISO-8859-1");
  4. System.out.println(wikiString);
  5. } catch (IOException e) {
  6. System.out.println(e);
  7. }

注意:如果文件太大(超过 2GB),那么字节数组的长度将会超出上限,会抛出 OutOfMemory 异常。这依赖于 JVM 的 Xmx 参数:在 32 位 JVM 上,不能超过 2GB(通常使用默认,不超过 256 MB)。在 64 位 JVM 上,可以大一些——十几 GB。

调用 readAllLines() 方法读取文件

使用 readAllLines() 方法会按行返回 String 类型的 List,方便进行循环取值(传递给这个方法的 Path 对象用于指定被读取文件,CharSet 用于设置解码的编码格式):

  1. Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
  2. Charset charset = Charset.forName("ISO-8859-1");
  3. try {
  4. List<String> lines = Files.readAllLines(wiki_path, charset);
  5. for (String line : lines) {
  6. System.out.println(line);
  7. }
  8. } catch (IOException e) {
  9. System.out.println(e);
  10. }

按照官方文档,这个方法会识别以下的行结束符:

  • \u000A\u000D - 换行回车
  • \u000A - 换行
  • \u000D - 回车

使用缓冲流

对于大多数操作系统来说,进行读写操作都是比较消耗资源的操作。NIO.2 提供了两个方法来进行缓冲区读写:Files.newBufferedReader() 和 Files.newBufferedWriter(),这两个方法都接受 Path 类型的对象,并返回老的 JDK 1.1 中的 BufferedReader 或 BufferedWriter 对象。

使用 newBufferedWriter() 方法

newBufferedWriter() 参数为一个 Path 类型的对象,一个 Charset 对象用于编码,一个可选的文件打开方式选项,返回一个新的 BufferedWriter 对象。这个方法将打开文件用于写出(如果文件不存在将会创建文件)或者将已存在的文件截取为 0 字节。简短说来,这个方法默认情况下相当于使用了 CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 属性。

下面的代码将在前面创建的 wiki.txt 文件后面添加内容:

  1. Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
  2. Charset charset = Charset.forName("UTF-8");
  3. String text = "\nVamos Rafa!";
  4. try (BufferedWriter writer = Files.newBufferedWriter(wiki_path, charset,
  5. StandardOpenOption.APPEND)) {
  6. writer.write(text);
  7. } catch (IOException e) {
  8. System.err.println(e);
  9. }

使用 newBufferedReader() 方法

newBufferedReader() 方法可用于通过缓冲区读取文件。它的参数为一个 Path 类型的对象,一个 Charset 用于解码。它将返回一个 BufferedReader 类型的对象。

下面的代码将演示使用 UTF-8 的方式读取 wiki.txt:

  1. Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
  2. Charset charset = Charset.forName("UTF-8");
  3. try (BufferedReader reader = Files.newBufferedReader(wiki_path, charset)) {
  4. String line = null;
  5. while ((line = reader.readLine()) != null) {
  6. System.out.println(line);
  7. }
  8. } catch (IOException e) {
  9. System.err.println(e);
  10. }

如果按照前文的例子一步步运行下来,那么运行结果将会是:

  1. Rafael "Rafa" Nadal Parera (born 3 June 1986) is a Spanish professional tennis player and a
  2. former World No. 1. As of 29 August 2011 (2011 -08-29)[update], he is ranked No. 2 by the
  3. Association of Tennis Professionals (ATP). He is widely regarded as one of the greatest
  4. players of all time; his success on clay has earned him the nickname "The King of Clay", and
  5. has prompted many experts to regard him as the greatest clay court player of all time. Some
  6. of his best wins are:
  7. Rome Masters - 5 titles in 6 years
  8. Monte Carlo Masters - 7 consecutive titles (2005-2011)
  9. Australian Open - Winner 2009
  10. Roland Garros - Winnner 2005-2008, 2010, 2011
  11. Wimbledon - Winner 2008, 2010
  12. US Open - Winner 2010
  13. Vamos Rafa!

使用非缓冲流

NIO.2 提供了使用非缓冲流的方法,可以用于直接读取或通过 java.io 的 API 封装成缓冲流使用。使用非缓冲流的方法是  Files.newInputStream() 和 Files.newOutputStream()。

使用 newOutputStream() 方法

newOutputStream() 参数为一个 Path 对象和一个可选的用于配置文件打开方式的 OpenOption 配置选项。它返回一个新的线程安全的非缓冲输出流 OutputStream。这个方法将打开文件用于写出(如果文件不存在将会创建文件)或者将已存在的文件截取为 0 字节。简短说来,这个方法默认情况下相当于使用了 CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 属性。

下面的代码将会写出 "Racquet: Babolat AeroPro Drive GT" 文本内容到文件 C:\rafaelnadal\equipment\racquet.txt,因为没有指定 OpenOption,所以文件不存的话会自动创建:

  1. Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
  2. String racquet = "Racquet: Babolat AeroPro Drive GT";
  3. byte data[] = racquet.getBytes();
  4. try (OutputStream outputStream = Files.newOutputStream(rn_racquet)) {
  5. outputStream.write(data);
  6. } catch (IOException e) {
  7. System.err.println(e);
  8. }

另外,如果你决定要使用缓冲流来取代非缓冲流,可以使用 java.io API 提供的方法。看看下面的代码,将会在 racquet.txt 文件(文件必须存在)后面添加“String: Babolat RPM Blast 16” 文本。

  1. Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
  2. String string = "\nString: Babolat RPM Blast 16";
  3. try (OutputStream outputStream = Files.newOutputStream(rn_racquet, StandardOpenOption.APPEND);
  4. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {
  5. writer.write(string);
  6. } catch (IOException e) {
  7. System.err.println(e);
  8. }

使用  newInputStream() 方法

newInputStream() 的参数为一个 Path 对象和一个可选的用于配置文件打开方式的 OpenOption 配置选项。它返回一个新的线程安全的 InputStream 对象。默认情况下,这个方法是用的是 OpenOption.READ 选项。

下面的代码将读取 racquet.txt 文件(文件必须存在)的内容:

  1. Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
  2. int n;
  3. try (InputStream in = Files.newInputStream(rn_racquet)) {
  4. while ((n = in.read()) != -1) {
  5. System.out.print((char)n);
  6. }
  7. } catch (IOException e) {
  8. System.err.println(e);
  9. }

你也可以使用 InputStream 的 read() 方法将数据读取到用于缓冲的字节数组中。你可以将上面的代码改写为(记住,你的处理对象依然是非缓冲输入流):

  1. Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
  2. int n;
  3. byte[] in_buffer = new byte[1024];
  4. try (InputStream in = Files.newInputStream(rn_racquet)) {
  5. while ((n = in.read(in_buffer)) != -1) {
  6. System.out.println(new String(in_buffer));
  7. }
  8. } catch (IOException e) {
  9. System.err.println(e);
  10. }

注:调用 read(in_buffer) 方法和调用 read(in_buffer,0,in_buffer.length) 方法是同样的效果。

你也可以通过 java.io API 将非缓冲输入流转换为缓冲输入流,下面的代码和上面的代码运行结果相同,但是更有效率:

  1. Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
  2. try (InputStream in = Files.newInputStream(rn_racquet);
  3. BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
  4. String line = null;
  5. while ((line = reader.readLine()) != null) {
  6. System.out.println(line);
  7. }
  8. } catch (IOException e) {
  9. System.err.println(e);
  10. }

上面三段代码输出的结果都是相同的:

    1. Racquet: Babolat AeroPro Drive GT
    2. String: Babolat RPM Blast 16

JavaNIO学习一的更多相关文章

  1. javaNIO学习

    Buffer其实就是是一个容器对象,它包含一些要写入或者刚读出的数据.在NIO中加入Buffer对象,体现了新库与原I/O的一个重要区别.在面向流的I/O中,您将数据直接写入或者将数据直接读到Stre ...

  2. JavaNIO深入学习

    NIO是Jdk中非常重要的一个组成部分,基于它的Netty开源框架可以很方便的开发高性能.高可靠性的网络服务器和客户端程序.本文将就其核心基础类型Channel, Buffer, Selector进行 ...

  3. Java I/O NIO学习

    给出一个学习的链接讲的很全.. http://ifeve.com/java-nio-all/ 上边的是中文翻译的这里是原地址:http://tutorials.jenkov.com/java-nio/ ...

  4. 【公司要求】- RPC学习(一)

    HADOOP-IPC(这里说的是1.0.4版本) 是轻量级RPC,在hadoop中主要用于2方面 1.TaskTracker和JobTracker 通讯. 2.NameNode和DataNode通讯. ...

  5. javaNIO(转载)

    (一) Java NIO 概述 Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Chan ...

  6. Java NIO 学习总结 学习手册

    原文 并发编程网(翻译):http://ifeve.com/java-nio-all/  源自 http://tutorials.jenkov.com/java-nio/index.html Java ...

  7. Java NIO学习笔记---Channel

    Java NIO 的核心组成部分: 1.Channels 2.Buffers 3.Selectors 我们首先来学习Channels(java.nio.channels): 通道 1)通道基础 通道( ...

  8. 20155204 2016-2017-2 《Java程序设计》第8周学习总结

    学号 2016-2017-2 <Java程序设计>第X周学习总结 教材学习内容总结 想要取得channel的操作对象,可以使用channels类,它定义了静态方法newChannel(). ...

  9. Java NIO学习笔记

    Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...

随机推荐

  1. UI5-文档-导航栏

    UI5-文档-1-前言 UI5-文档-2-开发环境 UI5-文档-2.1-使用OpenUI5开发应用 UI5-文档-2.2-使用SAP Web IDE开发应用程序 UI5-文档-2.3-使用SAPUI ...

  2. Datatable数据分组

    datatable里面的数据是按照这个顺序排列的 姓名    性别        年龄 a1          男           12 a1         女             11 a ...

  3. SpringMVC知识(1)

    1.SpringMVC的工作流程 流程 : 1.用户发送请求至前端控制器DispatcherServlet 2.DispatcherServlet收到请求调用HandlerMapping处理器映射器. ...

  4. 控制html元素的隐藏问题

    控制元素隐藏的方式,有display:none.visibility:hidden以及不透明度设置. 一.display:none 被隐藏的元素,在页面中不占位,空出的位置会被相邻的元素占用. < ...

  5. Android中decode JPG时建议使用inPreferQualityOverSpeed

    在BitmapFactory.decodeBitmap方法中,参数BitmapFactory.Options里有一项是inPreferQualityOverSpeed:设为true的话,画质更好,加载 ...

  6. Django1.8:403错误:CSRF verification failed. Request aborted.

    问题:Django 403错误:CSRF verification failed. Request aborted.     原因:需要加cookie验证 解决方法: 1.在view.py中增加 fr ...

  7. 基于mysql全文索引的深入理解

    最近要使用mysql的全文索引,一直没能成功,一个是只有MyISAM引擎支持,创建表时需要指定,而是需要对my.ini进行配置. 前言:本文简单讲述全文索引的应用实例,MYSQL演示版本5.5.24. ...

  8. VMware安装win7:units specified don't exist问题

    主要是磁盘接口不匹配,调整CD/DVD和硬件磁盘接口, CD/DVD调整成IDE,硬盘调整成SATA即可. 提示system not found,主分区没有激活,进入disgenius,会提示修正,保 ...

  9. 模块常用snippet

    压缩,文件操作,数据库,md5,json, 压缩 import os, sys, time import zipfile # 解压 filename = 'callofdutyblackopszomb ...

  10. jdeveloper基础教程(中文版)

    jdeveloper基础教程(中文版) 程序员的基础教程:菜鸟程序员