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

Java中与NIO相关的包如下:

java.nio包:主要包含各种与Buffer相关的类.

java.nio.channels包:与Channle和Selector相关的类.

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

java.nio.channels.spi包:与Channel相关的服务提供编程接口

java.nio.charset.spi包:包含与字符集想的服务提供者编程接口

NIO中的新特点:Channel(通道)和Buffer(缓冲),Channle与传统的InputStream,OutputStream最大的区别在于它提供了一个map()方法,通过该方法可以海子街将"一块数据"映射到内存中.如果说传统的输入/输出系统是面向流的处理,则NIO则是面向块的处理.

Buffer可以被理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中,而从Channel中读取的数据也必须先放到Buffer中.

除了ByteBuffer之外,它们都采用相同或相似的方法来管理数据,只是各自管理的数据类型不同而已.

NIO还提供了加工Unicode字符串映射成字节序列以及逆映射操作的Charset类, 也提供了用于支持费阻塞式输入输出的Selector类.

15.9.2使用Buffer

Buffer就像是一个数组,可以保存多个类型相同的数据.Buffer是一个抽象类,最常用的子类是ByteBuffer,它可以在底层字节数组上进行get/set操作. 也有CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer.

这些实现类都没提供构造器,使用如下方式是构造:

ByteBuffer byteBuffer = ByteBuffer.allocate(160);

或者

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(160);//ByteBuffer 独有的创建方法,称为直接BUffer

直接Buffer的创建成本比普通的Buffer创建成本高,但效率也高,所以只适用于长生存期的Buffer,不适用于短生存期,一次用完就丢弃的Buffer.

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

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

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

当Buffer装入数据结束后,调用Buffer的flip()方法,该方法将limit设置为position所在位置,并将position设为0,这就使得Buffer的读写指针又移动到了开始的位置.也就是说,Buffer调用filp()方法之后,Buffer为输出数据做好准备;当Buffer输出数据结束后,Buffer调用clear()方法,clear方法不是清空Buffer的数据,它仅仅将position指为0,将limit置为capacity,这样为再次向Buffe热衷装入数据做好准备.

Buffer包含的一些常用方法:

这些方法大概可以分类

1.标记功能: mark()和reset()

2.position操作: position() position(int newPs) rewind() reset()

3.limit操作: limit() limit(int newLt)

4.剩余元素操作: hasRemaining() remaining()

5.容器大小: capacity()

重点:

put()方法:用于向Buffer中放入数据

get()方法:用于从Buffer中取出数据

这两个方法,既支持对单个数据的访问,也支持对批量数据的访问.

而且,分为相对和绝对两种.

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

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

    public static void bufferTest(){
CharBuffer buff = CharBuffer.allocate(); // ①
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()); // ⑥
System.out.println("执行绝对读取后,position = "
+ buff.position());
}

15.9.3 使用Channel

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

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

2. 程序不能直接访问Channel中的数据,包括读取, 写入,都不行,Channel中取出一些数据,然后让程序从Buffer中取出这些数据:如果要将程序中的数据写入Channel, 一样先让程序将数据放入Buffer中,程序再将Buffer里的数据写入Channel中.

Channle接口的实现类很多,这里只介绍FileChannel的用法.

所有的Channle都不应该通过构造器来直接创建,而是通过传统的节点InputStream,OutputStream的getChannle()方法来返回对应的Channle.

Channle中最常用的三类方法是map(),read(),write(),其中map()方法用于将Channle对应的部分或全部数据映射成ByteBuffer,而read()和write()方法都有一系列重载形式,这些方法用于从Buffer中读取数据或者向Buffer中写入数据.

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

下面给一个出问题的例子,这个是

RandomAccessFile生成的FileChannle,可是与疯狂讲义里说的有偏差,主要问题是当插入的数据超出FileChannle的范围时,FileChannle不会自动扩大,反而变成等待状态,
所访问的文件直接挂掉,一点那个文件,eclipse也挂掉,最后Mac都无法关机,只能强制关机。也不知道哪里没用对。
public static void randomFileChannelTest() throws FileNotFoundException, IOException{
File f = new File("write.txt");
try(
// 创建一个RandomAccessFile对象 FileChannel randomChannel = new RandomAccessFile(f, "rw").getChannel();
)
{
// 将Channel中所有数据映射成ByteBuffer
randomChannel.position();
System.out.println(f.length());
System.out.println(randomChannel.size());
ByteBuffer buffer = randomChannel.map(FileChannel.MapMode.READ_ONLY, ,);
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
// 使用GBK的字符集来创建解码器
Charset charset = Charset.forName("UTF-8");
// 创建解码器(CharsetDecoder)对象
CharsetDecoder decoder = charset.newDecoder();
// 使用解码器将ByteBuffer转换成CharBuffer
CharBuffer charBuffer = decoder.decode(buffer);
// CharBuffer的toString方法可以获取对应的字符串
System.out.println(charBuffer);
// 把Channel的记录指针移动到最后
randomChannel.position();
System.out.println("randomChannel.position():"+randomChannel.position());
// 将buffer中所有数据输出
buffer.flip();
randomChannel.write(buffer);
buffer.clear();
randomChannel.close();
}catch(Exception e){
e.printStackTrace();
}
}

编码解码(字符集和Charset),(在String类里也提供了一个getBytes(String charset)方法,该方法返回byte[],也是使用指定的字符集将字符串转换成字节序列,跟这个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();
cbuff.put('孙');
cbuff.put('悟');
cbuff.put('空');
cbuff.flip();
// 将CharBuffer中的字符序列转换成字节序列
ByteBuffer bbuff = cnEncoder.encode(cbuff);
// 循环访问ByteBuffer中的每个字节
for (int i = ; i < bbuff.capacity() ; i++)
{
System.out.print(bbuff.get(i) + " ");
}
// 将ByteBuffer的数据解码成字符序列
System.out.println("\n" + cnDecoder.decode(bbuff));
}
}

15.9.5 文件锁

java8--NIO(java疯狂讲义3复习笔记)的更多相关文章

  1. java8--类加载机制与反射(java疯狂讲义3复习笔记)

    本章重点介绍java.lang.reflect包下的接口和类 当程序使用某个类时,如果该类还没有被加载到内存中,那么系统会通过加载,连接,初始化三个步骤来对该类进行初始化. 类的加载时指将类的clas ...

  2. java8--网络编程(java疯狂讲义3复习笔记)

    重点复习一下网络通信和代理 java的网络通信很简单,服务器端通过ServerSocket建立监听,客户端通过Socket连接到指定服务器后,通信双方就可以通过IO流进行通信. 需要重点看的工具类:I ...

  3. java8--异常处理(java疯狂讲义3复习笔记)

    try,catch,finally,throw,throws java将异常分为两种,Checked异常和Runtime异常. IndexOutOfBoundsException NumberForm ...

  4. java8--Mysql数据库与JDBC编程(java疯狂讲义3复习笔记)

    JDBC:java database connectivity 数据库基本命令: 启动 show databases; create database [if not exists] 数据库名; do ...

  5. java8--多线程(java疯狂讲义3复习笔记)

    多线程这块,平时用的框架里都封装好了,只有写批处理和工具包时用过几次.现在水平仅仅限于会用的程度,需要全面深入学习多线程. 主要内容:创建线程,启动线程,控制线程,多线程的同步,线程池,使用线程安全的 ...

  6. java8--IO工具类(java疯狂讲义3复习笔记)

    Paths类 public static void pathTest(){ Path path = Paths.get("~"); System.out.println(path) ...

  7. java8--IO(java疯狂讲义3复习笔记)

    产生文件 File file = new File("abc.txt"); if(!file.exists()){ System.out.println(file.exists() ...

  8. 参考《Java疯狂讲义》

    参考<Java疯狂讲义>Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例 1. Lambda ...

  9. java程序设计基础篇 复习笔记 第一单元

    java语言程序设计基础篇笔记1. 几种有名的语言COBOL:商业应用FORTRAN:数学运算BASIC:易学易用Visual Basic,Delphi:图形用户界面C:汇编语言的强大功能和易学性,可 ...

随机推荐

  1. Images for Journals

    Images for publication Table of Contents 1. Images for publication 1.1. image format : vector image ...

  2. 03 数据解析-Xpath

    Xpath解析 XPath在Python的爬虫学习中,起着举足轻重的地位,对比正则表达式 re两者可以完成同样的工作,实现的功能也差不多,但XPath明显比re具有优势,在网页分析上使re退居二线. ...

  3. LeetCode(22)Generate Parentheses

    题目 Given n pairs of parentheses, write a function to generate all combinations of well-formed parent ...

  4. XV6环境搭建及注意事项

    Ubuntu16.04SLT 64位 工具链 sudo apt-get install gcc-multilib libsdl1.2-dev, libtool-bin, libglib2.0-dev, ...

  5. Leetcode 318.最大单词长度乘积

    最大单词长度乘积 . 示例 1: 输入: ["abcw","baz","foo","bar","xtfn&qu ...

  6. laravel 文件删除

    删除文件 <?php class demo{ public function del(){ $disk = Storage::disk('public');//获取磁盘实例 $disk-> ...

  7. POJ-2387Til the Cows Come Home,最短路坑题,dijkstra+队列优化

    Til the Cows Come Home Time Limit: 1000MS   Memory Limit: 65536K       http://poj.org/problem?id=238 ...

  8. codeforces #301 div2

    A:简单题 每次判断向上转快,还是向下转快即可 #include <cstdio> #include <cstring> #include <iostream> # ...

  9. C# 通过HTTP代理访问Socket来获取邮件

    C# 通过HTTP代理访问Socket来获取邮件 关键穿透代理的代码(通过HTTP代理获取TcpClent) public class ClientHelper { public static Tcp ...

  10. HTML介绍&常用的标签

    HTML介绍 1. web服务器本质 import socket s = socket.socket() s.bind(('127.0.0.1', 8080)) s.listen(5) while T ...