疯狂Java学习笔记(75)-----------NIO.2第一篇

Java 7引入了NIO.2,NIO.2是继承自NIO框架,并增加了新的功能(例如:处理软链接和硬链接的功能)。这篇帖子包括三个部分,我将使用NIO.2的一些示例,由此向大家演示NIO.2的基本使用方法。

下一篇地址http://blog.csdn.net/u011225629/article/details/46386599

文件拷贝

Q:怎样拷贝一个文件?

A:你可以使用java.nio.file.Files类的public static Path copy(Path source, Path target, CopyOption… options)方法来实现这个功能,该方法可以实现从源文件到目标文件的拷贝。

默认情况下,如果目标文件已经存在或者是一个符号链接,拷贝就会失败。但是,如果源文件和目标文件是同一个文件,这个拷贝的动作就不会执行。

此外还有一些注意的事项:

  • 文件的属性的拷贝不是必须的。
  • 如果支持符号链接,当源文件是一个符号链接时,拷贝的是最终目标文件的链接。
  • 当源文件是一个目录,copy()方法将目标位置生成一个空目录(目录中的元素不会拷贝)。

每个java.nio.file.CopyOption类型的参数传递到copy()方法的可变参数列表后将改变该方法的行为。该参数是一个java.nio.file.StandardCopyOption类型或java.nio.file.LinkOption枚举常量:

  • COPY_ATTRIBUTES:尝试将文件的属性拷贝到目标文件。这些属性依赖于平台和文件系统,因此是不确定的。但是,至少来说,如果源文件和目标文件的存储都支持最后修改时间属性的话,该属性是会拷贝到目标文件的。不过,需要注意的时,拷贝文件的时间戳的精度可能会有所丢失。
  • NOFOLLOW_LINKS:不一样的符号链接.如果该文件是一个符号链接,拷贝的是符号链接自身而不是其引用的目标文件。它的特殊实现在于是否拷贝文件的属性到新的链接上,换句话说,当拷贝一个符号链接的时候,COPY_ATTRIBUTES可能被忽略。
  • REPLACE_EXISTING:当目标文件已经存在时,目标文件将被替换,除非目标文件是一个非空的目录。当目标文件是一个符号链接并且已经存在的话,仅仅符号链接自身被替换而不改变符号链接所引用的文件。

copy() 方法不支持StandardCopyOption的ATOMIC_MOVE选项,在文件拷贝中该选项是一个无意义的。我将在之后关于文件移动的讨论中介绍ATOMIC_MOVE选项。

非原子性拷贝:

拷贝文件是一个非原子性操作。如果抛出java.io.IOException异常,意味着目标文件可能没有拷贝完成或者文件的属性没有从源文件拷贝过来。如果指定为REPLACE_EXISTING模式并且目标文件已经存在,则目标文件已经被替换了。此外,对文件系统已有文件的存在的检查和新文件的创建的检查可能也不是原子的。

除了java.lang.SecurityException,copy()还会抛出以下某一种异常:

  • java.nio.file.DirectoryNotEmptyException: REPLACE_EXISTING模式下,因目标文件是一个非空的目录文件而不能被替换。
  • java.nio.file.FileAlreadyExistsException:目标文件已经存在,但没有指定REPLACE_EXISTING参数而不能被替换。
  • IOException: I/O异常
  • java.lang.UnsupportedOperationException: 传入的可变参数CopyOptions是不被支持的。

可选的特定异常:

DirectoryNotEmptyException和FileAlreadyExistsException是可选的异常,它们之所以是可选的是因为这些异常的抛出需要底层操作系统能识别,如果不能识别的话,就抛出IOException来替换。

我已经创建了一个小的应用程序来展示copy()方法的最基本方法。列表1展示了该应用程序的源代码:

列表1:Copy.java

import java.io.IOException;

import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Copy
{
   public static void main(String[] args)
   {
      if (args.length != 2)
      {
         System.err.println("usage: java Copy source target");
         return;
      }

      Path source = Paths.get(args[0]);
      Path target = Paths.get(args[1]);

      try
      {
         Files.copy(source, target);
      }
      catch (FileAlreadyExistsException faee)
      {
         System.err.printf("%s: file already exists%n", target);
      }
      catch (DirectoryNotEmptyException dnee)
      {
         System.err.printf("%s: not empty%n", target);
      }
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}

列表1的main()方法首先验证命令行确认有两个参数,代表源文件和目标文件,如果没有,则输出相关信息,并结束该程序。

接下来,java.nio.file.Paths类的静态方法Path get(URI uri)方法被调用两次,根据文件名从文件系统获取源文件和目标文件的java.nio.file.Path的实例对象。

Path对象现在被传到了copy()方法。如果方法执行成功了,将不会输出任何信息,否则,将输出适当的错误信息。

编译列表1中的代码(javac Copy.java)然后运行该程序。例如,执行java Copy Copy.java Copy.bak.你可以尝试拷贝一个非空目录到另一个目录.将出现什么现象?

作为练习,可以修改Copy.java增加命令行参数使得该程序能识别CopyOptions,然后传入对应的枚举常量到copy()方法,再观察这些参数对copy()功能的影响。

向后兼容

为了兼容过去的基于流的I/O框架,Files类提供以下两种copy()方法的变种:

public static long copy(InputStream in, Path target, CopyOption… options)

public static long copy(Path source, OutputStream out)

在此系列的后面,我将演示扩展列表1中的copy方法,使得此方法能够拷贝一个目录及子目录到另外一个目录中。

删除文件和目录

Q: 怎样删除一个文件或目录?

A:你可以使用Files类的public static void delete(Path path)方法来删除一个文件或目录,该方法根据文件或目录的路径来删除:

  • 如果该路径引用的文件是一个被使用的打开的文件,某些操作系统将会阻止该文件被删除。
  • 如果该路径引用的是一个目录,该目录必须是空的(除非是特殊操作系统的特殊的文件)。
  • 如果该路径引用的是一个符号链接,该方法只删除符号链接,而不删除符号链接指向的文件。

非原子性删除

该方法的实现是可能需要检查该物理文件是否是在当前目录下。由于这个原因,delete()方法对部分操作系统来说可能不是原子的。为了可移植性和安全性考虑,你不应该认为delete()方法的实现是原子的。

delete()遇到I/O异常时抛出IOException,如果要删除的文件不存在,将抛出java.nio.file.NoSuchFileException,如果目录不为空,则会抛出DirectoryNotEmptyException。

可选的特定异常:

NoSuchFileException是一个可选的特定的异常。

我创建了一个小的应用程序,用于演示怎样使用delete()方式。列表2中列出了该应用程序的源代码。

列表2: Delete.java

import java.io.IOException;

import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Delete
{
   public static void main(String[] args)
   {
      if (args.length != 1)
      {
         System.err.println("usage: java Delete file-or-directory");
         return;
      }

      Path path = Paths.get(args[0]);
      try
      {
         Files.delete(path);
      }
      catch (NoSuchFileException nsfe)
      {
         System.err.printf("%s: no such file or directory%n", path);
      }
      catch (DirectoryNotEmptyException dnee)
      {
         System.err.printf("%s: not empty%n", path);
      }
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}

列表2的main()方法首先验证命令行参数,确保有且仅有一个参数被传入,该参数是一个文件或者目录的路径。如果没有,将会输出有用的信息并结束该程序。

接下来,调用Paths类的get()方法获取Path对象,该对象代表着文件系统中的文件或目录。

Path对象现在被传入到delete()方法中。如果该方法执行成功,将不会输出任何信息。但是,如果失败,则会输出适当的错误信息。

编译列表2(javac Delete.java)中的代码并运行该应用程序。试着删一个可读写的文件,一个只读文件,一个非空目录和一个符号链接,然后观察删除的结果。

deleteIfExists方法:

Files类声明一个静态方法boolean deleteIfExists(Path path)作为delete()的变种。这两个方法唯一的区别是deleteIfExists只删除存在的文件,因此,deleteIfExists方法永远不会抛出NoSuchFileException异常。

移动文件:

Q: 怎样移动一个文件?

A:你可以使用Files类的public static Path move(Path source, Path target, CopyOption… options)方法来移动文件,该方法的作用就是从源文件移动到目标文件。

默认情况下,该方法尝试移动源文件到目标文件,当目标文件已经存在的时候会发生异常,除非目标文件是源文件自身,这种情况下,该方法不会起作用。

每个CopyOption类型的参数传递到move()方法的可变参数列表后将改变该方法的行为。该参数是一个java.nio.file.StandardCopyOption类型枚举常量:

  • ATOMIC_MOVE: move方法表现为原子的文件系统操作,其他的参数都会被忽略。当目标文件已经存在的时候,特定的实现表现为该存在的文件是否能够被替换,否则将会抛出IO异常。如果该move方法不能实现原子的文件系统操作,将会抛出java.nio.file.AtomicMoveNotSupportedException异常。
  • REPLACE_EXISTING:当目标文件已经存在的时候,目标文件将会被替换,除非目标文件是一个非空的目录。当目标文件已经存在并且是一个符号链接,只替换该符号链接自身,而不替换符号链接所指向的文件。

除了AtomicMoveNotSupportedException之外,move方法抛出的异常与copy方法一致。

我创建了一个小的应用程序用于演示move方法最基本的使用。列表3列出了该应用程序的源代码,该方法与列表1的代码基本一致。

列表3:Move.java

import java.io.IOException;

import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Move
{
   public static void main(String[] args)
   {
      if (args.length != 2)
      {
         System.err.println("usage: java Move source target");
         return;
      }

      Path source = Paths.get(args[0]);
      Path target = Paths.get(args[1]);

      try
      {
         Files.move(source, target);
      }
      catch (FileAlreadyExistsException faee)
      {
         System.err.printf("%s: file already exists%n", target);
      }
      catch (DirectoryNotEmptyException dnee)
      {
         System.err.printf("%s: not empty%n", target);
      }
      catch (IOException ioe)
      {
         System.err.printf("I/O error: %s%n", ioe.getMessage());
      }
   }
}

编译列表3并运行该应用程序。例如,假设有一个名为report.txt的文件,执行java Move report.txt report bak。当你移动文件到另一个已经存在的文件的时候将会发生什么?

作为练习,可以修改Move.java增加命令行参数使得该程序能识别CopyOptions,然后传入对应的枚举常量到move()方法,然后观察这些参数对move()功能的影响。

接下来的内容

在第二部分,我将演示路径相关方法(例如获取路径、检索路径信息),文件或目录测试方法(例如测试文件或目录的存在性)以及面向属性的一些方法。

本文借鉴http://www.codeceo.com/article/nio-2-guide-1.html

时间: 2024-10-18 12:15:13

疯狂Java学习笔记(75)-----------NIO.2第一篇的相关文章

疯狂Java学习笔记(87)-----------十篇必读的Java文章

1.Brian Goetz:"管理工作:发人深省的部分" 这其实不是一篇博文,而是Brian Goetz对于甲骨文公司Java的管理的一个非常有趣的讨论的记录.在 以前我们将Java语言与Scala或者Ceylon相比较的时候,对其1-2个特性一直稍微有些意见. 对于为什么Java尽快变得和其他语言一样"时髦"不是一个好主意,Brian提出了很好的观点.每一个Java开发者都应有所了解(大约一个小时). Youtube链接. 2.Aleksey Shipilёv:(

疯狂Java学习笔记(76)------------NIO.2第二篇

上一篇地址http://write.blog.csdn.net/postedit/46386609 在该系列的上一篇中我演示了NIO.2的三个方法:文件拷贝.文件和目录的删除和文件移动.在这篇文章中,我将向大家展示路径相关的方法(如获取路径.检索路径信息).文件和目录测试方法(如文件或目录的存在性测试)以及面向属性的方法. 获取路径 问:怎样获得一个 java.nio.file.Path 对象? 答:你可以调用 java.nio.file.Paths 类的下列任何一静态个方法,通过文件或目录在文

【疯狂Java学习笔记】【理解面向对象】

[学习笔记]1.Java语言是纯粹的面向对象语言,这体现在Java完全支持面向对象的三大基本特征:封装.继承.多态.抽象也是面向对象的重要组成部分,不过它不是面向对象的特征之一,因为所有的编程语言都需要抽象. 2.面向对象开发方法比较结构化开发方法的优势在于可以提供更好的可重用性.可扩展性.可维护性. 3.基于对象和面向对象的区别:基于对象也使用了对象,但是无法通过现有的对象作为模板来生成新的对象类型,继而产生新的对象,也就是说,基于对象没有继承的特点.而面向对象有继承,而多态则是建立在继承的基

疯狂Java学习笔记(84)----------关于 Java 对象序列化您不知道的 5 件事

数年前,当和一个软件团队一起用 Java 语言编写一个应用程序时,我体会到比一般程序员多知道一点关于 Java 对象序列化的知识所带来的好处. 关于本系列 您觉得自己懂 Java 编程?事实上,大多数程序员对于 Java 平台都是浅尝则止,只学习了足以完成手头上任务的知识而已.在本 系列 中,Ted Neward 深入挖掘 Java 平台的核心功能,揭示一些鲜为人知的事实,帮助您解决最棘手的编程挑战. 大约一年前,一个负责管理应用程序所有用户设置的开发人员,决定将用户设置存储在一个 Hashta

疯狂Java学习笔记(84)----------大约 Java 对象序列化,你不知道 5 事

几年前,.当一个软件团队一起用 Java 书面申请.我认识比一般程序猿多知道一点关于 Java 对象序列化的知识所带来的优点. 关于本系列 您认为自己懂 Java 编程?其实,大多数程序猿对于 Java 平台都是浅尝则止,仅仅学习了足以完毕手头上任务的知识而已.在本 系列 中,Ted Neward 深入挖掘 Java 平台的核心功能,揭示一些鲜为人知的事实,帮助您解决最棘手的编程挑战. 大约一年前,一个负责管理应用程序全部用户设置的开发者,决定将用户设置存储在一个 Hashtable中,然后将这

疯狂java学习笔记之面向对象(八) - static和final

一.static: 1.static是一个标识符: - 有static修饰的成员表明该成员是属于类的; - 没有static修饰的成员表明该成员是属于实例/对象的. 2.static修饰的成员(Field.方法.初始化块),与类共存亡:static修饰的成员建议总是通过类名来访问,虽然它也可以通过实例来访问(实质也是通过类来访问的),所以平时若在其他程序中见到通过实例/对象来访问static成员时,可以直接将实例/对象 替换成类名: 3.程序都是先有类再有对象的,有可能出现有类但没有实例/对象的

疯狂Java学习笔记(52)-----------Annotation(注释)第一篇

从JDK1.5开始,Java中增加了对元数据(MetaData)的支持,也就是Annotation(注释),这种Annotation与Java程序中的单行注释和文本注释是有一定区别,也有一定联系的.其实,我们现在说的Annotation是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理.通过Annotation,程序开发人员可以在不改变原来逻辑的情况下,在源文件嵌入一些补充的信息.代码分析工具,开发工具和部署工具可通过这些补充信息进行验正或者部署. Annotatio

疯狂Java学习笔记(61)-----------40个Java集合面试问题和答案

1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最初的Java版本包含几种集合类:Vector.Stack.HashTable和Array.随着集合的广泛使用,Java1.2提出了囊括所有集合接口.实现和算法的集合框架.在保证线程安全的情况下使用泛型和并发集合类,Java已经经历了很久.它还包括在Java并发包中,阻塞接口以及它们的实现.集合框架的部分优点如下: (1)使用核心集合类降低开发成本,而非实现我们自己的集合类. (2)随着使用经过严格测试的集合框架类,代

疯狂Java学习笔记(37)----------List集合

在网上找了很长时间关于List集合的资料,发现都是代码,理论性的东西太少了,对于想要深入的学习我来说,很困难呀,光看代码,不能解决问题呀!所以,自己精心的搜刮来了一点资料在这整理了一下! List集合! ·List列表 ·list: list代表有序.可重复集合,每个元素都有对应的索引,所以List集合中的元素可以重复.List集合默认暗元素的添加顺序设计元素! ·list当然也用于collection中的所有方法,并且自己也有添加了额外的方法,所有List实现类都可以调用这些方法来操作元素.L