12、NIO
12.1 Java NIO 概述 1课时
12.2 Java NIO.2 之Path、Paths 与 Files 的使用 1课时
12.3 自动资源管理 1课时
12.4 缓冲区(Buffer) 1课时
12.5 通道(Channel)与文件通道(FileChannel) 1课时
12.6管道(Pipe)中的SinkChannel和SourceChannel 1课时
12.7 字符集(Charset) 1课时

##12-1 Java NIO 概述

Java NIO 概述
  • Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
NIO. 2
  • 随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。因为 NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分。
Java NIO 与 IO 的主要区别
  1. <tr>
  2. <td >IO</td>
  3. <td >NIO</td>
  4. </tr>
  5. <tr>
  6. <td >面向流(Stream Oriented):单向的</td>
  7. <td >面向缓冲区(Buffer Oriented):通道是单向的,也可以是双向的</td>
  8. </tr>
  9. <tr>
  10. <td >阻塞IO(Blocking IO)</td>
  11. <td >非阻塞IO(Non Blocking IO)</td>
  12. </tr>
  13. <tr>
  14. <td >(无)</td>
  15. <td >选择器(Selectors)</td>
  16. </tr>

##12-2 NIO.2 之 Path、Paths、Files的使用

Path接口
  • Path 常用方法:

    • String toString() : 返回调用 Path 对象的字符串表示形式
    • boolean startsWith(String path) : 判断是否以 path 路径开始
    • boolean endsWith(String path) : 判断是否以 path 路径结束
    • boolean isAbsolute() : 判断是否是绝对路径
    • Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径
    • Path getRoot() :返回调用 Path 对象的根路径
    • Path getFileName() : 返回与调用 Path 对象关联的文件名
    • Path getName(int idx) : 返回指定索引位置 idx 的路径名称
    • int getNameCount() : 返回Path 根目录后面元素的数量
    • Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
    • Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
    • File toFile(): 将Path转化为File类的对象
  • java.nio.file.Files 用于操作文件或目录的工具类。

  • Files常用方法:

    • Path copy(Path src, Path dest, CopyOption … how) : 文件的复制
    • Path createDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录
    • Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件
    • void delete(Path path) : 删除一个文件,如果不存在,执行报错
    • void deleteIfExists(Path path) : Path对应的文件如果存在,执行删除
    • Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置
    • long size(Path path) : 返回 path 指定文件的大小
  • Files常用方法:用于判断

    • boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
    • boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
    • boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件
    • boolean isHidden(Path path) : 判断是否是隐藏文件
    • boolean isReadable(Path path) : 判断文件是否可读
    • boolean isWritable(Path path) : 判断文件是否可写
    • boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
  • Files常用方法:用于操作内容

    • SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
    • DirectoryStream newDirectoryStream(Path path) : 打开 path 指定的目录
    • InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象
    • OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象

Path案例

  1. /**
  2. * 1. jdk 7.0 时,引入了 Path、Paths、Files三个类。
  3. * 2.此三个类声明在:java.nio.file包下。
  4. * 3.Path可以看做是java.io.File类的升级版本。也可以表示文件或文件目录,与平台无关
  5. *
  6. * 4.如何实例化Path:使用Paths.
  7. * static Path get(String first, String … more) : 用于将多个字符串串连成路径
  8. * static Path get(URI uri): 返回指定uri对应的Path路径
  9. */
  10. public class PathTest {
  11. //Path中的常用方法
  12. @Test
  13. public void test2(){
  14. Path path1 = Paths.get("d:\\", "nio\\nio1\\nio2\\hello.txt");
  15. Path path2 = Paths.get("hello.txt");
  16. // String toString() : 返回调用 Path 对象的字符串表示形式
  17. System.out.println(path1);
  18. // boolean startsWith(String path) : 判断是否以 path 路径开始
  19. System.out.println(path1.startsWith("d:\\nio"));
  20. // boolean endsWith(String path) : 判断是否以 path 路径结束
  21. System.out.println(path1.endsWith("hello.txt"));
  22. // boolean isAbsolute() : 判断是否是绝对路径
  23. System.out.println(path1.isAbsolute() + "~");
  24. System.out.println(path2.isAbsolute() + "~");
  25. // Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径
  26. System.out.println(path1.getParent());
  27. System.out.println(path2.getParent());
  28. // Path getRoot() :返回调用 Path 对象的根路径
  29. System.out.println(path1.getRoot());
  30. System.out.println(path2.getRoot());
  31. // Path getFileName() : 返回与调用 Path 对象关联的文件名
  32. System.out.println(path1.getFileName() + "~");
  33. System.out.println(path2.getFileName() + "~");
  34. // int getNameCount() : 返回Path 根目录后面元素的数量
  35. // Path getName(int idx) : 返回指定索引位置 idx 的路径名称
  36. for(int i = 0;i < path1.getNameCount();i++){
  37. System.out.println(path1.getName(i) + "*****");
  38. }
  39. // Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
  40. System.out.println(path1.toAbsolutePath());
  41. System.out.println(path2.toAbsolutePath());
  42. // Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
  43. Path path3 = Paths.get("d:\\", "nio");
  44. Path path4 = Paths.get("nioo\\hi.txt");
  45. path3 = path3.resolve(path4);
  46. System.out.println(path3);
  47. // File toFile(): 将Path转化为File类的对象
  48. File file = path1.toFile();//Path--->File的转换
  49. Path newPath = file.toPath();//File--->Path的转换
  50. }
  51. //如何使用Paths实例化Path
  52. @Test
  53. public void test1(){
  54. Path path1 = Paths.get("d:\\nio\\hello.txt");//new File(String filepath)
  55. Path path2 = Paths.get("d:\\", "nio\\hello.txt");//new File(String parent,String filename);
  56. System.out.println(path1);
  57. System.out.println(path2);
  58. Path path3 = Paths.get("d:\\", "nio");
  59. System.out.println(path3);
  60. }
  61. }

Files案例

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.OutputStream;
  4. import java.nio.channels.SeekableByteChannel;
  5. import java.nio.file.DirectoryStream;
  6. import java.nio.file.Files;
  7. import java.nio.file.LinkOption;
  8. import java.nio.file.Path;
  9. import java.nio.file.Paths;
  10. import java.nio.file.StandardCopyOption;
  11. import java.nio.file.StandardOpenOption;
  12. import java.util.Iterator;
  13. import org.junit.Test;
  14. /**
  15. * Files工具类的使用:操作文件或目录的工具类
  16. *
  17. */
  18. public class FilesTest {
  19. /**
  20. * StandardOpenOption.READ:表示对应的Channel是可读的。
  21. * StandardOpenOption.WRITE:表示对应的Channel是可写的。
  22. * StandardOpenOption.CREATE:如果要写出的文件不存在,则创建。如果存在,忽略
  23. * StandardOpenOption.CREATE_NEW:如果要写出的文件不存在,则创建。如果存在,抛异常
  24. */
  25. @Test
  26. public void test3() throws IOException{
  27. // SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
  28. Path path1 = Paths.get("d:\\nio", "hello.txt");
  29. SeekableByteChannel channel = Files.newByteChannel(path1, StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
  30. // DirectoryStream<Path> newDirectoryStream(Path path) : 打开 path 指定的目录
  31. Path path2 = Paths.get("e:\\teach");
  32. DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path2);
  33. Iterator<Path> iterator = directoryStream.iterator();
  34. while(iterator.hasNext()){
  35. System.out.println(iterator.next());
  36. }
  37. // InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象
  38. InputStream inputStream = Files.newInputStream(path1, StandardOpenOption.READ);
  39. // OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象
  40. OutputStream outputStream = Files.newOutputStream(path1, StandardOpenOption.WRITE,StandardOpenOption.CREATE);
  41. }
  42. @Test
  43. public void test2() throws IOException{
  44. Path path1 = Paths.get("d:\\nio", "hello.txt");
  45. Path path2 = Paths.get("xxx.txt");
  46. // boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
  47. System.out.println(Files.exists(path2, LinkOption.NOFOLLOW_LINKS));
  48. // boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
  49. //不要求此path对应的物理文件存在。
  50. System.out.println(Files.isDirectory(path1, LinkOption.NOFOLLOW_LINKS));
  51. // boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件
  52. // boolean isHidden(Path path) : 判断是否是隐藏文件
  53. //要求此path对应的物理上的文件需要存在。才可判断是否隐藏。否则,抛异常。
  54. // System.out.println(Files.isHidden(path1));
  55. // boolean isReadable(Path path) : 判断文件是否可读
  56. System.out.println(Files.isReadable(path1));
  57. // boolean isWritable(Path path) : 判断文件是否可写
  58. System.out.println(Files.isWritable(path1));
  59. // boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
  60. System.out.println(Files.notExists(path1, LinkOption.NOFOLLOW_LINKS));
  61. }
  62. @Test
  63. public void test1() throws IOException{
  64. Path path1 = Paths.get("d:\\nio", "hello.txt");
  65. Path path2 = Paths.get("xxx.txt");
  66. // Path copy(Path src, Path dest, CopyOption … how) : 文件的复制
  67. //要想复制成功,要求path1对应的物理上的文件存在。path1对应的文件没有要求。
  68. // Files.copy(path1, path2, StandardCopyOption.REPLACE_EXISTING);
  69. // Path createDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录
  70. //要想执行成功,要求path对应的物理上的文件目录不存在。一旦存在,抛出异常。
  71. Path path3 = Paths.get("d:\\nio\\nio1");
  72. // Files.createDirectory(path3);
  73. // Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件
  74. //要想执行成功,要求path对应的物理上的文件不存在。一旦存在,抛出异常。
  75. Path path4 = Paths.get("d:\\nio\\hi.txt");
  76. // Files.createFile(path4);
  77. // void delete(Path path) : 删除一个文件/目录,如果不存在,执行报错
  78. // Files.delete(path4);
  79. // void deleteIfExists(Path path) : Path对应的文件/目录如果存在,执行删除.如果不存在,正常执行结束
  80. Files.deleteIfExists(path3);
  81. // Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置
  82. //要想执行成功,src对应的物理上的文件需要存在,dest对应的文件没有要求。
  83. // Files.move(path1, path2, StandardCopyOption.ATOMIC_MOVE);
  84. // long size(Path path) : 返回 path 指定文件的大小
  85. long size = Files.size(path2);
  86. System.out.println(size);
  87. }
  88. }
Path、Paths和Files核心API

早期的java只提供了一个File类来访问文件系统,但File类的功能比较有限,所提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异常信息。

NIO. 2为了弥补这种不足,引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看成是File类的升级版本,实际引用的资源也可以不存在。

在以前IO操作都是这样写的:

  1. import java.io.File;
  2. File file = new File("index.html");

但在Java7 中,我们可以这样写:

  1. import java.nio.file.Path;
  2. import java.nio.file.Paths;
  3. Path path = Paths.get("index.html");

同时,NIO.2还提供了Files、Paths工具类,Files包含了大量静态的工具方法来操作文件;Paths则包含了两个返回Path的静态工厂方法。

  • Paths 类提供的静态 get() 方法用来获取 Path 对象:

    1. static Path get(String first, String more) : 用于将多个字符串串连成路径
    2. static Path get(URI uri): 返回指定uri对应的Path路径
URI、URL和URN的区别

URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。而URL是uniform resource locator,统一资源定位符,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。而URN,uniform resource name,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL和URN都是一种URI。

在Java的URI中,一个URI实例可以代表绝对的,也可以是相对的,只要它符合URI的语法规则。而URL类则

不仅符合语义,还包含了定位该资源的信息,

因此它不能是相对的。

12-3 自动资源管理

自动资源管理案例

  1. import java.io.FileInputStream;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.nio.channels.SeekableByteChannel;
  5. import java.nio.file.Files;
  6. import java.nio.file.Paths;
  7. import java.nio.file.StandardOpenOption;
  8. import org.junit.Test;
  9. /**
  10. *
  11. * jdk 7 提供基于try-catch的自动资源管理
  12. */
  13. public class ARMTest {
  14. /**
  15. * 能够实现资源的自动关闭,需要满足:
  16. * 1.可以在一条 try 语句中管理多个资源,每个资源以“;” 隔开即可。
  17. * 2.需要关闭的资源,必须实现了 AutoCloseable 接口或其子接口 Closeable
  18. *
  19. * 目的:不需要再使用finally,显式的关闭资源了。
  20. *
  21. */
  22. @Test
  23. public void test2(){
  24. try(
  25. FileInputStream fis = new FileInputStream("xxx.txt");
  26. FileOutputStream fos = new FileOutputStream("abc.txt");
  27. SeekableByteChannel channel = Files.newByteChannel(Paths.get("d:\\nio", "hello.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE)
  28. )
  29. {
  30. //可能出现异常的代码
  31. int data;
  32. while((data = fis.read()) != -1){
  33. System.out.println((char)data);
  34. }
  35. }catch(Exception e){
  36. e.printStackTrace();
  37. }
  38. }
  39. @Test
  40. public void test1() {
  41. FileInputStream fis = null;
  42. try {
  43. fis = new FileInputStream("xxx.txt");
  44. int data;
  45. while((data = fis.read()) != -1){
  46. System.out.println((char)data);
  47. }
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. }finally{
  51. if(fis != null){
  52. try {
  53. fis.close();
  54. } catch (IOException e) {
  55. e.printStackTrace();
  56. }
  57. }
  58. }
  59. }
  60. }
  • Java 7 增加了一个新特性,该特性提供了另外一种管理资源的方式,这种方式能自动关闭文件。这个特性有时被称为自动资源管理(Automatic Resource Management, ARM), 该特性以 try 语句的扩展版为基础。自动资源管理主要用于当不再需要文件(或其他资源)时,可以防止无意中忘记释放它们。

  • 自动资源管理基于 try 语句的扩展形式:

    1. try(需要关闭的资源声明){
    2. //可能发生异常的语句
    3. }catch(异常类型 变量名){
    4. //异常的处理语句
    5. }
    6. ……
    7. finally{
    8. //一定执行的语句
    9. }

当 try 代码块结束时,自动释放资源。因此不需要显示的调用 close() 方法。该形式也称为“带资源的 try 语句”。

注意:

① try 语句中声明的资源被隐式声明为 final ,资源的作用局限于带资源的 try 语句

② 可以在一条 try 语句中管理多个资源,每个资源以“;” 隔开即可。

③ 需要关闭的资源,必须实现了 AutoCloseable 接口或其子接口 Closeable

FileUtils案例

导入import org.apache.commons.io.FileUtils;

  1. import java.io.File;
  2. import java.io.IOException;
  3. import org.apache.commons.io.FileUtils;
  4. import org.junit.Test;
  5. public class FileUtilsTest {
  6. @Test
  7. public void test1() throws IOException{
  8. File srcFile = new File("Dilraba.jpg");
  9. File destFile = new File("Dilraba1.jpg");
  10. FileUtils.copyFile(srcFile, destFile);
  11. }
  12. }

12-4 缓冲区(Buffer)

缓冲区案例

  1. import java.nio.ByteBuffer;
  2. import java.nio.CharBuffer;
  3. import org.junit.Test;
  4. /**
  5. * 一、NIO的使用中两个重要的要素:缓冲区(Buffer)、通道(Channel)
  6. * 缓冲区(Buffer):存储数据的。 ---->byte[] buffer = new byte[1024]
  7. * 通道(Channel):代表着数据源与目标节点之间的连接,负责缓冲区的传输。 --->IO流
  8. *
  9. * 二者的交互:Java NIO 中的 Buffer 主要用于与 NIO 通道(Channel)进行交互,
  10. * 数据是从通道读入缓冲区,从缓冲区写入通道中的。
  11. *
  12. * 二、缓冲区(Buffer)的结构 (除boolean之外)
  13. * java.nio.Buffer抽象类
  14. * |----ByteBuffer
  15. * |----CharBuffer
  16. *
  17. * |----ShortBuffer
  18. * |----IntBuffer
  19. * |----LongBuffer
  20. * |----FloatBuffer
  21. * |----DoubleBuffer
  22. * XxxBuffer底层使用xxx[]进行存储。
  23. *
  24. * 三、如何实例化缓冲区?调用缓冲区类XxxBuffer的静态方法:allocate(int capacity)
  25. * 举例:ByteBuffer byteBuffer = ByteBuffer.allocate(10);byte[] hb = new byte[10];
  26. * 类似:ArrayList list = new ArrayList(10);//Object[] eleData = new Object[10];
  27. * 说明:方法的形参,决定了底层创建的数组的长度
  28. *
  29. * 四、Buffer中的常用属性:
  30. * capacity:容量,决定了底层数组的长度,表明了最大存储数据的容量
  31. * limit:限制,默认情况下,limit等于capacity.在读数据模式下,limit<=capacity.表明最大可以读取数据的量
  32. * position:位置,表明了当前读取或写入数据的位置
  33. * mark:标记。默认值为-1.
  34. *
  35. * 关系式:mark <= position <= limit <= capacity
  36. *
  37. * 类比:项目三中TeamService类中的属性:
  38. * private final int MAX_MEMBER = 5;//相当于capacity
  39. private Programmer[] team = new Programmer[MAX_MEMBER];//Buffer底层封装的数组
  40. private int total;//相当于limit
  41. * index:读取、写入数组指定为的索引:position
  42. *
  43. * 五、Buffer中的常用方法:
  44. * 1)最基本的两个方法:put(Xxx xxx) / get()
  45. * 2)其他方法:见ppt中的表格即可。
  46. *
  47. * 六、针对于ByteBuffer来讲,可以创建非直接缓冲区:allocate(int capacity)
  48. * 直接缓冲区:allocateDirect(int capacity) / FileChannel 的 map()
  49. *
  50. * 了解非直接缓冲区 与 直接缓冲区的区别
  51. */
  52. public class BufferTest {
  53. @Test
  54. public void test3(){
  55. ByteBuffer byteBuffer = ByteBuffer.allocate(10);
  56. byteBuffer.put("hello".getBytes());
  57. //array():返回缓冲区底层的数组
  58. byte[] array = byteBuffer.array();
  59. System.out.println(array.length);
  60. for(int i = 0;i < array.length;i++){
  61. System.out.println(array[i]);
  62. }
  63. //isDirect():判断此时的字节缓冲区是否是直接缓冲区
  64. System.out.println(byteBuffer.isDirect());
  65. }
  66. @Test
  67. public void test2(){
  68. ByteBuffer byteBuffer = ByteBuffer.allocate(10);
  69. byteBuffer.put("hello".getBytes());
  70. System.out.println(byteBuffer.capacity());
  71. System.out.println(byteBuffer.limit());
  72. System.out.println(byteBuffer.position());
  73. byteBuffer.flip();
  74. byte[] dst = new byte[5];
  75. byteBuffer.get(dst,0,2);//从数组角标0开始,写入两个字节的数组
  76. System.out.println(byteBuffer.capacity());
  77. System.out.println(byteBuffer.limit());
  78. System.out.println(byteBuffer.position());
  79. System.out.println("***********mark()***************");
  80. byteBuffer.mark();//标记当前position的位置
  81. byteBuffer.get(dst,2,2);//从数组角标2开始,写入两个字节的数组
  82. System.out.println(byteBuffer.capacity());
  83. System.out.println(byteBuffer.limit());
  84. System.out.println(byteBuffer.position());
  85. // byteBuffer.rewind();//position = 0;
  86. //reset():此方法必须要配合着mark()使用。调用此方法其调用mark(),否则抛异常。
  87. byteBuffer.reset();//position = mark;即为将position置为标记的位置
  88. if(byteBuffer.hasRemaining()){//判断是否还有元素没有读取到。
  89. System.out.println(byteBuffer.remaining());//还有几个没有读取到。
  90. }
  91. System.out.println();
  92. while(byteBuffer.hasRemaining()){
  93. System.out.println((char)byteBuffer.get());
  94. }
  95. }
  96. @Test
  97. public void test1(){
  98. System.out.println("*********allocate(10)************");
  99. ByteBuffer byteBuffer = ByteBuffer.allocate(10);
  100. // CharBuffer charBuffer = CharBuffer.allocate(10);
  101. System.out.println(byteBuffer.capacity());
  102. System.out.println(byteBuffer.limit());
  103. System.out.println(byteBuffer.position());
  104. System.out.println("*********put()************");
  105. byteBuffer.put("hello".getBytes());//写入长度为5的字节数.每put一个字节,position就+1
  106. System.out.println(byteBuffer.capacity());
  107. System.out.println(byteBuffer.limit());
  108. System.out.println(byteBuffer.position());
  109. System.out.println("*********flip()************");
  110. byteBuffer.flip();//切换为读数据模式。将limit设置为position,position归零
  111. System.out.println(byteBuffer.capacity());
  112. System.out.println(byteBuffer.limit());
  113. System.out.println(byteBuffer.position());
  114. System.out.println("*********get()************");
  115. //方式一:
  116. // System.out.println((char)byteBuffer.get());//每get一个字节,position也自动+1
  117. // System.out.println((char)byteBuffer.get());
  118. //方式二:
  119. byte[] dst = new byte[3];
  120. byteBuffer.get(dst);
  121. System.out.println(byteBuffer.capacity());
  122. System.out.println(byteBuffer.limit());
  123. System.out.println(byteBuffer.position());
  124. // System.out.println("*********rewind()************");
  125. // byteBuffer.rewind();//重置position
  126. // System.out.println(byteBuffer.capacity());
  127. // System.out.println(byteBuffer.limit());
  128. // System.out.println(byteBuffer.position());
  129. System.out.println("*********clear()************");
  130. byteBuffer.clear();//清空.将position归零,limit设置为capacity.数据并未删除。
  131. System.out.println(byteBuffer.capacity());
  132. System.out.println(byteBuffer.limit());
  133. System.out.println(byteBuffer.position());
  134. System.out.println((char)byteBuffer.get());
  135. }
  136. }
缓冲区和通道
  • Java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示IO源到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。

简而言之,Channel 负责传输, Buffer 负责存储数据

缓冲区 (Buffer)
  • 缓冲区(Buffer):一个用于特定基本数据类型(除boolean型外)的容器,底层使用数组存储。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
  • Java NIO 中的 Buffer 主要用于与 NIO 通道(Channel)进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。

  • Buffer 就像一个数组,可以保存多个相同类型的数据。根据数据类型不同(boolean 除外) ,有以下 Buffer 常用子类:



    上述 Buffer 类 他们都采用相似的方法进行管理数据,只是各自管理的数据类型不同而已。都是通过各自类的如下方法获取一个 Buffer 对象:

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

缓冲区的基本属性
  • 容量 (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
缓冲区的数据基本操作
  • Buffer 所有子类提供了两个用于数据操作的方法:

    put(Xxx xxx) 与 get() 方法

以ByteBuffer类为例:

  • 放入数据到 Buffer 中

    put(byte b):将给定单个字节写入缓冲区的当前位置

    put(byte[] src):将 src 中的字节写入缓冲区的当前位置

    put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动 position)

  • 获取 Buffer 中的数据

    get() :读取单个字节

    get(byte[] dst):批量读取多个字节到 dst 中

    get(int index):读取指定索引位置的字节(不会移动 position)

12-5 通道(Channel)与文件通道(FileChannel)

通道案例

  1. import java.io.FileInputStream;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.RandomAccessFile;
  6. import java.nio.ByteBuffer;
  7. import java.nio.MappedByteBuffer;
  8. import java.nio.channels.FileChannel;
  9. import java.nio.channels.FileChannel.MapMode;
  10. import java.nio.file.Paths;
  11. import java.nio.file.StandardOpenOption;
  12. import org.junit.Test;
  13. /**
  14. * 一、java.nio.channels.Channel接口
  15. * Channel:不负责存储数据,数据存储在Buffer中,Channel负责Buffer的传输
  16. *
  17. *
  18. * 二、
  19. * java.nio.channels.Channel
  20. * |-----FileChannel:处理本地文件
  21. *
  22. * |-----SocketChannel:TCP网络编程的客户端的Channel
  23. * |-----ServerSocketChannel:TCP网络编程的服务器端的Channel
  24. * |-----DatagramChannel:UDP网络编程中发送端和接收端的Channel
  25. *
  26. * |-----Pipe.SinkChannel
  27. * |-----Pipe.SourceChannel
  28. *
  29. * 三、如何实例化Channel
  30. * 方式一:调用相关结构的getChannel()
  31. * FileInputStream / FileOutputStream / RandomAccessFile ----> FileChannel
  32. *
  33. * Socket --->SocketChannel
  34. * ServerSocket ---->ServerSocketChannel
  35. * DatagramSocket --->DatagramChannel
  36. *
  37. * 说明:Channel可以是单向的,也可以是双向的。
  38. *
  39. * 方式二:XxxChannel的静态方法:open().
  40. * 方式三:Files工具类的静态方法:newByteChannel() 得到的是字节通道。
  41. * 说明:方式二和方式三在jdk7.0新增的。
  42. *
  43. * 四、Channel的常用方法:读:read(ByteBuffer buffer) / write(ByteBuffer buffer)
  44. */
  45. public class ChannelTest {
  46. @Test
  47. public void test4() throws Exception{
  48. RandomAccessFile readRaf = new RandomAccessFile("IDEA.java", "r");
  49. RandomAccessFile writeRaf = new RandomAccessFile("NEWIDEA.java", "rw");
  50. //实例化Channel
  51. FileChannel inChannel = readRaf.getChannel();
  52. FileChannel outChannel = writeRaf.getChannel();
  53. ByteBuffer buffer1 = ByteBuffer.allocate(1024);
  54. ByteBuffer buffer2 = ByteBuffer.allocate(2048);
  55. ByteBuffer[] dsts = {buffer1,buffer2};
  56. inChannel.read(dsts);//分散读取
  57. //改为可读模式
  58. buffer1.flip();
  59. buffer2.flip();
  60. System.out.println(new String(buffer1.array(),0,buffer1.limit()));
  61. System.out.println();
  62. System.out.println(new String(buffer2.array(),0,buffer2.limit()));
  63. //测试聚集写入
  64. outChannel.write(dsts);
  65. outChannel.close();
  66. inChannel.close();
  67. }
  68. @Test
  69. public void test3() throws IOException{
  70. FileChannel inChannel = FileChannel.open(Paths.get("Dilraba.jpg"), StandardOpenOption.READ);
  71. FileChannel outChannel = FileChannel.open(Paths.get("mm1.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);
  72. //transferTo():将数据从可读的Channel中转换到可写的Channel中
  73. // inChannel.transferTo(0, inChannel.size(), outChannel);
  74. //transferFrom():将数据从可读的Channel中转换到可写的Channel中
  75. outChannel.transferFrom(inChannel, 0, inChannel.size());
  76. inChannel.close();
  77. outChannel.close();
  78. }
  79. //使用Channel实例化的方式二,使用直接缓冲区,实现文件的复制
  80. @Test
  81. public void test2(){
  82. FileChannel inChannel = null;
  83. FileChannel outChannel = null;
  84. try {
  85. long start = System.currentTimeMillis();
  86. // String srcPath = "Dilraba.jpg";
  87. // String destPath = "Dilraba3.jpg";
  88. String srcPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼.mp4";
  89. String destPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼1.mp4";
  90. //实例化Channel
  91. inChannel = FileChannel.open(Paths.get(srcPath), StandardOpenOption.READ);
  92. outChannel = FileChannel.open(Paths.get(destPath), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
  93. //得到直接缓冲区
  94. MappedByteBuffer inMappedBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());//size():返回操作的文件的大小
  95. MappedByteBuffer outMappedBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
  96. //数据的读写操作
  97. byte[] buffer = new byte[inMappedBuffer.limit()];
  98. inMappedBuffer.get(buffer);
  99. outMappedBuffer.put(buffer);
  100. long end = System.currentTimeMillis();
  101. System.out.println("直接缓冲区花费的时间:" + (end - start));//1929-1894
  102. } catch (IOException e) {
  103. e.printStackTrace();
  104. }finally{
  105. if(inChannel != null){
  106. try {
  107. inChannel.close();
  108. } catch (IOException e) {
  109. e.printStackTrace();
  110. }
  111. }
  112. if(outChannel != null){
  113. try {
  114. outChannel.close();
  115. } catch (IOException e) {
  116. e.printStackTrace();
  117. }
  118. }
  119. }
  120. }
  121. //使用Channel实例化的方式一,使用非直接缓冲区,实现文件的复制操作
  122. @Test
  123. public void test1(){
  124. FileChannel inChannel = null;
  125. FileChannel outChannel = null;
  126. FileInputStream fis = null;
  127. FileOutputStream fos = null;
  128. try {
  129. long start = System.currentTimeMillis();
  130. // String srcPath = "Dilraba.jpg";
  131. // String destPath = "Dilraba2.jpg";
  132. String srcPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼.mp4";
  133. String destPath = "C:\\Users\\Administrator\\Desktop\\score\\战狼2.mp4";
  134. fis = new FileInputStream(srcPath);
  135. fos = new FileOutputStream(destPath);
  136. //实例化Channel
  137. inChannel = fis.getChannel();
  138. outChannel = fos.getChannel();
  139. //提供ByteBuffer
  140. ByteBuffer buffer = ByteBuffer.allocate(1024);
  141. while(inChannel.read(buffer) != -1){
  142. buffer.flip();//修改为读数据模式
  143. outChannel.write(buffer);
  144. buffer.clear();//清空
  145. }
  146. long end = System.currentTimeMillis();
  147. System.out.println("非直接缓冲区花费的时间:" + (end - start));//20795-13768
  148. } catch (Exception e) {
  149. e.printStackTrace();
  150. }finally{
  151. if(outChannel != null){
  152. //关闭资源
  153. try {
  154. outChannel.close();
  155. } catch (IOException e) {
  156. e.printStackTrace();
  157. }
  158. }
  159. if(inChannel != null){
  160. try {
  161. inChannel.close();
  162. } catch (IOException e) {
  163. e.printStackTrace();
  164. }
  165. }
  166. if(fos != null){
  167. try {
  168. fos.close();
  169. } catch (IOException e) {
  170. e.printStackTrace();
  171. }
  172. }
  173. if(fis != null){
  174. try {
  175. fis.close();
  176. } catch (IOException e) {
  177. e.printStackTrace();
  178. }
  179. }
  180. }
  181. }
  182. }
通道 (Channel)
  • 通道 (Channel):由 java.nio.channels 包定义的。

    • Channel 表示 IO 源与目标节点打开的连接。
    • Channel 类似于传统的“流”。只不过 Channel 本身不能直接存储数据,Channel 只能与 Buffer 进行交互。
传统的IO和NIO理解



PIO(parts in one)模式下硬盘和内存之间的数据传输是由CPU来控制的



DMA模式下,CPU必须向DMA(理解为秘书)控制器下达命令,让DMA控制器来处理数据的传送,数据传输玩波比再把信息反馈给CPU,这样就很大程度上减轻了CPU资源占有率,可以大大节省系统资源。

DMA模式又可以分为Single-Word(单字节DMA)和Multi-Word(多字节DMA)两种,其中所能达到的最大传输速率也只能有16.6MB/s.

12-6 管道(Pipe)中的SinkChannel和SourceChannel

管道(Pipe)案例

  1. import java.io.IOException;
  2. import java.nio.ByteBuffer;
  3. import java.nio.channels.Pipe;
  4. import org.junit.Test;
  5. public class PipeTest {
  6. /**
  7. * 管道的中SinkChannel 和 SourceChannel使用举例
  8. * @throws IOException
  9. */
  10. @Test
  11. public void test1() throws IOException{
  12. //获取管道
  13. Pipe pipe = Pipe.open();
  14. //将缓冲区中的数据写入管道
  15. ByteBuffer buf = ByteBuffer.allocate(1024);
  16. //线程1
  17. Pipe.SinkChannel sinkChannel = pipe.sink();
  18. buf.put("通过单向管道发送数据".getBytes());
  19. buf.flip();
  20. sinkChannel.write(buf);
  21. //线程2
  22. //读取缓冲区中的数据
  23. Pipe.SourceChannel sourceChannel = pipe.source();
  24. buf.flip();
  25. int len = sourceChannel.read(buf);
  26. System.out.println(new String(buf.array(), 0, len));
  27. sourceChannel.close();
  28. sinkChannel.close();
  29. }
  30. }

12-7 字符集(Charset)

字符集案例

  1. import java.nio.ByteBuffer;
  2. import java.nio.CharBuffer;
  3. import java.nio.charset.CharacterCodingException;
  4. import java.nio.charset.Charset;
  5. import java.nio.charset.CharsetDecoder;
  6. import java.nio.charset.CharsetEncoder;
  7. import java.util.Iterator;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import java.util.SortedMap;
  11. import org.junit.Test;
  12. /**
  13. * Charset:字符集
  14. *
  15. * 1.如何实例化:Charset的静态方法:forName(String charset)
  16. *
  17. * 2.如何得到编码器和解码器:newEncoder()/newDecoder();
  18. */
  19. public class CharsetTest {
  20. @Test
  21. public void test2() throws Exception{
  22. //1.如何实例化
  23. // Charset charset = Charset.forName("gbk");
  24. Charset charset = Charset.forName("utf-8");
  25. //2.得到编码器 和 解码器
  26. CharsetEncoder encoder = charset.newEncoder();//编码器
  27. CharsetDecoder decoder = charset.newDecoder();//解码器
  28. CharBuffer charBuffer = CharBuffer.allocate(1024);
  29. charBuffer.put("保持微笑");
  30. charBuffer.flip();
  31. ByteBuffer byteBuffer = encoder.encode(charBuffer);//编码
  32. //将编码后的数据输出到控制台
  33. // byte[] array = byteBuffer.array();
  34. for(int i = 0;i < byteBuffer.limit();i++){
  35. // System.out.println(array[i]);
  36. System.out.println(byteBuffer.get());
  37. }
  38. byteBuffer.flip();
  39. CharBuffer charBuffer2 = decoder.decode(byteBuffer);//解码
  40. System.out.println(new String(charBuffer2.array(),0,charBuffer2.limit()));
  41. System.out.println("******************");
  42. Charset charset1 = Charset.forName("gbk");
  43. CharsetDecoder decoder2 = charset1.newDecoder();
  44. byteBuffer.flip();
  45. CharBuffer charBuffer3 = decoder2.decode(byteBuffer);//解码
  46. System.out.println(new String(charBuffer3.array(),0,charBuffer3.limit()));
  47. }
  48. @Test
  49. public void test1(){
  50. SortedMap<String,Charset> charsets = Charset.availableCharsets();
  51. Set<Entry<String,Charset>> entrySet = charsets.entrySet();
  52. Iterator<Entry<String, Charset>> iterator = entrySet.iterator();
  53. while(iterator.hasNext()){
  54. Entry<String, Charset> entry = iterator.next();
  55. System.out.println(entry.getKey() + "---->" + entry.getValue());
  56. }
  57. }
  58. }

第十二章 NIO的更多相关文章

  1. PRML读书会第十二章 Continuous Latent Variables(PCA,Principal Component Analysis,PPCA,核PCA,Autoencoder,非线性流形)

    主讲人 戴玮 (新浪微博: @戴玮_CASIA) Wilbur_中博(1954123) 20:00:49 我今天讲PRML的第十二章,连续隐变量.既然有连续隐变量,一定也有离散隐变量,那么离散隐变量是 ...

  2. <构建之法>第十一章、十二章有感

    十一章:软件设计与实现 工作时要懂得平衡进度和质量.我一直有一个困扰:像我们团队这次做 男神女神配 社区交友网,我负责主页的设计及内容模块,有个队友负责网站的注册和登录模块,有个队友负责搜索模块,有个 ...

  3. sql 入门经典(第五版) Ryan Stephens 学习笔记 (第六,七,八,九,十章,十一章,十二章)

    第六章: 管理数据库事务 事务 是 由第五章 数据操作语言完成的  DML ,是对数据库锁做的一个操作或者修改. 所有事务都有开始和结束 事务可以被保存和撤销 如果事务在中途失败,事务中的任何部分都不 ...

  4. 《Linux命令行与shell脚本编程大全》 第二十二章 学习笔记

    第二十二章:使用其他shell 什么是dash shell Debian的dash shell是ash shell的直系后代,ash shell是Unix系统上原来地Bourne shell的简化版本 ...

  5. 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高

    第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...

  6. [CSAPP笔记][第十二章并发编程]

    第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟 ...

  7. perl5 第十二章 Perl5中的引用/指针

    第十二章 Perl5中的引用/指针 by flamephoenix 一.引用简介二.使用引用三.使用反斜线(\)操作符四.引用和数组五.多维数组六.子程序的引用  子程序模板七.数组与子程序八.文件句 ...

  8. 第十二章——SQLServer统计信息(4)——在过滤索引上的统计信息

    原文:第十二章--SQLServer统计信息(4)--在过滤索引上的统计信息 前言: 从2008开始,引入了一个增强非聚集索引的新功能--过滤索引(filter index),可以使用带有where条 ...

  9. 第十二章——SQLServer统计信息(3)——发现过期统计信息并处理

    原文:第十二章--SQLServer统计信息(3)--发现过期统计信息并处理 前言: 统计信息是关于谓词中的数据分布的主要信息源,如果不知道具体的数据分布,优化器不能获得预估的数据集,从而不能统计需要 ...

随机推荐

  1. please select android sdk(出现小红叉)

    问题原因: 在项目中通过 git 协同开发,项目是 kotlin 与 Java 混合开发.在 build.gradle 中添加依赖之后就出现这个问题了,点击运行无法编译. 在网上找了各种解决办法都没能 ...

  2. ubuntu解压命令(转)

    -c: 建立压缩档案 -x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的参数是 ...

  3. 2016 Russian Code Cup (RCC 16), Final Round B - Cactusophobia

    B - Cactusophobia 思路: 点双联通分量+最大流 用tarjan求出每个点双联通分量 对于大小大于1的点双联通分量,它就是个环,那么源点和这个环相连, 容量为环的大小减一,这个环和环上 ...

  4. 基于Bootsrap的BeyondAdmin前端模板 --分享

    1.PC端 2.移动端 3.下载 最新:http://www.yidt.cn/ 链接:https://pan.baidu.com/s/1Tx6EVmGFnVV7H7h3SFwldA 提取码:0btw

  5. ionic3开发ios端

    ionic框架是一端开发,三端适用(android端,ios端,web端),意思是在其中一端开发的代码,拿到另外两端,代码同样生效 那现在就说一下在web端开发拿到ios端开发前需要做的事情 开发io ...

  6. JavaScript基础二

    1.7 常用内置对象 所谓内置对象就是ECMAScript提供出来的一些对象,我们知道对象都是有相应的属性和方法 1.7.1 数组Array 1.数组的创建方式 字面量方式创建(推荐大家使用这种方式, ...

  7. JS碰撞检测

    视图理解://div1的上边大于div2的下边,,div1的右边小于div2的左边,,div1的上边大于div2的下边,,div1的左边大于div2的右边,这四种情况,问题是没有碰撞/重叠,如下: & ...

  8. java使用valueOf的方法反转字符串输出

    public class FanZhuan { public static void main(String[] args) { String s = "987654321088123abo ...

  9. Python-接口自动化(二)

    python基础知识(二) (二)常用控制流 1.控制语句 分支语句:起到一个分支分流的作用,类似马路上的红绿灯 循环语句:for while 可以使代码不断重复的执行 2.判断语句:关键字是if.. ...

  10. css图形——三角形

    1.css图形简介 在浏览网页的时候,我们经常看见各种图形的效果,而但凡涉及到图形效果,我们第一个想到的就是用图片来实现.但是在前端开发中,为了网站的性能速度,我们都是秉承着少用图片的原生质. 因为图 ...