Java开发学习实战教程之JavaNIO

Java中的I/O流或者输入/输出流是指数据在本地文件或网络中以流的方式进行传输。新的输入/输出(NIO)库是在JDK1.4版本中引入的。NIO弥补了原来的I/O的不足,它在标准Java代码中提供了高速的、面向块的I/O。
原来的I/O库与NIO最重要的区别是数据打包和传输方式的不同,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。
面向流的I/O系统一次一个字节地处理数据。一个输入流读取一个字节的数据,一个输出流写出一个字节的数据,为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的I/O通常相当慢。
NIO与原来的I/O有同样的作用和目的,但是它使用块I/O的处理方式。每一个操作都在一步中读取或者写出一个数据块。按块处理数据比按流式的字节处理数据要快很多。但是面向块的I/O缺少一些面向流的I/O所具有的优雅性和简单性。
下面我们从一个简单的使用IO和NIO读取一个文件中的内容为例,进行NIO的学习。

/**
 * 使用IO读取指定文件的前1024个字节的内容
 * @param file 指定文件名称
 * @throws java.io.IOException IO异常
 */
public void ioRead(String file) throws IOException {
FileInputStream in = new FileInputStream(file);
byte[] b = new byte[1024];
in.read( b );
System.out.println(new String(b));
}
 
/**
 * 使用NIO读取指定文件的前1024个字节的内容
 * @param file 指定文件名称
 * @throws java.io.IOException IO异常
 */
public void nioRead(Stirng file) thorws IOException {
FileInputStream in = new FileInputStream(file);
FileChannel channel = in.getChanel();
 
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read( buffer );
byte[] b = buffer.array();
System.out.println( new String( b ));
}

通道和缓冲区是NIO中的核心对象,几乎在每一个I/O操作中都要使用它们。
缓冲区(Buffer)实质上是一个容器对象,它包含一些要写入或者刚读出的数据。在NIO中加入Buffer对象,体现了新库与原I/O的一个重要区别。
在面向流的I/O中,将数据直接写入或者将数据直接读到Stream对象中。
在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问NIO中的数据,都是将它放到缓冲区中。
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。最常用的缓冲区类型是ByteBuffer。 一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的获取和设置)。
通道(Channel)是对原I/O包中的流的模拟,可以通过它读取和写入数据。拿NIO与原来的I/O做个比较,通道就像是流。
正如前面提到的,所有数据都通过Buffer对象来处理。永远不会将字节直接写入通道中,相反,而会将数据写入包含一个或者多个字节的缓冲区。同样,不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是
InputStream或者OutputStream的子类), 而通道可以用于读、写或者同时用于读写。
读和写是I/O的基本过程。从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中。写入也相当简单:创建一个缓冲区,用数据填充它,然后让通 道用这些数据来执行写入操作。
如果使用原来的I/O,那么我们只需创建一个FileInputStream并从它那里读取。而在NIO中,情况稍有不同:我们首先从FileInputStream获取一个FileChannel对象,然后使用这个通道来读取数据。
在NIO系统中,任何时候执行一个读操作,都是从通道中读取,但是不是直接从通道读取。因为所有数据最终都驻留在缓冲区中,所以是从通道读到缓冲区中。
现在,让我们看一下NIO基本读写数据的过程。
在NIO中读取文件涉及的三个步骤:

// 第一步是获取通道。我们从 FileInputStream 获取通道:
FileInputStream fin = new FileInputStream( "readandshow.txt" );
FileChannel fc = fin.getChannel();
// 下一步是创建缓冲区:
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
// 最后,需要将数据从通道读到缓冲区中:
fc.read( buffer );

在 NIO 中写入文件类似于从文件中读取

// 首先从 FileOutputStream 获取一个通道:
FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );
FileChannel fc = fout.getChannel();
// 下一步是创建一个缓冲区并在其中放入一些数据,这里,用data来表示一个持有数据的数组。
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
for (int i=0; i<data.length; ++i) {
buffer.put( data[i] );
}
buffer.flip();
// 最后一步是写入缓冲区中:
fc.write( buffer );

下面使用NIO进行读写结合,将一个文件的所有内容拷贝到另一个文件中

/**
 * 将一个文件的所有内容拷贝到另一个文件中。
 * 执行三个基本操作:
 *     首先创建一个 Buffer
 *     然后从源文件中将数据读到这个缓冲区中
 *     最后将缓冲区写入目标文件
 *     程序不断重复(读、写、读、写) 直到源文件结束
 */
public static void main(String[] args) throws Exception {
    String infile = "C:\\copy.sql";String outfile = "C:\\copy.txt";
    // 获取源文件和目标文件的输入输出流
    FileInputStream fin = new FileInputStream(infile);
    FileOutputStream fout = new FileOutputStream(outfile);
    // 获取输入输出通道
FileChannel fcin = fin.getChannel();
FileChannel fcout = fout.getChannel();
    // 创建缓冲区
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while (true) {
        // clear方法,重设缓冲区,使它可以接受读入的数据
        buffer.clear();
        // 从输入通道中将数据读到缓冲区
        int r = fcin.read(buffer);
        // read方法,返回读取的字节数,可能为零,如果该通道已到达流的末尾则返回-1
        if (r == -1) {
            break;
        }
        // flip方法,让缓冲区可以将新读入的数据,写入到另一个通道中
        buffer.flip();
        // 从输出通道中将数据写入缓冲区
        fcout.write(buffer);
}
}

通过以上的介绍,我们懂得了什么是NIO,了解了NIO与IO的区别,以及如何通过NIO进行文件的基本读写操作。若您对NIO感兴趣,还可以进行更深入的学习了解。

上一篇:mysql innodb double write概念汇总


下一篇:【Innodb】插入缓存,两次写,自适应hash索引