输入/输出流都是阻塞式的(eg:InputStream的read方法从流中读取数据时,如果数据源中没有数据,它会阻塞该线程)
传统的输入流、输出流都是通过字节的移动来处理,面向流的输入/输出系统只能一次处理一个字节,因此效率不高。
Java新IO将文件或文件的一段区域映射到内存中,效率比较高。
Channel(通道)和Buffer(缓冲)是新IO中的两个核心对象在新IO系统中所有的数据都需要通过通道传输:Channel和传统的InputStream、OutputStream最大的区别在于它提供了一个map()方法,通过该map()方法可以直接将“一块数据”映射到内存中。
新IO还提供了用于将Unicode字符串映射成字符序列以及逆映射操作的Charset类,也提供了用于支持非阻塞式输入/输出的Selector类。
Buffer中有三个重要概念:
容量(capacity):最大数据量;
界限(limit):limit后的数据既不可以被读,也不可以被写。
位置(position):下一个可以被读取或者写入缓冲区位置的索引,第一个位置的索引是0。
import java.nio.CharBuffer;
public class BufferTest {
public static void main(String[] args) {
CharBuffer charBuffer = CharBuffer.allocate(6);
System.out.println("容量="+charBuffer.capacity());
System.out.println("界限limit="+charBuffer.limit());
System.out.println("位置="+charBuffer.position());
charBuffer.put('1');
charBuffer.put('1');
charBuffer.put('2');
System.out.println("加入三个元素后的位置索引="+charBuffer.position());
charBuffer.flip();
System.out.println("调用flip()方法后,limit="+charBuffer.limit());
System.out.println("调用flip()方法后,位置索引="+charBuffer.position());
//开始取数据
System.out.println("取出第一个元素="+charBuffer.get(0));
System.out.println("取出第一个元素后,位置索引="+charBuffer.position());
//调用clear()
charBuffer.clear();
System.out.println("调用clear()方法后,limit="+charBuffer.limit());
System.out.println("调用clear()方法后,位置索引="+charBuffer.position());
System.out.println("取出第三个元素="+charBuffer.get(2));
charBuffer.put('1');
charBuffer.put('2');
charBuffer.put('3');
charBuffer.put('4');
charBuffer.put('5');
charBuffer.put('6');
System.out.println("重新put值之后,取出第三个元素="+charBuffer.get(2));
System.out.println("取出第三个元素后,位置索引="+charBuffer.position());
}
}
调用Buffer的flip()后,该方法将limit设置为position所在位置,并将position设置为0,即Buffer的读写指针又回到了开始位置,调用flip()方法之后,Buffer为输出数据做好准备;当Buffer输出数据结束后,Buffer调用clear()方法,clear()方法不是清空数据,它只是将position的设置为0,将limit设置成capacity,这样再次向Buffer中装入数据做好准备,put数据的时候将会覆盖之前的数据。
实际使用较多的是ByteBuffer和CharBuffer,ByteBuffer类还有一个子类:MappedByteBuffer,它用于表示Channel将磁盘文件部分或全部内容映射到内存中后得到的结果,通常MappedByteBuffer对象由Channel的map()方法返回。
Channel(通道)
(1)Channel可以直接将指定文件的部分或全部直接映射成Buffer。
(2)程序不能直接访问Channel中的数据,读写都不允许,Channel只能与Buffer进行交互。新IO中的Channel是按功能划分的,eg:Pipe.SinkChannel、Pipe.SourceChannel是用于支持线程之间通信的管道Channel;ServerSocketChannel、SocketChannel是用于支持TCP网络通信的Channel;而DatagramChannel则是用于支持UDP网络通信的Channel。
Channel最常用的三个方法是:map()、read()、write(),其中map()方法用于将Channel对应的部分或全部数据映射成ByteBuffer;而read()或write()方法都有一系列重载形式。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class FileChannelTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
File file = new File("InputStreamTest.java");
try {
FileChannel inFileChannel = new FileInputStream(file).getChannel();
FileChannel outFileChannel = new FileOutputStream("file.txt").getChannel();
//将inFileChannel里的全部数据映射到ByteBuffer中
MappedByteBuffer mappedByteBuffer = inFileChannel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
//直接将buff中数据全部读出
outFileChannel.write(mappedByteBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
读取任意字节数据,防止使用map()方法一次将所有的文件内容映射到内存引起性能下降
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
public class ReadFile {
public static void main(String[] args) {
try(FileChannel fileChannel = new FileInputStream("InputStreamTest.java").getChannel();) {
//一次去1024字节
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while(fileChannel.read(byteBuffer)!=-1){
//锁定byteBuffer中的空白区,开始读取数据
byteBuffer.flip();
//创建CharsetDecoder对象
Charset charset = Charset.forName("GBK");
//将ByteBuffer中的内容转码
CharBuffer charBuffer = charset.decode(byteBuffer);
System.out.println(charBuffer);
//为下次装入数据做准备
byteBuffer.clear();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}