资讯专栏INFORMATION COLUMN

Java NIO

Steve_Wang_ / 1583人阅读

摘要:缓冲区的容量不可能为负值,创建后不能改变界限界限第一个不应该被读写或者写入的缓冲区位置索引。当使用从中读取数据时,的值恰好等于已经读到了多少数据。

NIO:New IO

Java新IO概述

新IO采用内存映射文件的方式来处理输入/输出,新IO文件或文件的一段区域映射到内存中,这样就可以访问内存一样来访问文件了(这种方式模拟了操作系统上的虚拟内存的概念),通过这种方式来进行输入/输出比传统的输入/输出要快得多

Java中NIO相关的包如下:

java.nio包:主要提供了一些和Buffer相关的类

java.nio.channels包:主要包括Channel和Selector相关的类

java.nio.charset包:主要包含和字符集相关的类

java.nio.channels.spi包:主要包含提供Channel服务的类

java.nio.charset.spi包:主要包含提供字符集服务的相关类

Channel(通道)和Buffer(缓冲)是新IO中的两个核心对象,Channel是对传统输入/输出系统中的模拟,在新IO系统中所有数据都需要通过通道传输;Channel与传统的InputStream、OutputStream最大的区别在于它提供了一个map方法,通过该map方法可以直接将“一块数据”映射到内存中。如果说传统的输入/输出系统是面向流的处理,而新IO则是面向块的处理

Buffer可以被理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先读到Buffer中。此处的Buffer有点类似于前面我们介绍的“竹筒”,但该Buffer既可以像前面那样一次、一次去Channel中取水,也允许使用Channel直接将文件的某块数据映射成Buffer

除了Channel和Buffer之外,新IO还提供了用于将UNICODE字符串映射成字节序列以及逆映射操作的Charset类,还提供了用于支持非阻塞式输入/输出的Selector类

使用Buffer

Buffer类没有提供构造器,通过使用如下方法来得到一个Buffer对象:

static XxxBuffer allocate(int capacity):创建一个容量为capacity的XxxBuffer对象

使用较多的是ByteBuffer和CharBuffer。其中ByteBuffer类还有一个子类:MappedByteBuffer,它用于表示Channel将磁盘文件的部分或全部内容映射到内存中后得到的结果,通常MappedByteBuffer对象由Channel的map()方法返回

Buffer三个重要概念:容量(capacity)、界限(limit)、位置(position)

容量(capacity):缓冲区的容量(capacity)表示该Buffer的最大数据容量,即最多可以存储多少数据。缓冲区的容量不可能为负值,创建后不能改变
界限(limit)

界限(limit):第一个不应该被读写或者写入的缓冲区位置索引。也就是说,位于limit后的数据既不可被读,也不可被写

位置(position):用于指明下一个可以被读出或者写入的缓冲区位置索引(类似于IO流中的记录指针)。当使用Buffer从Channel中读取数据时,position的值恰好等于已经读到了多少数据。当刚刚新建一个Buffer对象时,其position为0;如果从Channel中读取了2个数据到该Buffer中,则position为2,指向Buffer中的第三个(第1个位置的索引为0)位置

标记(mark):Buffer里还支持一个可选的标记(mark,类似于传统IO流中的mark),Buffer允许直接将position定位到该mark处。

0 <= mark <= position <= limit <= capacity

Buffer的主要作用就是装入数据,然后输出数据,开始时Buffer的position为0,limit为capacity,程序可通过put()方法向Buffer中放入一些数据(或从channel获取数据),每放入一些数据,position向后移动一些位置

当Buffer装入数据结束后,调用filp()方法,该方法将limit设置为position所在位置,将position设置为0。这样使得从Buffer中读取数据总是从0开始。读完所有装入的数据即结束,也就是说,Buffer调用filp后,Buffer为输出数据做好了准备

当Buffer输出数据结束后,调用clear方法。将position置为0,将limit置为capacity,这样为再次向Buffer中装载数据做好准备

Buffer抽象类常用方法:

int capacity():返回Buffer的capacity大小

boolean hasRemaining():判断当前位置(position)和界限(limit)之间是否有元素可供处理

int limit():返回Buffer的界限(limit)的位置

Buffer limit(int newLimit):重新设置界限(limit)的值,并返回一个具有新的limit的缓冲区对象

int position():返回Buffer的position值

Buffer position(new position):设置Buffer的position值,并返回position被修改后的Buffer对象

int remaining():返回当前位置和界限(limit)之间的元素个数

Buffer reset():将位置(position)转到mark所在的位置

Buffer rewind():将位置(position)设置为0,取消设置的mark

put()和get()方法,用于向Buffer中放入数据和从Buffer中取出数据。当使用put()和get()。Buffer既支持对单个数据的访问,也支持对批量数据的访问(以数组作为参数)

当使用put()和get()来访问Buffer中的数据时,分为相对和绝对两种:

相对(Relative):从Buffer当前位置读取或写入数据,然后将位置(position)的值按处理元素个数增加

绝对(Absolute):直接根据索引来向Buffer中读取或写入数据,使用绝对方式来访问Buffer里的数据,并不会影响position的值

import java.nio.*;

public class BufferTest
{
    public static void main(String[] args)
    {
        // 创建Buffer
        CharBuffer buff = CharBuffer.allocate(8);    // ①
        System.out.println("capacity: "    + buff.capacity());
        System.out.println("limit: " + buff.limit());
        System.out.println("position: " + buff.position());
        // 放入元素
        buff.put("a");
        buff.put("b");
        buff.put("c");      // ②
        System.out.println("加入三个元素后,position = " + buff.position());
        // 调用flip()方法
        buff.flip();      // ③
        System.out.println("执行flip()后,limit = " + buff.limit());
        System.out.println("position = " + buff.position());
        // 取出第一个元素
        System.out.println("第一个元素(position=0):" + buff.get());  // ④
        System.out.println("取出一个元素后,position = " + buff.position());
        // 调用clear方法
        buff.clear();     // ⑤
        System.out.println("执行clear()后,limit = " + buff.limit());
        System.out.println("执行clear()后,position = " + buff.position());
        System.out.println("执行clear()后,缓冲区内容并没有被清除:" + "第三个元素为:" +  buff.get(2));    // ⑥
        System.out.println("执行绝对读取后,position = " + buff.position());
    }
}

运行结果:

capacity: 8
limit: 8
position: 0
加入三个元素后,position = 3
执行flip()后,limit = 3
position = 0
第一个元素(position=0):a
取出一个元素后,position = 1
执行clear()后,limit = 8
执行clear()后,position = 0
执行clear()后,缓冲区内容并没有被清除:第三个元素为:c
执行绝对读取后,position = 0

代码①:新分配的CharBuffer对象:

代码②:向Buffer中放入3个对象后

代码③:执行Buffer的flip()方法后

代码⑤:执行clear()后的Buffer

使用Channel

Channel类似于传统的流对象,但与传统的流对象有两个主要区别:

Channel可以直接将指定文件的部分或全部直接映射成Buffer

程序不能直接访问Channel中的数据,包括读、写入都不行,Channel只能与Buffer进行交互。也就是说,如果要从Channel中取得数据,必须先用Buffer从Channel中取出一些数据,然后让程序从Buffer中取出这些数据;如果要将程序中的数据写入Channel,一样先让程序将谁放入Buffer中,程序再将Buffer里的数据写入Channel中

所有的Channel都不应该通过构造器来直接创建,而是通过传统的节点InputStream、OutputStream的getChannel()方法来返回对应的Channel,不同的节点流获得的Channle不一样

Channel中最常用的三类方法是map()、read()和write()

map()方法用于将Channel对应的部分或全部数据映射成ByteBuffer;而read()或write()方法都有一系列重载形式,这些方法用于从Buffer中读取数据或向Buffer里写入数据

map方法的方法签名为:MappedByteBuffer map(FileChannel.MapMode mode, long position, long size),第一个参数执行映射时的模式,分别有只读,读写模式,而第二个,第三个参数用于控制将Channel的哪些数据映射成ByteBuffer

以下是直接将FileChannel的全部数据映射成ByteBuffer的效果的代码。使用FileInputStream、FileOutputStream来获取FileChannel。代码①直接将指定Channel中的全部数据映射成ByteBuffer,代码②直接将整个ByteBuffer的全部数据写入一个输出FileChannel中,完成文件复制。使用Charset类和CharsetDecoder类将ByteBuffer转换成CharBuffer

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class FileChannelTest
{
    public static void main(String[] args)
    {
        File f = new File("FileChannelTest.java");
        try(
            // 创建FileInputStream,以该文件输入流创建FileChannel
            FileChannel inChannel = new FileInputStream(f).getChannel();
            // 以文件输出流创建FileBuffer,用以控制输出
            FileChannel outChannel = new FileOutputStream("a.txt").getChannel())
        {
            // 将FileChannel里的全部数据映射成ByteBuffer
            MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());   // ①
            // 使用GBK的字符集来创建解码器
            Charset charset = Charset.forName("GBK");
            // 直接将buffer里的数据全部输出
            outChannel.write(buffer);     // ②
            // 再次调用buffer的clear()方法,复原limit、position的位置
            buffer.clear();
            // 创建解码器(CharsetDecoder)对象
            CharsetDecoder decoder = charset.newDecoder();
            // 使用解码器将ByteBuffer转换成CharBuffer
            CharBuffer charBuffer =  decoder.decode(buffer);
            // CharBuffer的toString方法可以获取对应的字符串
            System.out.println(charBuffer);
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }
}

RandomAccessFile中也包含getChannel()方法,返回的FileChannel()读写类型取决于RandomAccessFile打开文件的模式。以下代码将对a.txt文件的内容进行复制,追加到该文件后面:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class RandomFileChannelTest
{
    public static void main(String[] args) throws IOException
    {
        File f = new File("a.txt");
        try(
            // 创建一个RandomAccessFile对象
            RandomAccessFile raf = new RandomAccessFile(f, "rw");
            // 获取RandomAccessFile对应的Channel
            FileChannel randomChannel = raf.getChannel())
        {
            // 将Channel中所有数据映射成ByteBuffer
            ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
            // 把Channel的记录指针移动到最后
            randomChannel.position(f.length());
            // 将buffer中所有数据输出
            randomChannel.write(buffer);
        }
    }
}

randomChannel.position(f.length()); 代码将Channel的记录指针移动到该Channel的最后,从而可以让程序将指定ByteBuffer的数据追加到该Channel后面。每次运行上面程序,都会把a.txt文件的内容复制一份,并将全部内容追加到该文件的后面

使用map()方法一次将所有的文件内容映射到内存中引起性能下降,可以使用Channel和Buffer传统的“用竹筒多次重复取水”的方式:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class ReadFile
{
    public static void main(String[] args)
        throws IOException
    {
        try(
            // 创建文件输入流
            FileInputStream fis = new FileInputStream("ReadFile.java");
            // 创建一个FileChannel
            FileChannel fcin = fis.getChannel())
        {
            // 定义一个ByteBuffer对象,用于重复取水
            ByteBuffer bbuff = ByteBuffer.allocate(256);
            // 将FileChannel中数据放入ByteBuffer中
            while( fcin.read(bbuff) != -1 )
            {
                // 锁定Buffer的空白区
                bbuff.flip();
                // 创建Charset对象
                Charset charset = Charset.forName("GBK");
                // 创建解码器(CharsetDecoder)对象
                CharsetDecoder decoder = charset.newDecoder();
                // 将ByteBuffer的内容转码
                CharBuffer cbuff = decoder.decode(bbuff);
                System.out.print(cbuff);
                // 将Buffer初始化,为下一次读取数据做准备
                bbuff.clear();
            }
        }
    }
}

Buffer提供了flip()和clear()两个方法,每次读取数后调用flip()方法将没有数据的区域“封印”起来,避免程序从Buffer中取出null值;数据取出后立即调用clear()方法将Buffer的position设0,为下一次读取数据做准备

字符集和Charset

编码Encode:把明文的字符序列换成计算机理解的二进制序列
解码Decode:把二进制序列转换成明文字符串

Java默认使用Unicode字符集,但很多操作系统并不使用Unicode字符集,那么当从系统中读取数据到Java程序中时,就可能出现乱码等问题

JDK1.4提供了Charset来处理字节序列和字符序列(字符串)之间的转换关系,该类包含了用于创建编码和解码的的方法,还提供了获取所有Charset所支持的字符集的方法,Charset类是不可变的

availableCharset()的静态方法用于获取当前JDK所支持的所有字符集

import java.nio.charset.*;
import java.util.*;

public class CharsetTest
{
    public static void main(String[] args)
    {
        // 获取Java支持的全部字符集
        SortedMap  map = Charset.availableCharsets();
        for (String alias : map.keySet())
        {
            // 输出字符集的别名和对应的Charset对象
            System.out.println(alias + "----->"
                + map.get(alias));
        }
    }
}

常用字符串别名:

GBK:简体中文字符串

BIG5:繁体中文字符串

ISO-8859-1:ISO拉丁字母表No.1

UTF-8:8位UCS转换格式

UTF-16BE:16位的UCS转换格式,Big-endian(最低地址存放高位字节)字节顺序

UTF-16LE:16位的UCS转换格式,Little-endian(最高地址存放低位字节)字节顺序

UTF-16:16位的UCS转换格式,字节顺序由可选的字节顺序标记来标识

调用Charset的forName()方法创建字符串别名对应的Charset对象,forName()方法的参数就是相应字符集的别名:
Charset cs = Charset.forName("ISO-8859-1");

获得Charset对象之后,通过该对象的newDecoder()、newEncoder()方法分别返回CharsetDecoder和CharsetEncoder对象,代表该Charset的解码器和编码器。调用CharsetDecoder的decode()方法可以将ByteBuffer(字节序列)转换成CharBuffer(字符序列),调用CharsetEncoder的encode()方法可以将CharBuffer或String(字符序列)转换成ByteBuffer(字节序列)

import java.nio.*;
import java.nio.charset.*;

public class CharsetTransform
{
    public static void main(String[] args)
        throws Exception
    {
        // 创建简体中文对应的Charset
        Charset cn = Charset.forName("GBK");
        // 获取cn对象对应的编码器和解码器
        CharsetEncoder cnEncoder = cn.newEncoder();
        CharsetDecoder cnDecoder = cn.newDecoder();
        // 创建一个CharBuffer对象
        CharBuffer cbuff = CharBuffer.allocate(8);
        cbuff.put("内");
        cbuff.put("马");
        cbuff.put("尔");
        cbuff.flip();
        // 将CharBuffer中的字符序列转换成字节序列
        ByteBuffer bbuff = cnEncoder.encode(cbuff);
        // 循环访问ByteBuffer中的每个字节
        for (int i = 0; i < bbuff.capacity() ; i++)
        {
            System.out.print(bbuff.get(i) + " ");
        }
        // 将ByteBuffer的数据解码成字符序列
        System.out.println("
" + cnDecoder.decode(bbuff));
    }
}

Charset类提供了如下三个方法:

CharBuffer decode(ByteBuffer bb):将ByteBuffer中的字节序列转换成字符序列的便捷方法

ByteBuffer encode(CharBuffer cb):将CharBuffer中的字节序列转换成字符序列的便捷方法

ByteBuffer encode(String str):将String中的字节序列转换成字符序列的便捷方法

获取Charset对象后,如果仅仅需要进行简单的编码、解码操作,实则无须创建CharsetDecoder和CharsetEncoder对象,直接调用Charset的encode()和decode()方法进行编码、解码即可

文件锁

如果多个运行的程序需要并发修改同一个文件时,程序之间需要某种机制来进行通信,使用文件锁可以有效阻止多个进程并发修改同一个文件

Java提供了FileLock类支持文件锁功能,在FileChannel中提供的lock()/tryLock()方法可以获得文件锁FileLock对象,从而锁定文件

lock()方法和trylock()方法的区别是:当lock()试图锁定某个文件时,如果无法得到文件锁,程序将一直阻塞,而tryLock()方法时尝试锁定文件,它将直接返回而不是阻塞,如果获得了文件锁,该方法则返回该文件锁,否则将返回null

lock(long position, long size, boolean shared):对文件从position开始,长度为size的内容加锁,该方法是阻塞式的

tryLock(long position, long size, boolean shared):非阻塞式的加锁方法

当shared为true时,表明该锁是一个共享锁,它将允许多个进程来读取文件,但不允许其他进程将其设为排他锁;当shared为false的时,表明这是一个排他锁,它将锁住对该文件的读写

通过调用FileLock的isShared来判断获得的锁是否为共享锁

直接使用lock()或tryLock()方法获取的文件锁是排他锁

处理完成之后,调用FileLock的release()方法释放文件锁

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

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

        try(
            // 使用FileOutputStream获取FileChannel
            FileChannel channel = new FileOutputStream("a.txt").getChannel())
        {
            // 使用非阻塞式方式对指定文件加锁
            FileLock lock = channel.tryLock();
            // 程序暂停10s
            Thread.sleep(10000);
            // 释放锁
            lock.release();
        }
    }
}
Java7的NIO2

NIO的改进主要包括如下方面:

提供了全面的文件IO和文件系统访问支持

基于异步Channel的IO

Path、Paths和File核心API

Paths提供了get(String first, String... more)方法来获取Path对象,Paths会将给定的多个字符串连缀成路径,如Paths.get("D:", "java","code")将返回D:javacode路径;getNameCount()返回Path路径所包含的路径名

import java.io.*;
import java.net.*;
import java.nio.file.*;

public class PathTest
{
    public static void main(String[] args)
        throws Exception
    {
        // 以当前路径来创建Path对象
        Path path = Paths.get(".");
        System.out.println("path里包含的路径数量:" + path.getNameCount());
        System.out.println("path的根路径:" + path.getRoot());
        // 获取path对应的绝对路径
        Path absolutePath = path.toAbsolutePath();
        System.out.println(absolutePath);
        // 获取绝对路径的根路径
        System.out.println("absolutePath的根路径:" + absolutePath.getRoot());
        // 获取绝对路径所包含的路径数量
        System.out.println("absolutePath里包含的路径数量:" + absolutePath.getNameCount());
        System.out.println(absolutePath.getName(3));
        // 以多个String来构建Path对象
        Path path2 = Paths.get("D:", "java","code");
        System.out.println(path2);
    }
}

运行结果:

path里包含的路径数量:1
path的根路径:null
D:DevelopmenteclipseworkspaceCrazyJava.
absolutePath的根路径:D:
absolutePath里包含的路径数量:5
CrazyJava
D:javacode

以下代码示范Files工具类的用法:

import java.nio.file.*;
import java.nio.charset.*;
import java.io.*;
import java.util.*;

public class FilesTest
{
    public static void main(String[] args)
        throws Exception
    {
        // 复制文件
        Files.copy(Paths.get("FilesTest.java"), new FileOutputStream("a.txt"));
        // 判断FilesTest.java文件是否为隐藏文件
        System.out.println("FilesTest.java是否为隐藏文件:"+ Files.isHidden(Paths.get("FilesTest.java")));
        // 一次性读取FilesTest.java文件的所有行
        List lines = Files.readAllLines(Paths.get("FilesTest.java"), Charset.forName("gbk"));
        System.out.println(lines);
        // 判断指定文件的大小
        System.out.println("FilesTest.java的大小为:" + Files.size(Paths.get("FilesTest.java")));
        List poem = new ArrayList<>();
        poem.add("感时花溅泪");
        poem.add("恨别鸟惊心");
        // 直接将多个字符串内容写入指定文件中
        Files.write(Paths.get("pome.txt"), poem, Charset.forName("gbk"));
        // 使用Java 8新增的Stream API列出当前目录下所有文件和子目录
        Files.list(Paths.get(".")).forEach(path -> System.out.println(path));
        // 使用Java 8新增的Stream API读取文件内容
        Files.lines(Paths.get("FilesTest.java"), Charset.forName("gbk")).forEach(line -> System.out.println(line));
        FileStore cStore = Files.getFileStore(Paths.get("C:"));
        // 判断C盘的总空间,可用空间
        System.out.println("C:共有空间:" + cStore.getTotalSpace());
        System.out.println("C:可用空间:" + cStore.getUsableSpace());
    }
}
使用FileVisitor遍历文件和目录

Files类提供了两个方法来遍历文件和子目录

walkFileTree(Path start, FileVisitor visitor):遍历start路径下的所有文件和子目录

walkFileTree(Path start, Set options, int maxDepth, FileVisitor visitor):遍历start路径下的所有文件和子目录,最多遍历maxDepth深度的文件

FileVisitor代表一个文件访问器,walkFileTree()方法会自动遍历start路径下的所有文件和子目录,遍历文件和子目录都会触发FileVisitor中相应的方法。这四个方法在下面的代码中出现。FileVisitor中定义了如下4个方法:

FileVisitResult postVisitDirectory(T dir, IOException exc):访问子目录之后触发该方法

FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs):访问子目录之前触发该方法

FileVisitResult visitFile(T file, BasicFileAttributes attrs):访问file文件时触发该方法

FileVisitResult visitFileFailed(T file, IOException exc):访问file文件失败时触发该方法

FileVisitResult对象,它是一个枚举类,代表访问之后的后续行为,它有如下几种后续行为:

CONTINUE:代表“继续访问”的后续行为

TERMINATE:代表“终止访问”的后续行为

SKIP_SUBTREE:代表“继续访问“,但不访问该目录文件或目录的子目录树

SKIP_SIBLINGS:代表“继续访问”,但不访问该文件或目录的兄弟文件或目录

以下程序使用Files工具类的walkFileTree()方法遍历g:publishcodes15目录下的所有文件和子目录,直到找到的文件以“FileVisitorTest.java”结尾,则程序停止遍历。实现了对指定目录进行搜索,直到找到指定文件为止

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class FileVisitorTest
{
    public static void main(String[] args)
        throws Exception
    {
        // 遍历D:codingJava路径CrazyJavacodes15目录下的所有文件和子目录
        Files.walkFileTree(Paths.get("D:", "coding", "Java路径", "CrazyJava", "codes", "15"), 
        new SimpleFileVisitor()
        {
            // 访问文件时候触发该方法
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
            {
                System.out.println("正在访问" + file + "文件");
                // 找到了FileVisitorTest.java文件
                if (file.endsWith("FileVisitorTest.java"))
                {
                    System.out.println("--已经找到目标文件--");
                    return FileVisitResult.TERMINATE;
                }
                return FileVisitResult.CONTINUE;
            }
            // 开始访问目录时触发该方法
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
            {
                System.out.println("正在访问:" + dir + " 路径");
                return FileVisitResult.CONTINUE;
            }
        });
    }
}
使用WatchService监控文件变化

register(WatchService watcher, WatchEvent.Kind ... events):用watcher监听该path代表的目录下文件变化。events参数指定要监听哪些类型的事件

WatchService代表一个文件系统监听服务,它负责监听path代表的目录下的文件变化。一旦使用register()方法完成注册之后,可调用WatchService如下的三个方法来监听目录的文件变化事件

WatchKey poll():获取下一个WatchKey,如果没有WatchKey发生就立即返回null

WatcheKey poll(long timeout, TimeUnit unit):尝试等待timeout时间去获取下一个WatchKey

WatchKey take():获取下一个WatchKey,如果没有WatchKey发生就一直等待

如果程序需要一直监控,则应该选择使用take()方法,如果程序只需要监控指定时间,则使用poll方法

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class WatchServiceTest
{
    public static void main(String[] args) throws Exception
    {
        // 获取文件系统的WatchService对象
        WatchService watchService = FileSystems.getDefault().newWatchService();
        // 为C:盘根路径注册监听
        Paths.get("C:/").register(watchService
            , StandardWatchEventKinds.ENTRY_CREATE
            , StandardWatchEventKinds.ENTRY_MODIFY
            , StandardWatchEventKinds.ENTRY_DELETE);
        while(true)
        {
            // 获取下一个文件改动事件
            WatchKey key = watchService.take();    //①
            for (WatchEvent event : key.pollEvents())
            {
                System.out.println(event.context() +" 文件发生了 "
                    + event.kind()+ "事件!");
            }
            // 重设WatchKey
            boolean valid = key.reset();
            // 如果重设失败,退出监听
            if (!valid)
            {
                break;
            }
        }
    }
}
访问文件属性

Java7的NIO.2在java.nio.file.attribute包下提供了大量工具类,可以简单地读取、修改文件属性。分为如下两类:

XxxAttributeView:代表某种文件属性的“视图”

XxxAttributes:代表某种文件属性的“集合”,程序一般通过XxxAttributeView对象获取XxxAttributes

FileAttributeView是其他XxxAttributeView的父接口

AclFileAttributeView: 为特定文件设置ACL(Access Control List)及文件所有者属性。getAcl()方法返回List对象,该返回值代表该文件的权限集,setAcl(List)方法修改该文件的ACL

BaseFileAttributeView:获取或修改文件的基本属性。readAttributes()方法返回一个BaseFileAttributeView对象,对文件夹基本属性的修改是通过BaseFileAttributeView对象完成的

DosFileAttributeView:获取或修改文件DOS相关属性。readAttributes()方法返回一个DosFileAttributeView对象,对文件夹属性的修改通过DosFileAttributeView对象完成的

FileOwnerAttributeView:获取或修改文件的所有者。getOwner()方法返回一个UserPrincipal对象来代表文件所有者;也可调用setOwner(UserPrincipal owner)方法来改变文件的所有者

PosixFileAttributeView:获取文件或修改POSIX(Portable Operating System Interface of INIX)属性。readAttributes()方法返回一个PosixFileAttributeView对象,该对象用于获取或修改文件的所有者、组所有者、访问权限信息。仅在UNIX、Linux等系统上有用

UserDefinedFileAttributeView:开发者自定义文件属性

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66608.html

相关文章

  • Java NIO 系列教程

    摘要:异步可以让你异步的使用,例如当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。因此,单个的线程可以监听多个数据通道。下面是系列文章的目录概述通道之间的数据传输与原文译者郭蕾校对方腾飞 Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的...

    fanux 评论0 收藏0
  • Java NIO 概览

    摘要:线程之间的切换对于操作系统来说是昂贵的。因此,单线程可以监视多个通道中的数据。当方法返回后,线程可以处理这些事件。 一 NIO简介 Java NIO 是 java 1.4 之后新出的一套IO接口,这里的的新是相对于原有标准的Java IO和Java Networking接口。NIO提供了一种完全不同的操作方式。 NIO中的N可以理解为Non-blocking,不单纯是New。 它支持面...

    chemzqm 评论0 收藏0
  • 关于Java IO与NIO知识都在这里

    摘要:从通道进行数据写入创建一个缓冲区,填充数据,并要求通道写入数据。三之通道主要内容通道介绍通常来说中的所有都是从通道开始的。从中选择选择器维护注册过的通道的集合,并且这种注册关系都被封装在当中停止选择的方法方法和方法。 由于内容比较多,我下面放的一部分是我更新在我的微信公众号上的链接,微信排版比较好看,更加利于阅读。每一篇文章下面我都把文章的主要内容给列出来了,便于大家学习与回顾。 Ja...

    Riddler 评论0 收藏0
  • Java NIO 的前生今世 之一 简介

    摘要:简介是由引进的异步由以下几个核心部分组成和的对比和的区别主要体现在三个方面基于流而基于操作是阻塞的而操作是非阻塞的没有概念而有概念基于与基于传统的是面向字节流或字符流的而在中我们抛弃了传统的流而是引入了和的概念在中我只能从中读取数据到中或将 简介 Java NIO 是由 Java 1.4 引进的异步 IO.Java NIO 由以下几个核心部分组成: Channel Buffer Se...

    李义 评论0 收藏0
  • 动力节点JavaNIO教程,轻松攻破Java NIO技术壁垒

    摘要:学习和掌握技术已经不是一个攻城狮的加分技能,而是一个必备技能。是双向的,不仅可以读取数据还能保存数据,程序不能直接读写通道,只与缓冲区交互为了让大家不被高并发与大量连接处理问题所困扰,动力节点推出了高效处理模型应用教程。 大家肯定了解Java IO, 但是对于NIO一般是陌生的,而现在使用到NIO的场景越来越多,很多技术框...

    ralap 评论0 收藏0
  • Netty序章之BIO NIO AIO演变

    摘要:后改良为用线程池的方式代替新增线程,被称为伪异步。最大的问题是阻塞,同步。每次请求都由程序执行并返回,这是同步的缺陷。这些都会被注册在多路复用器上。多路复用器提供选择已经就绪状态任务的能力。并没有采用的多路复用器,而是使用异步通道的概念。 Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能、高可靠的网络服务器和客户端程序。Netty简化了网络程序的开发,是很多框架和公司...

    VincentFF 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<