• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
风吹花落泪如雨
博客园    首页    新随笔    联系   管理    订阅  订阅

JAVA输入/输出(四)-----NIO(new IO)

一、新IO概述

新IO采用内存映射文件的方式来处理输入/输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了。

 

二、使用Buffer

 

容量 (capacity) :表示 Buffer 最大数据容量,一旦声明后,不能更改。通过Buffer中的capacity()获取。缓冲区capacity不能为负。

 

限制 (limit):第一个不应该读取或写入的数据的索引,即位于 limit 
后的数据不可读写。通过Buffer中的limit()获取。缓冲区的limit不能为负,并且不能大于其capacity。

 

位置 (position):当前要读取或写入数据的索引。通过Buffer中的position()获取。缓冲区的position不能为负,并且不能大于其limit。

 

标记 (mark):标记是一个索引,通过 Buffer 中的 mark() 方法将mark标记为当前position位置。 
之后可以通过调用 reset() 方法将 position恢复到标记的mark处。

 

标记、位置、限制、容量遵守以下不变式: 
0 <= mark <= position <= limit <= capacity

 

flip():封印Buffer中没有数据的空间。limit设置为position,position设置为0。

clear()方法:将position设为0,limit设为capacity。

import java.nio.CharBuffer;

public class Buffer {
    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("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
position = 0
执行clear()后,缓冲区内容并没有被清除:  第三个元素为: c
执行绝对读取后,position = 0

 

三、使用Channel

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

map(): 用于将Channel对应的部分或全部数据映射成ByteBuffer;而Read()和write()拥有一系列重载的方法,用于从Buffer中读取数据或向Buffer中写入数据。

MappedByteBuffer map(FileChannel.MapMode mode, long position, long size):mode只能为只读,读写。

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("aaa.txt");
        //虽然FileChannel既可以读取也可以写入,但FileInputStream获取的FileChannel只能读,而FileOutputStream获取的FileChannel只能写。
        try(FileChannel inChannel = new FileInputStream(f).getChannel();
            FileChannel outChannel = new FileOutputStream("bbb.txt").getChannel())
        {
            //将FileChannel里的全部数据映射成ByteBuffer
            MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
            //使用GBK的字符集来创建解码器
            Charset charset = Charset.forName("GBK");
            //直接将Buffer里的数据全部输出到"bbb.txt"文件
            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 e) {
            e.printStackTrace();
        }
    }
}

RandomAccessFile中也包含了一个getChannel()。下面程序将Channel的记录指针移动到该Channel的最后,从而可以让程序将指定的ByteBuffer的数据追加到该Channel后面。

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

public class RandomFileChannelTest {

    public static void main(String[] args) throws IOException {
        File f = new File("aaa.txt");
        //创建一个Random
        try(RandomAccessFile raf = new RandomAccessFile(f,"rw");
            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);
        }
    }
}

 

四、字符集和Charset

import java.nio.charset.Charset;
import java.util.SortedMap;

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

 

CharSetEncoder将CharBuffer或String(字符序列)转换成ByteBuffer(字节序列)

CharSetDecoder将ByteBuffer(字节序列)转换成CharBuffer(字符序列)

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

public class CharsetTransform {
    public static void main(String[] args) throws CharacterCodingException {
        //创建简体中文对应的GBK
        Charset cn = Charset.forName("GBK");
        //创建对应的编码器和解码器
        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);
        //循环访问bbuff中的每个字节
        for(int i = 0; i < bbuff.capacity(); i++) {
            System.out.println(bbuff.get(i) + " ");
        }
        //将ByteBuff中的数据解码成字符序列
        System.out.println("\n" + cnDecoder.decode(bbuff));
    }
}

 

五、文件锁

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

shared:true,共享锁,允许多个进程读取文件,阻止其他进程获得该文件的排他锁。

shared:false,排他锁,锁住对该文件的读写。

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

public class FileLockTest {

    public static void main(String[] args) throws Exception {
        try(FileChannel channel = new FileOutputStream("a.txt").getChannel())
        {
            //使用肥阻塞式的方式对指定文件全部数据进行枷锁
            FileLock lock = channel.tryLock();
            //程序暂停10s,其他程序无法对a.txt文件进行修改
            Thread.sleep(10000);
            //释放锁
            lock.release();
        }
    }
}

 

 

六、Java7的NIO.2(Path,Paths,Files)

Path接口:代表一个平台关的平台路径

Files工具类:包含了大量静态工具方法来操作文件

Paths工具类:包含了两个返回Path的静态工厂方法

import java.nio.file.*;

public class PathTest {

    public static void main(String[] args) {
        //以当前路径来创建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(1));
        //以多个String来构建Path对象
        Path path2 = Paths.get("g:", "publish", "codes");
        System.out.println(path2);
    }
}

熟练掌握以下Files工具类的用法,可以大大简化文件IO

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.*;
public class FilesTest {

    public static void main(String[] args) throws Exception {
        //复制文件
        Files.copy(Paths.get("aaa.txt"), new FileOutputStream("bbb.txt"));
        //判断aaa.txt是否为隐藏文件
        System.out.println(Files.isHidden(Paths.get("aaa.txt")));
        //一次性读取aaa.txt文件的所有行
        List<String> lines = Files.readAllLines(Paths.get("aaa.txt"), Charset.forName("gbk"));
        System.out.println(lines);
        //判断指定文件的大小
        System.out.println(Files.size(Paths.get("aaa.txt")));
        //将多个字符串内容写入文件
        List<String> poem = new ArrayList<>();
        poem.add("水晶潭底银鱼跃");
        poem.add("清徐风中碧竿横");
        Files.write(Paths.get("poem.txt"), poem, Charset.forName("gbk"));
        //使用Java8新增的Stream API列出当前目下所有文件和子目录
        Files.list(Paths.get(".")).forEach(path -> System.out.println(path));
        //使用Java8新增的Stream API读取文件内容
        Files.lines(Paths.get("poem.txt"), Charset.forName("gbk")).forEach(line -> System.out.println(line));
        //判断C盘的总空间,可用空间
        FileStore cStore = Files.getFileStore(Paths.get("C:"));
        System.out.println("C:总空间" + cStore.getTotalSpace());
        System.out.println("C:可用空间" + cStore.getUsableSpace());
    }
}

 

七、Java7的NIO.2(FileVisitor遍历文件和目录)

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

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

walkFileTree(Path statr, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor):最多遍历maxDepth深度的文件

 上面两个方法都需要FileVisitor参数,Filevisitor代表一个文件访问器,遍历文件和子目录都会触发Filevisitor中相应的方法:

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

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

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

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

上面4个方法返回一个FileVisitResult对象,它是一个枚举类,代表了访问之后的后续行为:

CONTINUE:继续访问

SKIP_SIBLINGS:继续访问,不访问该文件或目录的兄弟文件或目录

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

TERMINATE:中止访问

实际编程没必要 为FileVisitor4个方法都提供实现,可以通过继承SimpleFileVisitor(FileVisitor的实现类)来实现自己的“文件访问器”

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
public class FileVisitorTest {

    public static void main(String[] args) throws IOException {
        //遍历F:\Workspaces\CSMA_CD下的所有文件和子目录
        Files.walkFileTree(Paths.get("f:","Workspaces","CSMA_CD"), new SimpleFileVisitor<Path>()
        {
            //访问文件时触发该方法
            @Override
            public FileVisitResult visitFile(Path file,BasicFileAttributes attrs) {
                System.out.println("正在访问" + file + "文件");
                //找到了aaa.txt文件
                if(file.endsWith("aaa.txt")) {
                    System.out.println("以找到目标文件");
                    return FileVisitResult.TERMINATE;
                }
                return FileVisitResult.CONTINUE;
            }
            //开始访问目录时触发该方法
            @Override
            public FileVisitResult preVisitDirectory(Path dir,BasicFileAttributes attrs) {
                System.out.println("正在访问:" + dir + " 路径");
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

 

八、Java7的NIO.2(使用WatchService监控文件变化)

Path提供了一个方法来监听文件系统的变化:

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

WatchService用三个方法来获取被监听的文件变化事件:

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

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

WatchKey take():如果没有WatchKey发生就一直等待

如果需要一直监控,使用take()方法;如果程序只需要监控指定事件,使用poll()方法

import java.io.IOException;
import java.nio.file.*;
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(访问文件属性)

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

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

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
public class AttributeViewTest {

    public static void main(String[] args) throws IOException {
        //获取将要操作的文件
        Path testPath = Paths.get("aaa.txt");
        //获取访问基本属性的BasicFileAttributeView
        BasicFileAttributeView basicView = Files.getFileAttributeView(testPath, BasicFileAttributeView.class);
        //获取访问基本属性的BasicFileAttribute
        BasicFileAttributes basicAttribs = basicView.readAttributes();
        //访问文件的基本属性
        System.out.println("创建时间:" + new Date(basicAttribs.creationTime().toMillis()));
        System.out.println("最后访问时间:" + new Date(basicAttribs.lastAccessTime().toMillis()));
        System.out.println("最后修改时间:" + new Date(basicAttribs.lastModifiedTime().toMillis()));
        System.out.println("文件大小:" + basicAttribs.size());
        
        //获取访问文件属主信息的的FileOwnerAttributeView
        FileOwnerAttributeView ownerView = Files.getFileAttributeView(testPath, FileOwnerAttributeView.class);
        //获取该文件所属的用户
        System.out.println(ownerView.getOwner());
        //获取系统中guest对应的用户
        UserPrincipal user = FileSystems.getDefault().getUserPrincipalLookupService().lookupPrincipalByName("guest");
        //修改用户
        ownerView.setOwner(user);
        
        //获取访问自定义属性的UserDefinedFileAttributeView
        UserDefinedFileAttributeView userView = Files.getFileAttributeView(testPath, UserDefinedFileAttributeView.class);
        List<String> attrNames = userView.list();
        for(String name : attrNames) {
            ByteBuffer buf = ByteBuffer.allocate(userView.size(name));
            userView.read(name, buf);
            buf.flip();
            String value = Charset.defaultCharset().decode(buf).toString();
            System.out.println(name + "--->" + value);
        }
        //添加一个自定义属性
        userView.write("发行者", Charset.defaultCharset().encode("疯狂Java"));
        
        //获取访问自定义属性的UserDefinedFileAttributeView
        DosFileAttributeView dosView = Files.getFileAttributeView(testPath, DosFileAttributeView.class);
        //将文件设置隐藏,只读
        dosView.setHidden(true);
        dosView.setReadOnly(true);
    }
}

 

posted @ 2018-08-20 22:55  风吹花落泪如雨  阅读(164)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3