NIO:New IOjava
新IO采用内存映射文件的方式来处理输入/输出,新IO文件或文件的一段区域映射到内存中,这样就能够访问内存同样来访问文件了(这种方式模拟了操做系统上的虚拟内存的概念),经过这种方式来进行输入/输出比传统的输入/输出要快得多数组
Java中NIO相关的包以下:并发
java.nio包:主要提供了一些和Buffer相关的类app
java.nio.channels包:主要包括Channel和Selector相关的类dom
java.nio.charset包:主要包含和字符集相关的类eclipse
java.nio.channels.spi包:主要包含提供Channel服务的类异步
java.nio.charset.spi包:主要包含提供字符集服务的相关类ide
Channel(通道)和Buffer(缓冲)是新IO中的两个核心对象,Channel是对传统输入/输出系统中的模拟,在新IO系统中全部数据都须要经过通道传输;Channel与传统的InputStream、OutputStream最大的区别在于它提供了一个map方法,经过该map方法能够直接将“一块数据”映射到内存中。若是说传统的输入/输出系统是面向流的处理,而新IO则是面向块的处理工具
Buffer能够被理解成一个容器,它的本质是一个数组,发送到Channel中的全部对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先读到Buffer中。此处的Buffer有点相似于前面咱们介绍的“竹筒”,但该Buffer既能够像前面那样一次、一次去Channel中取水,也容许使用Channel直接将文件的某块数据映射成Bufferpost
除了Channel和Buffer以外,新IO还提供了用于将UNICODE字符串映射成字节序列以及逆映射操做的Charset类,还提供了用于支持非阻塞式输入/输出的Selector类
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能够直接将指定文件的部分或所有直接映射成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,为下一次读取数据作准备
编码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<String,Charset> 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("\n" + 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(); } } }
NIO的改进主要包括以下方面:
提供了全面的文件IO和文件系统访问支持
基于异步Channel的IO
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:\Development\eclipse\workspace\CrazyJava\. absolutePath的根路径:D:\ absolutePath里包含的路径数量:5 CrazyJava D:\java\code
如下代码示范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<String> 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<String> 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()); } }
Files类提供了两个方法来遍历文件和子目录
walkFileTree(Path start, FileVisitor<? super Path> visitor):遍历start路径下的全部文件和子目录
walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> 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:\coding\Java路径\CrazyJava\codes\15目录下的全部文件和子目录 Files.walkFileTree(Paths.get("D:", "coding", "Java路径", "CrazyJava", "codes", "15"), new SimpleFileVisitor<Path>() { // 访问文件时候触发该方法 @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; } }); } }
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<AclEntry>对象,该返回值表明该文件的权限集,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:开发者自定义文件属性