Java的IO相关内容。
NIO2
文件系统访问
java.nio.file.Path类用来表示一个路径。该类有许多路径相关的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | public static void main(String[] args) throws IOException { 	Path path = Paths.get("/Users/Mark/Project", "Base.java");    System.out.println(path);     System.out.println(path.toAbsolutePath());     System.out.println(path.toRealPath());     System.out.println(path.getFileName());     System.out.println(path.getNameCount());     System.out.println(path.getRoot());     System.out.println(path.getParent());     for (int i = 0; i < path.getNameCount(); i++) {        System.out.println(path.getName(i));     }     	path = Paths.get("src/main/java/", "com/mark/nio"); 	System.out.println(path);  	System.out.println(path.subpath(0, 3));  	 	System.out.println(path.resolve("test"));  	System.out.println(path.resolveSibling("io"));  	 	Path other = Paths.get("src/main/resources");  	System.out.println(other.toAbsolutePath());   	path = path.relativize(other);  	System.out.println(path);  	System.out.println(path.toAbsolutePath());  	System.out.println(path.toAbsolutePath().normalize());  }
  | 
 
列出目录的子目录和文件可以使用File.list和File.listFiles方法,NIO2中可以使用java.nio.file.DirectoryStream来进行遍历目录。DirectoryStream接口实现了Closeable和Iterable接口。通过Files.newDirectoryStream方法获得DirectoryStream实例,可以通过DirectoryStream.Filter来过滤。
1 2 3 4 5
   | Path path = Paths.get(""); DirectoryStream<Path> ds = Files.newDirectoryStream(path, "*.java");  for (Path d : ds) {   System.out.println(d); }
  | 
 
如果想递归遍历整个目录,可以使用Files.walkFileTree方法和java.nio.file.FileVisitor接口。FileVisitor接口有四个方法
- visitFile  正在访问某个文件
 
- visitFileFailed 访问某个文件出现错误
 
- preVisitDirectory 在访问一个目录中的子目录和文件之前调用
 
- postVisitDirectory 在访问一个目录中的子目录和文件之后调用
 
这四个方法都返回FileVisitResult,这是一个枚举类型,有四个值CONTINUE、TERMINATE、SKIP_SUBTREE 和 SKIP_SIBLINGS。
该接口有一个默认实现SimpleFileVisitor。我们可以继承这个类来实现我们自己的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   |  Path start = Paths.get("");    Files.walkFileTree(start, new SimpleFileVisitor<Path>() {        @Override        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {            if (file.getFileName().toString().endsWith(".java")) {                System.out.println(file.toAbsolutePath());            }            return FileVisitResult.CONTINUE;        }
         @Override        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {        		            if (".git".equals(dir.getFileName().toString()) || ".idea".equals(dir.getFileName().toString())) {                return FileVisitResult.SKIP_SUBTREE;            }            return FileVisitResult.CONTINUE;        }
         @Override        public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {            return super.visitFileFailed(file, exc);        }
         @Override        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {            return super.postVisitDirectory(dir, exc);        }    });
 
  | 
 
文件的属性信息由java.nio.file.attribute.BasicFileAttributes、PosixFileAttributes、DosFileAttributes这几个接口定义。可以通过java.nio.file.attribute.BasicFileAttributeView、DosFileAttributeView、PosixFileAttributeView的readAttributes方法来得到。Files类也提供了一系列静态方法来获得文件的属性信息。不同类型的FileAttributes会有不同的方法。
1 2 3 4 5 6 7 8 9 10 11
   | Path path = Paths.get("pom.xml"); PosixFileAttributeView fav = Files.getFileAttributeView(path, PosixFileAttributeView.class); PosixFileAttributes attributes = fav.readAttributes(); System.out.println(attributes.group()); System.out.println(attributes.owner()); System.out.println(attributes.permissions()); System.out.println(attributes.creationTime()); System.out.println(attributes.isRegularFile()); System.out.println(attributes.lastAccessTime()); System.out.println(attributes.lastModifiedTime()); System.out.println(attributes.size());
  | 
 
对一个目录进行监视可以通过java.nio.file.WatchService接口来实现,通过将要监视的目录和感兴趣的事件注册到WatchService,事件类StandardWatchEventKinds中定义了一些事件,在事件发生的时候就可以通过WatchKey来获得事件列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | Path this_dir = Paths.get("").toAbsolutePath();    System.out.println("Now watching the current directory " + this_dir.toString());    try {        WatchService watcher = this_dir.getFileSystem().newWatchService();        this_dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
         while (true) {            WatchKey watchKey = watcher.take();            List<WatchEvent<?>> events = watchKey.pollEvents();            for (WatchEvent event : events) {                Path context = (Path) event.context();                 context = this_dir.resolve(context);                 System.out.println("Someone just created the file " + context.toString());                System.out.println(Files.size(context));            }            watchKey.reset();         }    } catch (IOException | InterruptedException e) {        System.out.println("Error: " + e.toString());    }
  | 
 
zip/jar文件系统
在Java中可以通过FileSystem和FileSystemProvider这两个抽象类来实现自定义的文件系统。
Java类库中包含两种文件系统,基于操作系统的文件系统和zip/jar文件系统。
例子:将一个文件添加到zip压缩包中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
   | public static void main(String[] args) throws IOException {    File testZip = new File("归档.zip");    File fileToAdd = new File("demo.txt");    if (testZip.exists() && fileToAdd.exists()) {        addFile(testZip, fileToAdd);    } }
 
  原先的方法: 先创建一个tmp文件,然后将原压缩文件中的数据写入tmp文件,再把要加入的文件数据写入tmp文件 最后删了原压缩文件,并把tmp文件改名为原压缩文件 */ private static void addFile(File zip, File file) throws IOException {    File tmp = File.createTempFile(zip.getName(), null);    int len = -1;    try (ZipInputStream input = new ZipInputStream(new FileInputStream(zip));        ZipOutputStream output = new ZipOutputStream(new FileOutputStream(tmp))) {        ZipEntry entry = input.getNextEntry();        byte[] buf = new byte[10240];        while (entry != null) {            String name = entry.getName();            if (!file.getName().equals(name)) {                output.putNextEntry(new ZipEntry(name));                while ((len = input.read(buf)) != -1) {                    output.write(buf, 0, len);                }                output.closeEntry();            }            entry = input.getNextEntry();        }        try (FileInputStream newFileInput = new FileInputStream(file)) {            output.putNextEntry(new ZipEntry(file.getName()));            System.out.println(newFileInput.available());            while ((len = newFileInput.read(buf)) != -1) {                output.write(buf, 0, len);            }            output.closeEntry();        }        zip.delete();        tmp.renameTo(zip);    } }
 
  private static void addFileToZip(File zip, File file) throws IOException {    Map<String, String> env = new HashMap<>();    env.put("create", "true");                try (FileSystem fs = FileSystems.newFileSystem(URI.create("jar:" + zip.toURI()), env)) {        Path path = file.toPath();        Path pathInZip = fs.getPath("/test/" + file.getName());        Files.copy(path, pathInZip, StandardCopyOption.REPLACE_EXISTING);    } }
  | 
 
异步I/O通道
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | private static void server() throws IOException {    AsynchronousChannelGroup group = AsynchronousChannelGroup.withFixedThreadPool(10, Executors.defaultThreadFactory());    AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress(8680));    channel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {        @Override        public void completed(AsynchronousSocketChannel result, Void attachment) {        	   channel.accept(null, this);            try {                System.out.println(result.getRemoteAddress());                result.write(ByteBuffer.wrap("hi there!".getBytes(StandardCharsets.UTF_8)));                result.close();            } catch (IOException e) {                e.printStackTrace();            }        }
         @Override        public void failed(Throwable exc, Void attachment) {            throw new RuntimeException(exc);        }
     }); }
  private static void writeToFile() throws IOException, InterruptedException, ExecutionException {    AsynchronousFileChannel afc = AsynchronousFileChannel.open(Paths.get("demo.txt"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);    Future<Integer> future = afc.write(ByteBuffer.wrap("hello world".getBytes()), 0);    System.out.println(future.get()); }
  | 
 
AsynchronousServerSocketChannel实现了NetworkChannel接口,该接口是是与网络套接字相关的通道,提供了绑定和配置相关的方法。