当前位置: 首页 > article >正文

Java 文件路径一口气讲完!(* ̄3 ̄)╭

Java 文件锁

Java IO教程 - Java文件锁

NIO支持文件锁定以同步对文件的访问。我们可以锁定文件或整个文件的区域。

文件锁定机制由操作系统处理。

有两种文件锁定:排他和共享。

只有一个程序可以保存文件区域上的排他锁。

多个程序可以在文件的同一区域上保存共享锁。

我们不能在文件的同一区域混合排他锁和共享锁。

java.nio.channels.FileLock类表示文件锁。

我们通过使用FileChannel对象的lock()或tryLock()方法获取对文件的锁。

lock()方法阻止所请求区域上的锁是否不可用。

tryLock()方法不阻塞; 如果获取锁,它立即返回FileLock类的对象; 否则返回null。

lock()和tryLock()方法有两个版本:一个没有参数,另一个有参数。

不带参数的版本锁定整个文件。

带有参数的版本接受要锁定的区域的起始位置,要锁定的字节数以及用于指示锁是否共享的布尔标志。

如果锁是共享的,FileLock对象的isShared()方法返回true; 否则,返回false。

以下代码显示了获取文件锁的不同方法。

import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class Main {
  public static void main(String[] args) throws Exception {
    RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
    FileChannel fileChannel = raf.getChannel();

    FileLock lock = fileChannel.lock();

  }
}

例子

获得前10个字节的独占锁

import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class Main {
  public static void main(String[] args) throws Exception {
    RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
    FileChannel fileChannel = raf.getChannel();
    // Get an exclusive lock on first 10 bytes
    FileLock lock = fileChannel.lock(0, 10, false);

  }
}

尝试获取整个文件的独占锁

import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class Main {
  public static void main(String[] args) throws Exception {
    RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
    FileChannel fileChannel = raf.getChannel();
    FileLock lock = fileChannel.tryLock();
    if (lock == null) {
      // Could not get the lock
    } else {
      // Got the lock
    }

  }
}

尝试在共享模式下从第11个字节开始锁定100个字节

import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class Main {
  public static void main(String[] args) throws Exception {
    RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
    FileChannel fileChannel = raf.getChannel();
    FileLock lock = fileChannel.tryLock(11, 100, true);
    if (lock == null) {
      // Could not get the lock
    } else {
      // Got the lock
    }

  }
}

以下代码显示如何使用try-catch-finally块来获取和释放文件锁定,如下所示:

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class Main {
  public static void main(String[] args) throws Exception {
    RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
    FileChannel fileChannel = raf.getChannel();
    FileLock lock = null;
    try {
      lock = fileChannel.lock(0, 10, true);

    } catch (IOException e) {
      // Handle the exception
    } finally {
      if (lock != null) {
        try {
          lock.release();
        } catch (IOException e) {
          // Handle the exception
        }
      }
    }

  }
}

Java 文件系统

Java IO教程 - Java文件系统

Java 7引入了新的输入/输出2(NIO.2)API并提供了一个新的 I/O API。

它向Java类库添加了三个包:java.nio.file,java.nio.file.attribute和java.nio.file.spi。

文件系统

FileSystem类的对象表示Java程序中的文件系统。

FileSystem对象用于执行两个任务:

  • Java程序和文件系统之间的接口。
  • 一个工厂用于创建许多类型的文件系统相关对象和服务。

FileSystem对象与平台相关。

创建文件系统

要获取默认的FileSystem对象,我们需要使用FileSystems类的getDefault()静态方法,如下所示:

FileSystem fs  = FileSystems.getDefault();

FileSystem由一个或多个FileStore组成。FileSystem的getFileStores()方法返回FileStore对象的Iterator。

FileSystem的getRootDirectories()方法返回Path对象的迭代器,它表示到所有顶级目录的路径。

FileSystem的isReadOnly()方法告诉我们是否获得对文件存储的只读访问权限。

例子

以下代码显示如何使用FileSystem对象。

import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    FileSystem fs = FileSystems.getDefault();

    System.out.println("Read-only file system: " + fs.isReadOnly());
    System.out.println("File name separator: " + fs.getSeparator());

    for (FileStore store : fs.getFileStores()) {
      printDetails(store);
    }
    for (Path root : fs.getRootDirectories()) {
      System.out.println(root);
    }
  }

  public static void printDetails(FileStore store) {
    try {
      String desc = store.toString();
      String type = store.type();
      long totalSpace = store.getTotalSpace();
      long unallocatedSpace = store.getUnallocatedSpace();
      long availableSpace = store.getUsableSpace();
      System.out.println(desc + ", Total: " + totalSpace + ",  Unallocated: "
          + unallocatedSpace + ",  Available: " + availableSpace);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

上面的代码生成以下结果。

Java 路径 

Java IO教程 - Java路径

Path 对象表示文件系统中文件,目录和符号链接的路径。

Path 是java.nio.file包中的一个接口。

当我们创建一个Path对象时,路径不一定存在。

Path 可以使用旧的java.io.File apI。我们可以使用File类的方法toPath()从File对象中获取一个Path对象

我们可以使用Path对象的toFile()方法从Path对象中获取File对象。

我们可以对Path对象执行两种操作:

  • 路径操作
  • 文件操作

我们可以使用 Path 来访问路径的组件,例如文件名,根名等。

我们可以比较和检查路径。例如,检查路径以.txt结尾,比较两个路径是否相同,检查路径是绝对路径还是相对路径等。我们还可以组合和解析路径。

我们需要使用 Files 类在 Path 对象上执行文件I/O操作。

创建路径对象

FileSystem对象充当工厂来创建Path对象

FilePath中的getPath()方法创建一个Path对象。

以下代码在Windows上为文件路径C:\Java_Dev\test1.txt创建一个Path对象:

Path  p1  = FileSystems.getDefault().getPath("C:\\Java_Dev\\test1.txt");

我们可以在构造Path对象时将路径的组件单独传递给getPath()方法。

以下语句创建一个Path对象以表示Windows上的C:\Java_Dev\test1.txt路径:

Path  p2  = FileSystems.getDefault().getPath("C:", "Java_Dev",   "test1.txt");

Paths 类可以从路径字符串或URI的组件创建 Path 对象。

Paths.get()静态方法创建一个Path对象。

以下创建 Path 对象来表示相同的路径C:\Java_Dev\test1.txt:

Path  p3  = Paths.get("C:\\Java_Dev\\test1.txt");
Path  p4  = Paths.get("C:", "Java_Dev", "test1.txt");

我们可以从一个空路径创建一个 Path 对象,例如Paths.get(“")。具有空路径的Path对象是指与当前工作目录相同的默认目录。

路径的组件

文件系统中的路径由一个或多个组件组成。

getNameCount()方法返回除了根之外的Path对象中的组件数量。

例如,路径C:\Java_Dev\test1.txt由三个组件组成:C:的根和名为Java_Dev和test1.txt的两个组件。这种情况下,getNameCount()方法将返回2。

getName(int index)方法返回指定索引处的组件名称。最靠近根的组件的索引为0。

离根最远的组件的索引为count - 1.在路径中。

C:\Java_Dev\test1.txt,Java_Dev组件的索引为0,test1.txt组件的索引为1。

getParent()方法返回路径的父代。如果路径没有父级,则返回null。

路径C:\Java_Dev\test.txt的父目录是C:\Java_Dev。

相对路径test.txt没有父级。

getRoot()方法返回路径的根。如果路径没有根,它返回null。

例如,Windows上的路径C:\Java_Dev\test1.txt具有C:\作为其根。

getFileName()方法返回由路径表示的文件名。

如果路径没有文件名,则返回null。文件名是根的最远组件。例如,在路径C:\Java_Dev\test1.txt中,test1.txt是文件名。

我们可以通过使用isAbsolute()方法来检查路径是否代表绝对路径。

路径不必存在以获取有关其组件的信息。

以下代码显示了如何访问Path对象的组件。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) {
    Path p1 = Paths.get("C:\\Java_Dev\\test1.txt");
    printDetails(p1);

    Path p2 = Paths.get("test1.txt");
    printDetails(p2);
  }

  public static void printDetails(Path p) {
    System.out.println("Details for path: " + p);

    int count = p.getNameCount();
    System.out.println("Name count: " + count);

    for (int i = 0; i < count; i++) {
      Path name = p.getName(i);
      System.out.println("Name at  index   " + i + "  is " + name);
    }

    Path parent = p.getParent();
    Path root = p.getRoot();
    Path fileName = p.getFileName();
    System.out.println("Parent: " + parent + ", Root:   " + root
        + ", File Name: " + fileName);
    System.out.println("Absolute Path: " + p.isAbsolute());
  }
}

上面的代码生成以下结果。

不同形式的路径

我们可以得到一个路径的不同类型的表示。假设我们创建一个Path对象,如下所示:

Path  p1  = Paths.get("test.txt");

这里,p1是相对路径。我们可以使用其toAbsolutePath()方法获得由p1表示的绝对路径,如下所示:

Path  p1AbsPath  = p1.toAbsolutePath();

现在p1AbsPath是p1的绝对路径。例如,在Windows上,p1AbsPath可能看起来像C:\testapp\test.txt。

如果路径不是绝对路径,toAbsolutePath()方法使用默认目录来解析路径。

如果路径是绝对路径,toAbsolutePath()方法返回相同的路径。

我们可以使用toRealPath()方法来获取现有文件的真实路径。它返回到现有文件的规范路径。如果路径表示符号链接,则它返回目标文件的实际路径。

我们可以传递一个链接选项到这个方法,指示我们是否要跟踪到它的目标的符号链接。

如果路径表示的文件不存在,toRealPath()会抛出一个IOException异常。

以下代码显示了如何从Path对象获取实际路径:

import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {

    Path p2 = Paths.get("test2.txt");
    // Follow link for p2, if it is a symbolic link
    Path p2RealPath = p2.toRealPath();
    System.out.println("p2RealPath:" + p2RealPath);
    Path p3 = Paths.get("test3.txt");
    // Do not follow link for p3, if it is a symbolic link
    Path p3RealPath = p3.toRealPath(LinkOption.NOFOLLOW_LINKS);
    System.out.println("p3RealPath:" + p3RealPath);

  }
}

路径URI

使用Path的toUri()方法来获取其URI表示。

以下代码显示了如何获取路径的URI形式。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path p2 = Paths.get("test2.txt");
    java.net.URI p2UriPath = p2.toUri();
    System.out.println("Absolute Path: " + p2.toAbsolutePath());
    System.out.println("URI Path: " + p2UriPath);

  }
}

上面的代码生成以下结果。

Java 路径操作 

Java IO教程 - Java路径操作

比较路径

我们可以基于它们的文本表示来比较两个Path对象的相等性。

equals()方法通过比较它们的字符串形式来测试两个Path对象的相等性。

等式测试是否区分大小取决于文件系统。

以下代码显示如何比较Windows路径:

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path  p1  = Paths.get("C:\\Java_Dev\\test1.txt"); 
    Path  p2  = Paths.get("C:\\Java_Dev\\LUCI1.TXT");
    Path  p3  = Paths.get("C:\\Java_Dev\\..\\Java_Dev\\test1.txt"); 
    boolean b1  = p1.equals(p2);
    System.out.println(b1);
    boolean b2  = p1.equals(p3);
    System.out.println(b2);
  }
}

equals()方法在不解析实际文件引用的情况下以文本方式比较两个路径,并且不会测试文件系统中是否存在路径。

Path接口实现java.lang.Comparable接口。我们可以使用它的compareTo()方法来与另一个Path对象进行文本比较。

compareTo()方法返回一个int值。

  • 0 - 当两条路径相等时
  • 小于0 - 路径小于指定的路径
  • 大于0 - 路径大于指定的路径

上面的代码生成以下结果。

例子

以下代码显示如何使用compareTo()方法:

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path  p1  = Paths.get("C:\\Java_Dev\\test1.txt"); 
    Path  p2  = Paths.get("C:\\Java_Dev\\Test1.txt");
    Path  p3  = Paths.get("C:\\Java_Dev\\..\\Java_Dev\\test1.txt");
    int v1  = p1.compareTo(p2); 
    System.out.println(v1);
    int v2  = p1.compareTo(p3);
    System.out.println(v2);
  }
}

我们可以使用endsWith()和startsWith()方法分别测试路径是否以给定路径结尾和开始。

上面的代码生成以下结果。

例2

以下代码显示了如何在路径中使用endsWith()和startsWith()方法。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path p1 = Paths.get("C:\\Java_Dev\\test1.txt");
    Path p2 = Paths.get("test1.txt");
    Path p3 = Paths.get("Java_Dev\\test1.txt");
    Path p4 = Paths.get(".txt");

    // Using endsWith()
    boolean b1 = p1.endsWith(p2);
    System.out.println(b1);
    boolean b2 = p1.endsWith(p3);
    boolean b3 = p1.endsWith(p4);

    // Using startsWith()
    Path p5 = Paths.get("C:\\");
    Path p6 = Paths.get("C:\\Java_Dev");
    Path p7 = Paths.get("C:\\Java_Dev");

    boolean b4 = p1.startsWith(p5);
    boolean b5 = p1.startsWith(p6);
    boolean b6 = p1.startsWith(p7);
  }
}

上面的代码生成以下结果。

例3

以下代码演示了isSameFile()方法的工作原理。它检查如果两个路径将找到相同的文件。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) {
    // Assume that C:\Java_Dev\test1.txt file exists
    Path p1 = Paths.get("C:\\Java_Dev\\test1.txt");
    Path p2 = Paths.get("C:\\Java_Dev\\..\\Java_Dev\\test1.txt");

    // Assume that C:\abc.txt file does not exist
    Path p3 = Paths.get("C:\\abc.txt");
    Path p4 = Paths.get("C:\\abc.txt");

    try {
      boolean isSame = Files.isSameFile(p1, p2);
      System.out.println("p1 and  p2  are   the   same:  " + isSame);

      isSame = Files.isSameFile(p3, p4);
      System.out.println("p3 and  p4  are   the   same:  " + isSame);
    }catch (IOException e) {
      e.printStackTrace();
    }
  }
}

规范化路径

Path接口的normalize()方法在删除点字符后返回一个Path。

此方法不访问文件系统。

以下代码显示了在Windows上标准化路径的一些示例。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path p1 = Paths.get("C:\\Java_Dev\\..\\\\Java_Dev\\test1.txt");
    Path p1n = p1.normalize();
    System.out.println(p1 + "  normalized to " + p1n);

    Path p2 = Paths.get("C:\\Java_Dev\\test1.txt");
    Path p2n = p2.normalize();
    System.out.println(p2 + "  normalized to " + p2n);

    Path p3 = Paths.get("\\..\\.\\test.txt");
    Path p3n = p3.normalize();
    System.out.println(p3 + "  normalized to " + p3n);
  }
}

上面的代码生成以下结果。

解决路径

我们可以使用Path接口的resolve(Path p)方法组合两个路径。

如果指定的路径是绝对路径,则返回指定的路径。如果指定的路径是空路径,则返回路径。

在其他情况下,它只是组合两个路径并返回结果,因此返回的路径以指定的路径结束。

以下代码具有一些解析路径的示例。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path p1 = Paths.get("C:\\Java_Dev");
    Path p2 = Paths.get("test1.txt");
    System.out.println(p1.resolve(p2));

    Path p3 = Paths.get("C:\\test.txt");
    System.out.println(p1.resolve(p3));

    Path p4 = Paths.get("");
    System.out.println(p1.resolve(p4));

    Path p5 = Paths.get("Java_Dev\\Test");
    Path p6 = Paths.get("test4.txt");
    System.out.println(p5.resolve(p6));
  }
}

上面的代码生成以下结果。

相关路径

Path接口的relativize(Path p)方法获得给定路径相对于另一个路径的相对路径。

以下代码有一些获取相对路径的示例。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path p1 = Paths.get("Java_Dev");
    Path p2 = Paths.get("Java_Dev", "recent", "Test");
    System.out.println(p1.relativize(p2));
    System.out.println(p2.relativize(p1));

    Path p3 = Paths.get("Abc");
    Path p4 = Paths.get("Def");
    System.out.println(p3.relativize(p4));
    System.out.println(p4.relativize(p3));
  }
}

上面的代码生成以下结果。

Java 符号链接 

Java IO教程 - Java符号链接

符号链接包含对另一个文件或目录的引用。

符号链接引用的文件称为符号链接的目标文件。

符号链接上的操作对应用程序是透明的。我们可以使用java.nio.file.Files类处理符号链接。

isSymbolicLink(Path p)方法检查指定路径指定的文件是否是符号链接。

文件的createSymbolicLink()方法(可能不是在所有平台上都支持)创建符号链接。

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

public class Main {
  public static void main(String[] args) throws Exception {
    Path existingFilePath = Paths.get("C:\\Java_Dev\\test1.txt");
    Path symLinkPath = Paths.get("C:\\test1_link.txt");
    Files.createSymbolicLink(symLinkPath, existingFilePath);
  }
}

默认情况下,Java NIO API遵循符号链接。我们可以指定是否要遵循符号链接。使用枚举常量LinkOption来指示不遵循符号链接的选项。 NOFOLLOW_LINKS。

LinkOption枚举在java.nio.file包中声明。支持此选项的方法让我们传递LinkOption类型的参数。

我们可以使用Files类的createLink(Path newLink,Path existingPath)方法来创建硬链接。

Java 文件

Java IO教程 - Java文件

java.nio.file.Files 包含所有允许我们对Path对象执行大多数文件操作的静态方法。

创建新文件

文件可以创建常规文件,目录,符号链接和临时文件/目录。

大多数方法接受FileAttribute类型的varargs参数,这允许我们指定文件属性。

createFile()方法创建一个新的常规文件。创建的新文件为空。

如果文件已存在,或父目录不存在,文件创建将失败。

以下代码显示如何创建新文件。

import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) {
    Path p1 = Paths.get("test.txt");
    try {
      Files.createFile(p1);
      System.out.format("File created:  %s%n", p1.toRealPath());
    } catch (FileAlreadyExistsException e) {
      System.out.format("File %s  already exists.%n", p1.normalize());
    } catch (NoSuchFileException e) {
      System.out.format("Directory %s  does  not  exists.%n", p1.normalize()
          .getParent());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

上面的代码生成以下结果。

创建目录

createDirectory()和createDirectories()方法创建一个新目录。

如果父目录不存在,createDirectory()方法将失败。

createDirectories()方法创建不存在的父目录。

createTempDirectory()和createTempFile()方法分别创建一个临时目录和一个临时文件。

以下代码显示了如何创建临时文件和目录。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
      String dirPrefix = "KDir";
      Path tDir = Files.createTempDirectory(dirPrefix);
      System.out.println("Temp directory: " + tDir);
      String fPrefix = "Header_";
      String fSuffix = ".txt";
      Path tFile1 = Files.createTempFile(fPrefix, fSuffix);
      System.out.println("Temp file1: " + tFile1);

      Path p1 = Paths.get("C:\\temp");
      Path tFile2 = Files.createTempFile(p1, fPrefix, fSuffix);
      System.out.println("Temp file2: " + tFile2);
  }
}

不会自动删除临时文件/目录。我们可能希望使用java.io.File类的deleteOnExit()方法在JVM退出时删除该文件。

Path  tempFile = Files.createTempFile("myTempFile", ".txt");
tempFile.toFile().deleteOnExit();

上面的代码生成以下结果。

删除文件

从文件中删除(Path p)和deleteIfExists(Path p)以删除文件,目录和符号链接。

如果删除失败,delete()方法将抛出异常。

如果要删除的文件不存在,deleteIfExists()方法不会抛出NoSuchFileException异常。

如果它删除文件,则返回true。否则,它返回false。

以下代码显示如何删除文件和处理异常:

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 Main {
  public static void main(String[] args) throws Exception {
    Path p = Paths.get("C:\\Java_Dev\\test1.txt");

    try {
      Files.delete(p);
      System.out.println(p + "  deleted successfully.");
    } catch (NoSuchFileException e) {
      System.out.println(p + "  does  not  exist.");
    } catch (DirectoryNotEmptyException e) {
      System.out.println("Directory " + p + "  is not  empty.");
    } catch (IOException e) {
      e.printStackTrace();
    }

  }
}

上面的代码生成以下结果。

存在文件

文件类有两个方法,分别是exists(Path p,LinkOption ... options)和notExists(Path p,LinkOption ... options)来检查文件的存在和不存在。

复制文件

文件类复制(Path source,Path target,CopyOption ... options)方法可以将指定的源路径复制到指定的目标路径。

如果指定的源文件是符号链接,则将复制符号链接的目标,而不是符号链接。

如果指定的源文件是目录,则创建目标位置处的空目录,而不复制目录的内容。

我们可以使用copy()方法指定一个或多个以下复制选项:

  • StandardCopyOption.REPLACE_EXISTING
  • StandardCopyOption.COPY_ATTRIBUTES
  • LinkOption.NOFOLLOW_LINKS

我们可以指定REPLACE_EXISTING选项来替换现有的目标文件。

如果目标文件是符号链接,并且如果存在,则通过指定REPLACE_EXISTING选项而不是符号链接的目标来替换符号链接。

COPY_ATTRIBUTES选项将源文件的属性复制到目标文件。

如果使用NOFOLLOW_LINKS选项,则copy()方法复制符号链接,而不是符号链接的目标。

以下代码显示了使用copy()方法复制文件。如果复制操作失败,它会处理可能的异常。

import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.DirectoryNotEmptyException;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;

public class Main {
  public static void main(String[] args) {
    Path source = Paths.get("C:\\Java_Dev\\test1.txt");
    Path target = Paths.get("C:\\Java_Dev\\test1_backup.txt");

    try {
      Path p = Files.copy(source, target, REPLACE_EXISTING, COPY_ATTRIBUTES);
      System.out.println(source + "  has  been  copied to " + p);
    } catch (FileAlreadyExistsException e) {
      System.out.println(target + "  already exists.");
    } catch (DirectoryNotEmptyException e) {
      System.out.println(target + "  is not  empty.");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

移动文件

Files类的move(Path source,Path target,CopyOption ... options)方法移动或重命名文件。

如果指定的目标文件已存在,则移动操作将失败。

我们可以指定REPLACE_EXISTING选项来替换现有的目标文件。

如果要移动的文件是符号链接,它将移动符号链接,而不是符号链接的目标。

move()方法只能用于移动一个空目录。

除了REPLACE_EXISTING复制选项,我们可以使用ATOMIC_MOVE作为另一个CopyOption。

如果使用ATOMIC_MOVE选项,如果无法以原子方式移动文件,则会抛出AtomicMoveNotSupportedException。

如果指定ATOMIC_MOVE选项,则忽略所有其他选项。

以下代码显示了如何通过处理可能的异常来移动文件:

import static java.nio.file.StandardCopyOption.ATOMIC_MOVE;

import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path source = Paths.get("C:\\Java_Dev\\test1.txt");
    Path target = Paths.get("C:\\Java_Dev\\dir2\\test1.txt");

    try {
      Path p = Files.move(source, target, ATOMIC_MOVE);
      System.out.println(source + "  has  been  moved to " + p);
    }catch (NoSuchFileException e) {
      System.out.println("Source/target does  not  exist.");
    } catch (FileAlreadyExistsException e) {
      System.out.println(target + "  already exists.  Move failed.");
    } catch (DirectoryNotEmptyException e) {
      System.out.println(target + "  is not  empty.  Move failed.");
    } catch (AtomicMoveNotSupportedException e) {
      System.out.println("Atomic move is not  supported. MOve  failed.");
    } catch (IOException e) {
      e.printStackTrace();
    }

  }
}

上面的代码生成以下结果。

Java 文件属性 

Java IO教程 - Java文件属性

Files类可以访问常用的文件属性。

Files.isHidden(Path p)方法测试文件是否被隐藏。

Files类中的以下方法可以访问文件的各种类型的常用属性。

long size(Path)
boolean isHidden(Path path)
boolean isRegularFile(Path path, LinkOption... options)
boolean isDirectory(Path path, LinkOption... options)
boolean isSymbolicLink(Path path)
FileTime getLastModifiedTime(Path path, LinkOption... options)

文件属性

java.nio.attribute包包含属性相关的类。它在以下六种类型的视图中捆绑文件属性。

BasicFileAttributeView管理基本文件属性,如创建时间,上次访问时间,上次修改时间,大小,文件类型(常规文件,目录,符号链接或其他)和文件键(文件的唯一编号)。所有平台都支持此视图。

DosFileAttributeView扩展BasicFileAttributeView访问特定于DOS的文件属性。它提供了支持以检查文件是否是隐藏文件,系统文件,归档文件和只读文件。它仅在支持DOS的系统(如Microsoft Windows)上可用。

POSIX代表UNIX的便携式操作系统接口。PosixFileAttributeView扩展了BasicFileAttributeView并添加了对支持POSIX标准(如UNIX)的系统上可用的属性的支持。 它允许我们管理所有者,组和[相关访问]权限。

FileOwnerAttributeView管理文件的所有者。

ACL代表访问控制列表。AclFileAttributeView管理文件的ACL。

UserDefinedFileAttributeView管理一组文件的用户定义属性。属性的名称是一个字符串。属性的值可以是任何数据类型。

文件属性视图支持

支持FileStore类中的File Attribute View()方法文件存储器是否支持特定文件属性视图。

如果支持指定的文件属性视图,则返回true; 否则,返回false。

以下代码显示如何检查文件属性支持。

import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributeView;

public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("");
    FileStore fs = Files.getFileStore(path);

    // Check if POSIX file attribute is supported by the file store
    boolean supported = fs
        .supportsFileAttributeView(PosixFileAttributeView.class);
    if (supported) {
      System.out.println("POSIX file attribute view  is supported.");
    } else {
      System.out.println("POSIX file attribute view  is not  supported.");
    }

  }
}

上面的代码生成以下结果。

例子

以下代码显示如何检查文件存储的支持的文件属性视图。

import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.DosFileAttributeView;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.UserDefinedFileAttributeView;

public class Main {
  public static void main(String[] args) {
    Path path = Paths.get("C:");

    try {
      FileStore fs = Files.getFileStore(path);
      printDetails(fs, AclFileAttributeView.class);
      printDetails(fs, BasicFileAttributeView.class);
      printDetails(fs, DosFileAttributeView.class);
      printDetails(fs, FileOwnerAttributeView.class);
      printDetails(fs, PosixFileAttributeView.class);
      printDetails(fs, UserDefinedFileAttributeView.class);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }

  public static void printDetails(FileStore fs,
      Class<? extends FileAttributeView> attribClass) {
    boolean supported = fs.supportsFileAttributeView(attribClass);
    System.out.format("%s is  supported: %s%n", attribClass.getSimpleName(),
        supported);
  }
}

上面的代码生成以下结果。

读取和更新文件属性

要读取或更新一个文件属性,请使用Files类。

Files类具有以下两种静态方法,以使用属性名称作为字符串来读取和更新文件属性:

Object getAttribute(Path path, String attribute, LinkOption... options)
Path setAttribute(Path path, String attribute, Object value, LinkOption... options)

要读取或更新文件的多个属性,请使用特定的文件属性视图。

对于大多数文件属性视图,我们必须使用名为 TypeAttributes和 TypeAttributeView的两个接口。

对于基本文件属性,我们有BasicFileAttributes和BasicFileAtrributeView接口。

Type Attributes读取的属性。该Type AttributeView读取/更新的属性。

以下两个Files类的方法批量读取文件属性。

<A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options)

Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options)

两种方法的最后一个参数指定如何处理符号链接。默认情况下,如果文件是符号链接,则读取符号链接的目标属性。

如果我们指定NOFOLLOW_LINKS作为选项,则读取符号链接的属性,而不是其目标的属性。

第一个readAttributes()方法返回 TypeAttributes对象中指定类型的所有文件属性。

创建表示文件路径的Path对象

Path  path   = Paths.get("C:\\Java_Dev\\test1.txt");

读取基本文件属性

BasicFileAttributes bfa  = Files.readAttributes(path, BasicFileAttributes.class);

获取上次修改时间

FileTime lastModifiedTime  = bfa.lastModifiedTime();

获取文件的大小

long  size = bfa.size();

第二个readAttributes()方法返回特定类型的所有或部分属性。

要读取的属性列表以字符串形式提供。属性列表的字符串形式使用以下语法:

view-name:comma-separated-attributes

view-name 是我们要读取的属性视图的名称,例如basic,posix,acl等。

如果省略view-name,则默认为basic。如果view-name存在,它后面跟一个冒号。

通过将星号指定为属性列表,我们可以读取特定视图类型的所有属性。例如,我们可以指定“basic:*”或“*”来读取所有基本文件属性。

要读取基本视图的大小和最后修改时间,我们将使用

"basic:size,lastModifiedTime" or "size,lastModifiedTime". 

要使用ACL视图读取文件的owner属性,我们将使用字符串“acl:owner”。

要读取文件的所有posix属性,我们将使用“posix:*"。

以下代码打印文件C:\Java_Dev\test1.txt的大小和最后修改时间。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;

public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("C:\\Java_Dev\\test1.txt");

    // Prepare the attribute list
    String attribList = "basic:size,lastModifiedTime";

    // Read the attributes
    Map<String, Object> attribs = Files.readAttributes(path, attribList);

    System.out.format("Size:%s, Last   Modified   Time:%s %n",
        attribs.get("size"), attribs.get("lastModifiedTime"));

  }
}

例2

以下代码读取文件C:\Java_Dev\test1.txt的基本文件属性,并在标准输出上打印其中的一些。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {
  public static void main(String[] args) {
    Path path = Paths.get("C:\\Java_Dev\\test1.txt");

    try {
      BasicFileAttributes bfa = Files.readAttributes(path,
          BasicFileAttributes.class);
      System.out.format("Size:%s bytes %n", bfa.size());
      System.out.format("Creation Time:%s %n", bfa.creationTime());
      System.out.format("Last Access  Time:%s %n", bfa.lastAccessTime());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

我们还可以使用特定的视图对象读取文件属性。

我们可以使用如下的Files类的getFileAttributeView()方法来获取特定的属性视图。如果文件属性视图不可用,则返回null。

<V extends FileAttributeView> V  getFileAttributeView(Path path,  Class<V> type, LinkOption... options)

在获得特定视图类型的视图对象之后,我们可以使用视图对象的readAttributes()方法读取该视图类型的所有属性。

例3

以下代码使用基本视图对象读取C:\Java_Dev\test1.txt文件的所有基本属性:

Path  path   = Paths.get("C:\\Java_Dev\\test1.txt");
BasicFileAttributeView bfv = Files.getFileAttributeView(path,  BasicFileAttributeView.class);
BasicFileAttributes bfa  = bfv.readAttributes();

以下代码显示如何使用基本文件属性视图来读取和更新基本文件属性。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Instant;

public class Main {
  public static void main(String[] args) {
    Path path = Paths.get("C:\\Java_Dev\\test1.txt");

    try {
      BasicFileAttributeView bfv = Files.getFileAttributeView(path,
          BasicFileAttributeView.class);
      BasicFileAttributes bfa = bfv.readAttributes();

      System.out.format("Size:%s bytes %n", bfa.size());
      System.out.format("Creation  Time:%s %n", bfa.creationTime());
      System.out.format("Last Access  Time:%s %n", bfa.lastAccessTime());

      FileTime newLastModifiedTime = null;
      FileTime newLastAccessTime = null;
      FileTime newCreateTime = FileTime.from(Instant.now());

      bfv.setTimes(newLastModifiedTime, newLastAccessTime, newCreateTime);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Java 文件内容

Java IO教程 - Java文件内容

文件的内容类型

Files.probeContentType(Path path)方法探测文件的内容类型。

该方法以多用途Internet邮件扩展(MIME)内容类型的值的字符串形式返回内容类型。

如果一个文件的内容类型不能确定,则返回null。

以下代码显示如何探测文件的内容类型。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    Path p = Paths.get("C:\\Java_Dev\\test1.txt");

    try {
      String contentType = Files.probeContentType(p);
      System.out.format("Content type   of  %s  is %s%n", p, contentType);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

上面的代码生成以下结果。

读取文件的内容

Files类包含以下方法来读取文件的内容作为字节和文本行:

  • static byte[] readAllBytes(Path path) - 读取文件中的所有字节。
  • static List readAllLines(Path path) - 读取文本文本行的整个内容。
  • static List readAllLines(Path path, Charset cs)

Files类可以从Path对象获取InputStream和BufferedReader对象。

newInputStream(Path path,OpenOption ... options)方法返回指定路径的InputStream对象。它假定文件的内容是UTF-8字符集。

newBufferedReader(Path path)和newBufferedReader(Path path,Charset cs)方法返回一个BufferedReader。我们可以指定字符集。

Files类提供了使用其newByteChannel(Path path,OpenOption ... options)方法从Path对象中获取SeekableByteChannel对象的方法。

OpenOption类型配置正在打开的文件。下表列出了OpenOption类型的值及其描述。OpenOption是java.nio.file包中的一个接口。

java.nio.file包中的StandardOpenOption枚举实现了OpenOption接口。

标准打开选项描述
APPEND将写入的数据附加到现有文件,如果文件被打开写入。
CREATE创建一个新文件,如果它不存在。
CREATE_NEW创建一个新文件,如果它不存在。如果文件已存在,则操作失败。
DELETE_ON_CLOSE关闭流时删除文件。 在与临时文件一起使用时非常有用。
DSYNC保持文件的内容与底层存储同步。
READ打开具有读访问权限的文件。
SPARSE如果它与CREATE_NEW选项一起使用,它对文件系统提示新文件应该是稀疏文件。
SYNC保持文件的内容和元数据与底层存储同步。
TRUNCATE_EXISTING如果打开文件以进行写访问,则将现有文件的长度截断为零。
WRITE打开文件以进行写访问。

以下代码在默认目录中为test2.txt文件获取一个SeekableByteChannel对象。

它打开文件以进行读取和写入访问。它使用CREATE选项,因此如果文件不存在,则创建该文件。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;

import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) throws Exception {
    Path src = Paths.get("test2.txt");
    SeekableByteChannel sbc = Files.newByteChannel(src, READ, WRITE, CREATE);
  }
}

以下代码演示了如何读取和显示我们默认目录中test1.txt文件的内容。 如果文件不存在,程序将显示一条错误消息。

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class Main {
  public static void main(String[] args) throws Exception{
    Charset cs = Charset.forName("US-ASCII");
    Path source = Paths.get("test1.txt");

    List<String> lines = Files.readAllLines(source, cs);
    for (String line : lines) {
        System.out.println(line);
    }
  }
}

写入文件

我们可以使用Files类的以下write()方法将内容写入文件。

static Path  write(Path path, byte[]  bytes,  OpenOption... options)
static Path  write(Path path, Iterable lines, OpenOption... options)
static Path  write(Path path, Iterable lines, Charset cs, OpenOption... options)

write()方法打开文件,将传递的内容写入文件,并关闭它。

如果没有打开选项,它将使用CREATE,TRUNCATE_EXISTING和WRITE选项打开文件。

如果我们正在向文件写入文本,它会写一个平台相关的行分隔符。

如果在写入文本行时未指定charset,则假定使用UTF-8字符集。

以下代码演示如何使用write()方法将文本行写入文件。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class Main {
  public static void main(String[] args) {
    List<String> texts = new ArrayList<>();
    texts.add("test");
    texts.add("test");
    Path dest = Paths.get("twinkle.txt");
    Charset cs = Charset.forName("US-ASCII");
    try {
      Path p = Files.write(dest, texts, cs, WRITE, CREATE);
      System.out.println("Text was written to " + p.toAbsolutePath());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Files.newOutputStream(Path path,OpenOption ... options)返回指定路径的OutputStream。

Files.newBufferedWriter(Path path,Charset cs,OpenOption ...选项)方法为指定的路径返回BufferedWriter。

上面的代码生成以下结果。

随机访问文件

SeekableByteChannel对象提供对文件的随机访问。

我们可以使用Files类的newByteChannel()方法为Path获取一个SeekableByteChannel对象,如下所示:

Path  src = Paths.get("test.txt"); 
SeekableByteChannel seekableChannel  = Files.newByteChannel(src, READ,  WRITE,  CREATE,  TRUNCATE_EXISTING);

我们可以使用size()方法以字节为单位获取SeekableByteChannel实体的大小。

由于数据被截断或写入通道,因此更新了大小。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) {
    Path src = Paths.get("test.txt");
    String encoding = System.getProperty("file.encoding");
    Charset cs = Charset.forName(encoding);
    try (SeekableByteChannel seekableChannel = Files.newByteChannel(src, READ,
        WRITE, CREATE, TRUNCATE_EXISTING)) {
      printDetails(seekableChannel, "Before writing data");
      writeData(seekableChannel, cs);
      printDetails(seekableChannel, "After writing data");

      seekableChannel.position(0);
      printDetails(seekableChannel, "After resetting position to 0");
      readData(seekableChannel, cs);
      printDetails(seekableChannel, "After reading data");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public static void writeData(SeekableByteChannel seekableChannel, Charset cs)
      throws IOException {
    String separator = System.getProperty("line.separator");
    StringBuilder sb = new StringBuilder();
    sb.append("test");
    sb.append(separator);
    sb.append("test2");
    sb.append(separator);

    CharBuffer charBuffer = CharBuffer.wrap(sb);
    ByteBuffer byteBuffer = cs.encode(charBuffer);
    seekableChannel.write(byteBuffer);
  }

  public static void readData(SeekableByteChannel seekableChannel, Charset cs)
      throws IOException {
    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
    String encoding = System.getProperty("file.encoding");
    while (seekableChannel.read(byteBuffer) > 0) {
      byteBuffer.rewind();
      CharBuffer charBuffer = cs.decode(byteBuffer);
      System.out.print(charBuffer);
      byteBuffer.flip();
    }
  }

  public static void printDetails(SeekableByteChannel seekableChannel,
      String msg) {
    try {
      System.out.println(msg + ": Size   = " + seekableChannel.size()
          + ", Position = " + seekableChannel.position());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

上面的代码生成以下结果。

Java 文件树

Java IO教程 - Java文件树

FileVisitor API可以递归地处理文件树中的所有文件和目录。

当我们要对文件树中的所有或某些文件或目录执行某些操作时,FileVisitor API非常有用。

SimpleFileVisitor类是FileVisitor接口的基本实现。

当访问文件/目录时,SimpleFileVisitor类不执行任何操作。我们可以从SimpleFileVisitor类继承我们的文件访问类,并且只覆盖适合我们需要的方法。

FileVisitor接口的方法:

ID含义
1FileVisitResult preVisitDirectory(T dir,BasicFileAttributes attrs) 在访问目录中的条目之前调用一次。
2FileVisitResult postVisitDirectory(T dir,IOException exc) 已访问目录中的后调用项。如果在目录的迭代期间抛出了任何异常,则将异常对象作为第二个参数传递给此方法。如果此方法的第二个参数为null,则在目录迭代期间没有异常。
3FileVisitResult visitFile(T文件,BasicFileAttributes attrs) 当访问目录中的文件时调用。
4FileVisitResult visitFileFailed(T文件,IOException exc) 当由于任何原因而无法访问文件或目录时调用。

下表列出了FileVisitResult的枚举常量及其说明

枚举常量描述
CONTINUE继续处理
SKIP_SIBLINGS继续处理而不访问文件或目录的兄弟节点。
SKIP_SUBTREE继续处理,而不访问目录中的条目。
TERMINATE终止文件访问过程。

我们不需要在我们的文件访问类的所有四个方法中编写逻辑。要复制目录,请使用preVisitDirectory()方法来创建一个新目录和visitFile()方法来复制文件。

以下代码显示如何打印目录的子目录和文件的名称。

import static java.nio.file.FileVisitResult.CONTINUE;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {
  public static void main(String[] args) {
    Path startDir = Paths.get("");
    FileVisitor<Path> visitor = getFileVisitor();
    try {
      Files.walkFileTree(startDir, visitor);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static FileVisitor<Path> getFileVisitor() {
    class DirVisitor<Path> extends SimpleFileVisitor<Path> {
      @Override
      public FileVisitResult preVisitDirectory(Path dir,
          BasicFileAttributes attrs) {

        System.out.format("%s [Directory]%n", dir);
        return CONTINUE;
      }

      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        System.out.format("%s [File,  Size: %s  bytes]%n", file, attrs.size());
        return CONTINUE;
      }
    }
    FileVisitor<Path> visitor = new DirVisitor<>();
    return visitor;
  }
}

上面的代码生成以下结果。

例子

以下代码显示如何使用FileVisitor API删除目录树。

import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.TERMINATE;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Main {
  public static void main(String[] args) {
    Path dirToDelete = Paths.get("DIR");
    FileVisitor<Path> visitor = getFileVisitor();

    try {
      Files.walkFileTree(dirToDelete, visitor);
    }
    catch (IOException e) {
      System.out.println(e.getMessage());
    }
  }

  public static FileVisitor<Path> getFileVisitor() {

    class DeleteDirVisitor extends SimpleFileVisitor<Path> {
      @Override
      public FileVisitResult postVisitDirectory(Path dir, IOException e)
          throws IOException {
        FileVisitResult result = CONTINUE;
        if (e != null) {
          System.out.format("Error deleting  %s.  %s%n", dir, e.getMessage());
          result = TERMINATE;
        } else {
          Files.delete(dir);
          System.out.format("Deleted directory  %s%n", dir);
        }
        return result;
      }

      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
        Files.delete(file);
        System.out.format("Deleted file %s%n", file);
        return CONTINUE;
      }
    }
    FileVisitor<Path> visitor = new DeleteDirVisitor();
    return visitor;
  }
}

上面的代码生成以下结果。

例2

以下代码显示如何使用walkFileTree()方法跟随符号链接。

import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Set;
import static  java.nio.file.FileVisitOption.FOLLOW_LINKS;
public class Main {
  public static void main(String[] args) throws Exception {
    Path startDir = Paths.get("");
    FileVisitor<Path> visitor = create your visitor;

    Set<FileVisitOption> options = EnumSet.of(FOLLOW_LINKS);

    int depth = Integer.MAX_VALUE;

    Files.walkFileTree(startDir, options, depth, visitor);
  }
}

模式匹配

我们可以使用glob和正则表达式模式对字符串形式的Path对象执行模式匹配。

功能接口PathMatcher用于执行匹配。它包含一个方法matches(Path path)方法,如果指定的路径匹配模式,则该方法返回true。

模式字符串由两部分组成,语法和模式由冒号分隔:

syntax:pattern

语法的值是glob或regex。模式部分遵循取决于语法部分的值的语法。

glob模式使用以下语法规则:

  • * 匹配零个或多个字符,而不会交叉目录边界。
  • ** 匹配零个或多个字符跨目录边界。
  • ? 只匹配一个字符。
  • \ 转义以下字符的特殊含义。
  • \\ 匹配单个反斜杠。
  • \* 匹配星号。

放在括号 [] 中的字符称为括号表达式,它匹配单字符。[aeiou]匹配a,e,i,o或u。

两个字符之间的破折号指定范围。[a-z]匹配a和z之间的所有字母。

左括号后的感叹号(!)被视为否定。[!abc]匹配除a,b和c之外的所有字符。

通过在大括号({})中指定逗号分隔的子模式来使用一组子模式。例如,{txt,java,doc}匹配txt,java和doc。

路径的根组件的匹配是实现相关的。

以下代码显示了如何使用PathMatcher对象将路径与glob模式匹配。

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;

public class Main {
  public static void main(String[] args) {
    String globPattern = "glob:**txt";
    PathMatcher matcher = FileSystems.getDefault().getPathMatcher(globPattern);
    Path path = Paths.get("C:\\Java_Dev\\test1.txt");
    boolean matched = matcher.matches(path);
    System.out.format("%s matches  %s:  %b%n", globPattern, path, matched);
  }
}

上面的代码生成以下结果。

 Java 文件所有者权限

Java IO教程 - Java文件所有者权限

有三种方法可以管理文件所有者:

  • 使用Files.getOwner()和Files.setOwner()方法。
  • 使用“owner”作为属性名称的Files.getAttribute()和Files.setAttribute()方法。
  • 使用FileOwnerAttributeView。

我们需要使用UserPrincipal和GroupPrincipal接口来管理文件的所有者。

文件的所有者可以是用户或组。

UserPrincipal表示用户。GroupPrincipal表示组。

当我们读取文件的所有者时,我们得到一个UserPrincipal的实例。在UserPrincipal对象上调用getName()方法以获取用户的名称。

要设置文件的所有者,请从用户名获取UserPrincipal的对象。

要从文件系统获取UserPrincipal,请使用UserPrincipalLookupService类的实例,我们可以使用FileSystem类的getUserPrincipalLookupService()方法获取该实例。

以下代码为用户ID为myName的用户获取一个UserPrincipal对象:

FileSystem fs  = FileSystems.getDefault();
UserPrincipalLookupService upls  = fs.getUserPrincipalLookupService();

UserPrincipal user = upls.lookupPrincipalByName("myName"); 
System.out.format("User principal name is %s%n", user.getName());

以下代码显示如何使用FileOwnerAttributeView更改文件的所有者。

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.attribute.UserPrincipalLookupService;

public class Main {
  public static void main(String[] args) throws Exception {

      Path path = Paths.get("C:\\Java_Dev\\test1.txt");

      FileOwnerAttributeView foav = Files.getFileAttributeView(path,
          FileOwnerAttributeView.class);

      UserPrincipal owner = foav.getOwner();
      System.out.format("Original owner  of  %s  is %s%n", path,
          owner.getName());

      FileSystem fs = FileSystems.getDefault();
      UserPrincipalLookupService upls = fs.getUserPrincipalLookupService();

      UserPrincipal newOwner = upls.lookupPrincipalByName("brice");
      foav.setOwner(newOwner);

      UserPrincipal changedOwner = foav.getOwner();
      System.out.format("New owner  of  %s  is %s%n", path,
          changedOwner.getName());

  }
}

以下代码使用Files.setOwner()方法更新在Windows上使用路径C:\Java_Dev\test1.txt标识的文件的所有者:

UserPrincipal owner  = get   the   owner;
Path  path   = Paths.get("C:\\Java_Dev\\test1.txt"); 
Files.setOwner(path,  owner);

ACL文件权限

Microsoft Windows上支持ACL类型文件属性。

ACL由访问控制条目的有序列表组成。每个条目由一个UserPrincipal,访问类型和对对象的访问级别组成。

AclEntry类表示ACL中的条目。

使用AclFileAttributeView的getAcl()和setAcl()方法获取和设置文件的AclEntry列表。

以下代码获取名为C:\Java_Dev\test1.txt的文件的ACL条目列表:

Path  path   = Paths.get("C:\\Java_Dev\\test1.txt"); 
AclFileAttributeView view  = Files.getFileAttributeView(path,  AclFileAttributeView.class); 
List<AclEntry> aclEntries = view.getAcl();

AclEntry类可以读取ACL条目的各种属性。其principal()方法返回UserPrincipal以标识用户或组。

permissions()从AclEntry返回一组AclEntryPermission对象以标识权限。

来自AclEntry的type()方法返回类型AclEntryType的枚举常量,例如ALARM,ALLOW,AUDIT和DENY,用于指示访问类型。

来自AclEntry的flags()方法返回一组AclEntryFlag枚举常量,其中包含ACL条目的继承标志。

以下代码显示如何读取文件C:\Java_Dev\test1.txt的ACL条目。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclFileAttributeView;
import java.util.List;
import java.util.Set;

public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("C:\\Java_Dev\\test1.txt");
    AclFileAttributeView aclView = Files.getFileAttributeView(path,
        AclFileAttributeView.class);
    if (aclView == null) {
      System.out.format("ACL view  is not  supported.%n");
      return;
    }
    List<AclEntry> aclEntries = aclView.getAcl();
    for (AclEntry entry : aclEntries) {
      System.out.format("Principal: %s%n", entry.principal());
      System.out.format("Type: %s%n", entry.type());
      System.out.format("Permissions are:%n");

      Set<AclEntryPermission> permissions = entry.permissions();
      for (AclEntryPermission p : permissions) {
        System.out.format("%s %n", p);
      }

    }

  }
}

例子

以下代码显示如何为名为brice的用户添加新的ACL条目。它在C:\Java_Dev\test1.txt文件中为用户添加DATA_READ和DATA_ WRITE权限。

import static java.nio.file.attribute.AclEntryPermission.READ_DATA;
import static java.nio.file.attribute.AclEntryPermission.WRITE_DATA;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclEntryType;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.UserPrincipal;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("C:\\Java_Dev\\test1.txt");

    AclFileAttributeView aclView = Files.getFileAttributeView(path,
        AclFileAttributeView.class);
    if (aclView == null) {
      System.out.format("ACL view  is not  supported.%n");
      return;
    }
    UserPrincipal bRiceUser = FileSystems.getDefault()
        .getUserPrincipalLookupService().lookupPrincipalByName("brice");

    Set<AclEntryPermission> permissions = EnumSet.of(READ_DATA, WRITE_DATA);

    AclEntry.Builder builder = AclEntry.newBuilder();
    builder.setPrincipal(bRiceUser);
    builder.setType(AclEntryType.ALLOW);
    builder.setPermissions(permissions);
    AclEntry newEntry = builder.build();

    List<AclEntry> aclEntries = aclView.getAcl();

    aclEntries.add(newEntry);

    aclView.setAcl(aclEntries);
  }
}

POSIX文件权限

UNIX支持POSIX标准文件属性。POSIX文件权限由九个组件组成:

  • three for the owner
  • three for the group
  • three for others

这三种类型的权限是读,写和执行。

字符串形式的典型POSIX文件权限看起来像“rw-rw ----",它具有所有者和组的读取和写入权限。

PosixFilePermission枚举类型定义九个常量,每个权限组件一个。

九个常数命名为X_Y,其中X是OWNER,GROUP和OTHERS,Y是READ,WRITE和EXECUTE。

PosixFilePermissions是一个实用程序类,它具有将文件的POSIX权限从一种形式转换为另一种形式的方法。

它的toString()方法将一组PosixFilePermission枚举常量转换为rwxrwxrwx形式的字符串。

它的fromString()方法将rwxrwxrwx形式的字符串中的POSIX文件权限转换为一组PosixFilePermission枚举常量。

它的asFileAttribute()方法将一组PosixFilePermission枚举常量转换为FileAttribute对象。

以下代码以默认目录中名为test的文件的rwxrwxrwx格式读取和打印POSIX文件权限:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;

public class Main {
  public static void main(String[] argv)throws Exception {
    Path path = Paths.get("test");
    PosixFileAttributeView posixView = Files.getFileAttributeView(path,
        PosixFileAttributeView.class);
    PosixFileAttributes attribs = posixView.readAttributes();
    Set<PosixFilePermission> permissions = attribs.permissions();
    // Convert the file permissions into the rwxrwxrwx string form
    String rwxFormPermissions = PosixFilePermissions.toString(permissions);
    // Print the permissions
    System.out.println(rwxFormPermissions);
  }
}

要更新POSIX文件权限,请调用PosixFileAttributeView的setPermissions()方法,将PosixFilePermission枚举常量的Set作为参数传递。

以下代码显示如何设置POSIX文件权限:

String rwxFormPermissions = "rw-r-----";
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(rwxFormPermissions);

posixView.setPermissions(permissions);

以下代码直接创建一组PosixFilePermission枚举常量,并将其设置为文件权限。

Set<PosixFilePermission> permissions = EnumSet.of(OWNER_READ,  OWNER_WRITE, GROUP_READ);
posixView.setPermissions(permissions);

以下代码演示如何在类似UNIX的平台上读取和更新名为test的文件的POSIX文件权限。

import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.EnumSet;
import java.util.Set;

public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("test");
    PosixFileAttributeView posixView = Files.getFileAttributeView(path,
        PosixFileAttributeView.class);
    if (posixView == null) {
      System.out.format("POSIX attribute view  is not  supported%n.");
      return;
    }
    readPermissions(posixView);
    updatePermissions(posixView);
  }

  public static void readPermissions(PosixFileAttributeView posixView)
      throws Exception {
    PosixFileAttributes attribs;
    attribs = posixView.readAttributes();
    Set<PosixFilePermission> permissions = attribs.permissions();
    // Convert the set of posix file permissions into rwxrwxrwx form
    String rwxFormPermissions = PosixFilePermissions.toString(permissions);
    System.out.println(rwxFormPermissions);
  }
  public static void updatePermissions(PosixFileAttributeView posixView)
      throws Exception {
    Set<PosixFilePermission> permissions = EnumSet.of(OWNER_READ, OWNER_WRITE,
        GROUP_READ);
    posixView.setPermissions(permissions);
    System.out.println("Permissions set successfully.");
  }
}

上面的代码生成以下结果。

Java 目录事件 

Java IO教程 - Java目录事件

当文件系统中的对象被修改时,我们可以监听watch服务以获取警报。

java.nio.file包中的以下类和接口提供watch服务。

  • Watchable接口
  • WatchService接口
  • WatchKey接口
  • WatchEvent接口
  • WatchEvent.Kind接口
  • StandardWatchEventKinds类

可监视对象表示可以被监视的文件系统对象。可观看对象可以向手表服务注册。

Path对象是一个Watchable对象。

WatchService表示观察服务。当一个对象使用WatchService注册时,WatchService返回一个WatchKey作为注册的令牌。

WatchEvent表示注册到监视服务的对象上的事件。它的kind()方法返回发生的事件的类型。

它的context()方法返回一个Path对象,它表示事件发生的条目。

count()方法返回特定通知的事件发生次数。 如果它返回大于1的值,那么它是一个重复的事件。

WatchEvent.Kind <T>表示发生的事件的类型。

StandardWatchEventKinds类定义了用于表示事件种类的常量,如下所示。

  • ENTRY_CREATE
  • ENTRY_DELETE
  • ENTRY_MODIFY
  • OVERFLOW

OVERFLOW表示丢失或丢弃的事件。

创建观察服务以观察目录以进行更改。

WatchService ws = FileSystems.getDefault().newWatchService();

要使用Watch服务注册目录,使用register()方法,该方法将返回一个WatchKey对象作为注册令牌。

// Get  a  Path  object for C:\myName  directory  to watch
Path  dirToWatch  = Paths.get("C:\\myName");
WatchKey token   = dirToWatch.register(ws, ENTRY_CREATE,  ENTRY_MODIFY,  ENTRY_DELETE);

要取消注册,请使用WatchKey的cancel()方法。

当注册目录时,其WatchKey处于就绪状态。

我们可以通过手表服务注册多个目录。

要从监视服务队列中检索WatchKey,使用WatchService对象的take()或poll()方法来检索和删除发出信号并排队的WatchKey。

take()方法等待,直到WatchKey可用。poll()方法允许我们为等待指定超时。

以下代码使用无限循环来检索发出信号的WatchKey。

while(true)  {
    WatchKey key  = ws.take();
}

处理事件

WatchKey的pollEvents()方法检索并删除所有挂起的事件。它返回一个WatchEvent的列表。 List的每个元素代表WatchKey上的一个事件。

以下代码显示了处理事件的典型逻辑:

while(true)  {
    WatchKey key  = ws.take();
    // Process all  events of  the   WatchKey 
    for(WatchEvent<?> event  : key.pollEvents())  {
       // Process each  event here
    }
}

处理事件后重置WatchKey

我们需要重置WatchKey对象,通过调用其reset()方法来再次接收事件通知。

reset()方法将WatchKey置于就绪状态。如果WatchKey仍然有效,reset()方法返回true。 否则,它返回false。

如果WatchKey被取消或其监视服务关闭,它可能会失效。

// Reset   the   WatchKey
boolean isKeyValid = key.reset();
if (!isKeyValid)  {
    System.out.println("No  longer  watching "  + dirToWatch);
}

WatchService是可自动关闭的。我们可以在try-with-resources中创建一个WatchService的对象块,当程序退出块时它将自动关闭。

例子

以下代码显示了如何实现监视服务以监视目录中的更改。

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class Main {
  public static void main(String[] args) {
    try (WatchService ws = FileSystems.getDefault().newWatchService()) {
      Path dirToWatch = Paths.get("C:\\myName");
      dirToWatch.register(ws, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
      while (true) {
        WatchKey key = ws.take();
        for (WatchEvent<?> event : key.pollEvents()) {
          Kind<?> eventKind = event.kind();
          if (eventKind == OVERFLOW) {
            System.out.println("Event  overflow occurred");
            continue;
          }
          WatchEvent<Path> currEvent = (WatchEvent<Path>) event;
          Path dirEntry = currEvent.context();
          System.out.println(eventKind + "  occurred on  " + dirEntry);
        }
        boolean isKeyValid = key.reset();
        if (!isKeyValid) {
          System.out.println("No  longer  watching " + dirToWatch);
          break;
        }
      }
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }
}

Java 异步I/O

Java IO教程 - Java异步I/O

在同步文件I/O中,对I/O操作的请求将等待,直到I/O操作完成。

在异步文件I/O中,I/O操作的请求由系统异步执行。

当系统完成文件I/O时,它通知应用程序其请求的完成。

java.nio.channels.AsynchronousFileChannel类表示异步文件通道。

AsynchronousFileChannel类的静态open()方法获取AsynchronousFileChannel类的实例。

以下代码显示了如何获取WRITE的异步文件通道。

Path  path   = Paths.get("C:\\Java_Dev\\rainbow.txt");
AsynchronousFileChannel afc   = AsynchronousFileChannel.open(path, WRITE,  CREATE);

AsynchronousFileChannel提供了两种方法来处理异步文件I/O操作的结果。

  • Using a java.util.concurrent.Future object.
  • Using a java.nio.channels.CompletionHandler object.

支持异步文件I/O操作的AsynchronousFileChannel类的每个方法有两个版本。

一个版本返回一个Future对象,我们可以使用它来处理所请求的异步操作的结果。

Future对象的get()方法返回写入文件通道的字节数。

以下代码使用返回Future对象的write()方法的版本:

ByteBuffer dataBuffer  = a buffer;
long  startPosition = 0;
Future<Integer> result = afc.write(dataBuffer, startPosition);

一旦我们得到一个Future对象,我们可以使用轮询方法或阻塞等待方法来处理异步文件I/O的结果。

下面的代码显示了轮询方法,它将继续调用Future对象的isDone()方法,以检查I/O操作是否完成:

while (!result.isDone()) {
}
int writtenNumberOfBytes = result.get();

AsynchronousFileChannel类的另一个版本的方法获得一个CompletionHandler对象,当请求的异步I/O操作完成或失败时,该对象的方法被调用。

CompletionHandler接口有两个方法:completed()和failed()。

当所请求的I/O操作成功完成时,将调用completed()方法。

当请求的I/O操作时失败,则调用failed()方法。

以下代码使用Attachment类的对象作为完成处理程序的附件:

class  Attachment {
    public Path  path;
    public  ByteBuffer buffer;
    public  AsynchronousFileChannel asyncChannel;
}
class MyHandler implements CompletionHandler<Integer,  Attachment>   {
    @Override
    public void  completed(Integer result, Attachment attach)  {
        // Handle  completion of  the   I/O  operation
    }
    
    @Override
    public void  failed(Throwable e,  Attachment attach)  {
        // Handle  failure of  the   I/O  operation
    }
}

以下代码使用MyHandler实例作为异步写操作的完成处理程序。

MyHandler handler = new MyHandler();
ByteBuffer dataBuffer  = get   a  data buffer;
Attachment attach  = new Attachment(); 
attach.asyncChannel = afc; 
attach.buffer = dataBuffer; 
attach.path = path;

// Perform  the   asynchronous write operation 
afc.write(dataBuffer, 0, attach, handler);

以下代码演示了如何使用CompletionHandler对象来处理对文件的异步写入的结果。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("test.txt");
    AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, WRITE,
        CREATE);
    WriteHandler handler = new WriteHandler();
    ByteBuffer dataBuffer = getDataBuffer();
    Attachment attach = new Attachment();
    attach.asyncChannel = afc;
    attach.buffer = dataBuffer;
    attach.path = path;

    afc.write(dataBuffer, 0, attach, handler);

    System.out.println("Sleeping for 5  seconds...");
    Thread.sleep(5000);
  }
  public static ByteBuffer getDataBuffer() {
    String lineSeparator = System.getProperty("line.separator");
    StringBuilder sb = new StringBuilder();
    sb.append("test");
    sb.append(lineSeparator);
    sb.append("test");
    sb.append(lineSeparator);
    String str = sb.toString();
    Charset cs = Charset.forName("UTF-8");
    ByteBuffer bb = ByteBuffer.wrap(str.getBytes(cs));
    return bb;
  }
}
class Attachment {
  public Path path;
  public ByteBuffer buffer;
  public AsynchronousFileChannel asyncChannel;
}

class WriteHandler implements CompletionHandler<Integer, Attachment> {
  @Override
  public void completed(Integer result, Attachment attach) {
    System.out.format("%s bytes written  to  %s%n", result,
        attach.path.toAbsolutePath());
    try {
       attach.asyncChannel.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void failed(Throwable e, Attachment attach) {
    try {
      attach.asyncChannel.close();
    } catch (IOException e1) {
      e1.printStackTrace();
    }
  }
}

例子

以下代码演示了如何使用Future对象来处理对文件的异步写入的结果。

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.WRITE;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Future;

public class Main {
  public static ByteBuffer getDataBuffer() {
    String lineSeparator = System.getProperty("line.separator");

    StringBuilder sb = new StringBuilder();
    sb.append("test");
    sb.append(lineSeparator);

    String str = sb.toString();
    Charset cs = Charset.forName("UTF-8");
    ByteBuffer bb = ByteBuffer.wrap(str.getBytes(cs));

    return bb;
  }

  public static void main(String[] args) throws Exception {
    Path path = Paths.get("test.txt");

    try (AsynchronousFileChannel afc = AsynchronousFileChannel.open(path,
        WRITE, CREATE)) {
      ByteBuffer dataBuffer = getDataBuffer();
      Future<Integer> result = afc.write(dataBuffer, 0);
      while (!result.isDone()) {
        System.out.println("Sleeping for 2  seconds...");
        Thread.sleep(2000);
      }
      int writtenBytes = result.get();
      System.out.format("%s bytes written  to  %s%n", writtenBytes,
          path.toAbsolutePath());

    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

上面的代码生成以下结果。

例2

以下代码演示了如何使用CompletionHandler对象来处理从文件进行异步读取的结果。

import static java.nio.file.StandardOpenOption.READ;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
  public static void main(String[] args) throws Exception{
    Path path = Paths.get("test.txt");
    AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, READ);
    ReadHandler handler = new ReadHandler();
    int fileSize = (int) afc.size();
    ByteBuffer dataBuffer = ByteBuffer.allocate(fileSize);

    Attachment attach = new Attachment();
    attach.asyncChannel = afc;
    attach.buffer = dataBuffer;
    attach.path = path;

    afc.read(dataBuffer, 0, attach, handler);

    System.out.println("Sleeping for 5  seconds...");
    Thread.sleep(5000);
  }
}
class Attachment {
  public Path path;
  public ByteBuffer buffer;
  public AsynchronousFileChannel asyncChannel;
}

class ReadHandler implements CompletionHandler<Integer, Attachment> {
  @Override
  public void completed(Integer result, Attachment attach) {
    System.out.format("%s bytes read   from  %s%n", result, attach.path);
    System.out.format("Read data is:%n");
    byte[] byteData = attach.buffer.array();
    Charset cs = Charset.forName("UTF-8");
    String data = new String(byteData, cs);
    System.out.println(data);
    try {
      // Close the channel
      attach.asyncChannel.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void failed(Throwable e, Attachment attach) {
    System.out.format("Read operation  on  %s  file failed."
        + "The  error is: %s%n", attach.path, e.getMessage());
    try {
      // Close the channel
      attach.asyncChannel.close();
    } catch (IOException e1) {
      e1.printStackTrace();
    }
  }
}

上面的代码生成以下结果。

例3

以下代码显示了如何使用Future对象来处理从文件进行异步读取的结果。它使用等待方法(Future.get()方法调用)等待异步文件I/O完成。

import static java.nio.file.StandardOpenOption.READ;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class Main {
  public static void main(String[] args) throws Exception {
    Path path = Paths.get("test.txt");

    try (AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, READ)) {
      int fileSize = (int) afc.size();
      ByteBuffer dataBuffer = ByteBuffer.allocate(fileSize);

      Future<Integer> result = afc.read(dataBuffer, 0);
      int readBytes = result.get();

      System.out.format("%s bytes read   from  %s%n", readBytes, path);
      System.out.format("Read data is:%n");

      byte[] byteData = dataBuffer.array();
      Charset cs = Charset.forName("UTF-8");
      String data = new String(byteData, cs);

      System.out.println(data);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

上面的代码生成以下结果。


http://www.kler.cn/a/372322.html

相关文章:

  • Elasticsearch DSL版
  • 【QT】实现RestFul接口
  • html 音频和视频组件
  • Redis 使用redisTemplate获取某个规则下的key的全量数据(示例Set结构)
  • javaEE-多线程进阶-JUC的常见类
  • 如何在IDEA一个窗口中导入多个项目
  • 牛客网刷题(3)(Java的几种常用包)
  • 实操|如何优雅的实现RAG与GraphRAG应用中的知识文档增量更新?
  • Webserver(1.8)操作函数
  • CSS常见适配布局方式
  • 逆变器竞品分析--倍思500W方案【2024/10/30】
  • Android 快捷方式
  • 海外共享奶牛牧场投资源码-理财金融源码-基金源码-共享经济源码
  • 《掌握 Java:从基础到高级概念的综合指南》(3/15)
  • 多GPU训练大语言模型,DDP, ZeRO 和 FSDP
  • 【再谈设计模式】单例模式~唯一性的守护者
  • Dockerfile制作Oracle19c镜像
  • xpath爬虫
  • 多线程显示 CSV 2 PNG 倒计时循环播放
  • 低功耗模组学习指南:从入门到精通通过MQTT连接实现远程控制
  • 如何在不同设备上轻松下载Facebook应用:全面指南
  • AI助力医疗数据自动化:诊断报告识别与管理
  • TCP全连接队列与 tcpdump 抓包
  • vue点击菜单,出现2个相同tab,啥原因
  • 代码备份管理 —— Git实用操作
  • Spring Boot框架下的酒店住宿登记系统