从jdk1.4开始,java提供了一系列改进的输入/输出处理的新功能,这些功能被统称为新IO(New IO,简称NIO),这些类都被放在java.nio包以及子包中,并且对原java.io包中的很多类都以NIO为基础进行了改写,新增了满足NIO的功能。
一、Buffer
与Buffer各种相关的类主要在java.nio包中
从内部结构上看Buffer就像一个数组,它可以保存多个相同类型相同的数据。
Buffer是一个抽象类,最常用的子类是ByteBuffer,可以在底层字节数组上进行get/set操作。对应其他基本数据类型(boolean除外)都有相应的Buffer类:CharBuffer,ShortBuffer,IntBuffer...等.
Buffer中有三个重要概念:
1)容量(capacity):缓冲区的容量(capacity),标识Buffer的最大数据容量,不可为负值,创建后不能改变
2)界限(limit):第一个不应该被读出或者写入的缓冲区位置索引,也就是说位于limit之后的数据既不可以被读,也不可以被写
3)位置(position):用于指明下一个可以被读出或者写入的缓冲区位置索引
@Test public void bufferTest() { //创建一个CharBuffer CharBuffer cbuff = CharBuffer.allocate(8); System.out.println("cbuff的容量:"+cbuff.capacity()); System.out.println("cbuff的界限:"+cbuff.limit()); System.out.println("cbuff的位置:"+cbuff.position()); //向cbuff中插入字符 cbuff.put('a'); cbuff.put('b'); cbuff.put('c'); System.out.println("cbuff的容量:"+cbuff.capacity()); System.out.println("cbuff的界限:"+cbuff.limit()); System.out.println("cbuff的位置:"+cbuff.position()); //调用flip方法:将limit设置为position所在的位置,并将position设置为0,即为输出数据做好准备 cbuff.flip(); System.out.println("cbuff的容量:"+cbuff.capacity()); System.out.println("cbuff的界限:"+cbuff.limit()); System.out.println("cbuff的位置:"+cbuff.position()); //取出cbuff中的元素 System.out.println(cbuff.get()); System.out.println("cbuff的容量:"+cbuff.capacity()); System.out.println("cbuff的界限:"+cbuff.limit()); System.out.println("cbuff的位置:"+cbuff.position()); //调用clear方法:clear不是清空数据,她将position设置为0,将limit设置为capactity,即为存入数据做好准备 cbuff.clear(); System.out.println("cbuff的容量:"+cbuff.capacity()); System.out.println("cbuff的界限:"+cbuff.limit()); System.out.println("cbuff的位置:"+cbuff.position()); //绝对取数 System.out.println(cbuff.get(2)); //绝对取数后相应的参数,绝对取数后相应的参数是不会变化的。 System.out.println("cbuff的容量:"+cbuff.capacity()); System.out.println("cbuff的界限:"+cbuff.limit()); System.out.println("cbuff的位置:"+cbuff.position()); } 结果: cbuff的容量:8 cbuff的界限:8 cbuff的位置:0 cbuff的容量:8 cbuff的界限:8 cbuff的位置:3 cbuff的容量:8 cbuff的界限:3 cbuff的位置:0 a cbuff的容量:8 cbuff的界限:3 cbuff的位置:1 cbuff的容量:8 cbuff的界限:8 cbuff的位置:0 c cbuff的容量:8 cbuff的界限:8 cbuff的位置:0
二、Channel
Channel类似于传统的流对象,但与传统的流对象有两个主要区别:
1)Channel可以直接将指定文件的部分或者全部直接映射成Buffer
2)程序不能直接访问Channel中的数据,包括读取写入都不行,Channel只能与Buffer进行交互
Channel接口的实现类有DatagramChannel、FileChannel、ServerSocketChannel、SocketChannel....等。主要是按功能来分的,DatagramChannel用于支持UDP网络通信的Channel,ServerSocketChannel、SocketChannel是用于支持TCP网络通信的Channel 等依次类推。
/** * Channel测试 * 将FileChannel的全部数据一次映射成ByteBuffer的效果 */ @Test public void fileChannelTest() { File f = new File("G://test//eda_src.sql"); try (// 获取FileChannel FileChannel inChannel = new FileInputStream(f).getChannel(); FileChannel outChannel = new FileOutputStream("G://test//eda_src_bak.sql").getChannel()) { //MappedByteBuffer是ByteBuffer的子类,它表示Channel将磁盘文件的部分内容或全部内容映射到内存后得到的结果 //通常MappedByteBuffer是由Channel的map方法返回。 MappedByteBuffer mbb = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length()); //创建UTF-8创建解码器 Charset charset = Charset.forName("UTF-8"); //将buffer的数据全部输出 outChannel.write(mbb); //将mbb恢复 mbb.clear(); //创建解码器 CharBuffer cbuff = charset.decode(mbb); System.out.println(cbuff); } catch (Exception e) { e.printStackTrace(); } } /** * Channel测试 * 如果Channel对应的文件过大,一次性映射到内存中引起性能下降,可以采用“竹筒多次重复取水”的方式 * 实现文件复制 */ @Test public void fileChannelTest1() { File f = new File("G://test//eda_src.sql"); try (// 获取FileChannel FileChannel inChannel = new FileInputStream(f).getChannel(); FileChannel outChannel = new FileOutputStream("G://test//eda_src_bak1.sql").getChannel()) { //定义一个ByteBuffer对象,用于重复取水 ByteBuffer bbuff = ByteBuffer.allocate(1024); while(inChannel.read(bbuff) != -1) { //准备读取数据 bbuff.flip(); outChannel.write(bbuff); //准备写入数据 bbuff.clear(); } } catch (Exception e) { e.printStackTrace(); } }
三、字符集和Charset
在计算机里所有的文件,数据等在底层都是二进制文件,即全部是字节码。对于文本文件,之所以看到一个个字符,这是因为系统将底层的二进制序列转换成字符的缘故。在这个过程中涉及到两个概念:编码(Encode)和解码(Decode)
通常而言:
Encode:把明文的字符序列转换成计算机所理解的二进制序列称为编码
Decode:把二进制序列转换成普通人能看懂的明文字符串称为解码
/** * 编码,解码 * @throws Exception */ @Test public void CharsetTest() throws Exception { Charset charset = Charset.forName("UTF-8"); CharBuffer cbuff = CharBuffer.allocate(10); cbuff.put('孙'); cbuff.put('悟'); cbuff.put('空'); cbuff.flip(); ByteBuffer bbuff = charset.newEncoder().encode(cbuff); cbuff.clear(); //编码 System.out.println(bbuff.get(5)); bbuff.flip(); //解码 System.out.println(charset.newDecoder().decode(bbuff)); }
四、NIO.2
JAVA7对原有的NIO进行了重大改进,改进主要包括如下两方面的内容(java7把这种改进称为NIO.2)
1)提供了全面的文件IO和文件系统访问支持(java7新增了java.nio.file包及各个子包)
2)基于异步Channel的IO(java7在java.nio.channels新增了多个以Asynchronous开头的Channel接口和类)
JAVA传统File类功能比较有限,NIO.2中引入了Path接口和Files、Paths两个工具类来弥补这种不足。
/** * Paths测试 * @throws Exception */ @Test public void pathTest() throws Exception { //以当前路径来获取Path对象 Path path = Paths.get("."); //path路径 System.out.println(path); //绝对路径 System.out.println(path.toAbsolutePath()); //真实路径 System.out.println(path.toRealPath()); //获取路径名数量 System.out.println(path.getNameCount()); //获取绝对路径名数量 System.out.println(path.toAbsolutePath().getNameCount()); //获取真实路径名数量 System.out.println(path.toRealPath().getNameCount()); } /** * Files测试 * @throws Exception */ @Test public void filesTest() throws Exception { // 复制文件 Files.copy(Paths.get("G://test//eda_src_bak1.sql"), new FileOutputStream("G://test//eda_src_bak2.sql")); //判断文件是否为影藏文件 System.out.println(Files.isHidden(Paths.get("G://test//eda_src_bak2.sql"))); //一次性读取文件所有行 List<String> lines = Files.readAllLines(Paths.get("G://test//eda_src_bak2.sql")); System.out.println(lines); //获取文件大小 System.out.println(Files.size(Paths.get("G://test//eda_src_bak2.sql"))); //直接将多个字符串内容写入到指定文件中 List<String> l = new ArrayList<>(); l.add("飞流直下三千尺"); l.add("佛说因果有缘"); Files.write(Paths.get("G://test//eda_src_bak3.sql"), l, Charset.forName("UTF-8")); //判断C盘总空间,可用空间 FileStore cStore = Files.getFileStore(Paths.get("C:")); System.out.println("C盘总空间"+cStore.getTotalSpace()); System.out.println("C可用空间"+cStore.getUsableSpace()); } /** * 使用FileVistor遍历目录 * FileVisitor接口代表一个文件访问器,定义了四个方法 * FileVisitResult visitFile(T file, BasicFileAttributes attrs) 访问文件时触发该方法 * FileVisitResult visitFileFailed(T file, IOException exc) 访问文件失败时触发该方法 * FileVisitResult postVisitDirectory(T dir, IOException exc) 访问子目录之前触发该方法 * FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) 访问子目录之后触发该方法 * 四个方法会返回FileVisitResult对象,这是一个枚举类 * CONTINUE:继续访问 * SKIP_SIBLINGS:继续访问,但不访问该文件或目录的兄弟文件或目录 * SKIP_SUBTREE:继续访问,但不访问该文件或目录的子目录树 * TERMINATE:终止访问 * * @throws Exception */ @Test public void FileVistorTest() throws Exception { //遍历G: est所有文件及子目录 Files.walkFileTree(Paths.get("G://test"), new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println(file.toRealPath()); return FileVisitResult.CONTINUE; } });