NIO揭秘-1

      时间过得真快,一晃就大四上学期了,马上面临着要找工作的压力和挑战,虽然我不怕失败,但心里还是忐忑不安,最近闲来无事,就把一直没有时间看的NIO顺便了解了解下。

      本次NIO系列都是基于IBM的Developerworks上的一篇pdf文档,我也就是拾人牙慧,如有值得商榷的地方,欢迎大家拍砖。

      本次主要讨论一下Channels和Buffers。

      Overview:

        Channels和Buffers是构成NIO的主要部件,他们被广泛的使用在每一个IO操作中。Channels类似于原IO包中的流的概念。所有流动的数据(不管是流进还是流入)都必须经过Channel。而Buffer则在本质上是一个容器,所有被送到channel中的数据首先必须放在buffer中,同样的,所有从channel中读入的数据也应先放在buffer中。

     what is a buffer?

        buffer是一个存装数据的实体,可以被写入或者只能从中读取数据。这些附加的属性使得buffer与原IO包中有明显的差异。在原来以流为主的IO中,你直接从流中读入数据,或者将数据写入流中。但在NIO中,所有的数据都被buffer持有,当数据被读入时,它将被直接读入到一个buffer中,当数据被写出时,也将写入到buffer中,所以在NIO中获得数据,都是从buffer中获取的。一个buffer本质上是一个数组,通常是一个字节数组,当然也有其他类型的数组。但且不仅仅是一个数组,它提供了有组织的获得数据的方式,同时也保证了系统的读/写进程。

      Kind of buffers

        在大多数应用条件下,一个buffer就是一个bytebuffer,bytebuffer提供了get和set操作底层的字节数组。和刚刚说的一样,buffer还可以是CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer等等,每一个buffer类都实现了buffer这个接口,除了bytebuffer外,其他的buffer都有着相同的操作方法,当然操作的数据类型是不一样的。因为bytebuffer被大多数的标准IO操作所使用,所以有一些独特的共享数据的方法。 

    what is the channel?

        一个channel就是一个可以读入数据和写入数据的构件,相比于原来的IO,就相当于stream。正如前面提到的,所有的数据都将经过buffer,你不可能直接将数据写入channel,相反你不得不将数据写入buffer。同样的,你不能直接从channel中读入数据,你必须将一个channel和一个buffer关联起来,然后从buffer中读入数据。

     Kinds of channels

        channels是双向的,这点不同于以往的stream。stream必须是inputstream或者是outputstream的子类,是单向的,但channel既可以读,或者写,或者两件兼之。因为channel是双向的,所以能比stream更形象的描述操作系统的模型。在当今比较流行的unix上,底层操作系统的管道也是双向的。

      From theory to practice : Reading and writing in NIO

        overview

        读和写是IO的基本操作,从channel中读取数据是简单的,我们只需要创建一个buffer然后让channel从这个buffer中读出数据即可。写同样是简单的,我们创建一个buffer,然后往里填充数据,再让channel从中写出。(注意这里的读出和写入,原文是这样的:Reading from a channel is simple: we simply create a buffer and then ask a channel to read data into it. Writing is
also fairly simply: we create a buffer, fill it with data, and then ask a channel to write from it.)在这一节中,我们接下来将用java语言来编写一些读或者写的实例,并复习一下NIO的组件(buffer,channel和一些相关的方法)。

      Reading from a file

        我们将从文件中读入数据这个场景作为我们的第一个实例。如果我们使用原始的IO,我们可以创建一个FileInoutStream来读取,在NIO中,事情变得有点复杂,我们必须先从FileInputStream上创建一个channel,然后用这个channel读取数据。

        不管什么时候你从NIO中使用一个读操作,你将不得不从channel中读入数据,但却不是直接的从channel中读入。由于所有的数据都存在于buffer中,你得使用channel从buffer中读取。

        所以从一个文件中读取数据需要以下几步:1.从FileInputStream中得到一个channel。2.创建一个buffer。3.使用channel从buffer中读取数据。

package com.nio.example;

import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class UseChannelReadFromFileTest {

	public static void main(String[] args) throws Exception {

		/** 首先创建一个FileInputStream 和channel */
		FileInputStream fin = new FileInputStream("./src/ex.txt");
		FileChannel channel = fin.getChannel();

		/** 然后创建一个buffer */
		ByteBuffer buffer = ByteBuffer.allocate(1024);

		/** 使用channel将数据读入到buffer中 */
		int len = 0;
		while (-1 != (len = channel.read(buffer))) {
			System.out.print(translate2Char(buffer.array(), len));

			buffer.clear();
		}

		channel.close();
		fin.close();

	}

	/** 将字节数组转换成字符数组,以便还原文件内容 */
	private static char[] translate2Char(byte[] array, int len) {

		char[] seq = new char[len];

		for (int i = 0; i < len; i++) {
			seq[i] = (char) array[i];
		}

		return seq;
	}

} 

        我们可以看到bytebuffer中还有很多值得探究的地方,不要着急,后面我们会细细道来。

      Writing to a file

        和读文件差不多,我们首先也要从一个FileOutputStream中获得一个channel。然后下一步就是创建一个buffer然后放入数据,buffer.flip()和buffer.put()的调用我们将在后面解释。

ByteBuffer buffer = ByteBuffer.allocate(1024);

for( int i = 0 ; i < message.length; ++i ){
    buffer.put(message[i]);
}

buffer.flip();

        最后,我们写入buffer。

fc.write( buffer );

    注意我们这里并不需要告诉channel我们有多少数据期望写入,buffer内部有一种机制保证了这种正确性。

    完整实例如下:

package com.nio.example;

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class UseChannelWriteToFileTest {
	
	public static void main(String[] args) throws Exception {
		
		/** 首先从fileoutputstream中获得channel */
		FileOutputStream fout = new FileOutputStream("./src/wt.txt");
		FileChannel channel = fout.getChannel();
		
		/** 准备一个装有数据的缓冲区 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		String message = "Change project.properties compass-version to the actual version number (it should probably already be set)";
		
		/** 填充数据 */
		fileData2Buffer(message,buffer);
		
		buffer.flip();
		
		/** 将数据写回文件 */
		channel.write(buffer);
		
		channel.close();
		fout.close();
		
	}
	
	/** 将message中的数据填充入buffer中 */
	private static void fileData2Buffer(String message, ByteBuffer buffer) {
		
		int len = message.getBytes().length;
		
		for( int i = 0 ; i < len; ++i ){
			
			buffer.put(message.getBytes()[i]);
			
		}
		
	}
	
}


    Reading and writing together

        下面我们看看将读和写结合在一起会发生什么。我们这个实例的名字叫做copyFile.java,它将一个文件的数据拷贝到另一个文件。copyFile有三个基本的操作:1.首先创建一个buffer,2.从源文件中读入数据并存入buffer中,3.然后将buffer写回到目标文件中。程序就这样往复循环,读,写,读,写。。。。。。直到源文件被读完。

        这个copyFile程序将让你看到我们如何检查操作的状态,就如我们使用clear()和flip()方法去重置buffer让其准备读入或者新的数据或者写入到另外的一个channel中。

        因为是对buffer中的数据进行操作,所以内层循环相当simple,如下:

fcin.read( buffer );
fcout.write( buffer );

        第一行将数据从channel中读入到buffer中,第二行将buffer中的数据写入到channel中。

        下一步呢,我们将检查是否已经拷贝成功,我们可以观察read()方法的返回值,如果返回--1,则文件读取完毕。

int r = fcin.read( buffer );

if ( r == -1 ) { break; }

        重置buffer:

        我们在将数据读入input channel之前调用claer()方法,同样的,我们在将数据写入output channel之前调用flip()方法,如下:

buffer.clear();
int r = fcin.read( buffer );

if( -1 == r )  { break; }

buffer.flip();
fout.write( buffer );

        clear()方法重置了buffer,让其可以将数据读入其中,flip()方法准备buffer中新读入的数据写入到一个output channel中。完整实例如下:

package com.nio.example;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class CopyFile {

	public static void main(String[] args) throws Exception {

		/** 首先获得input channel和output channel */
		FileInputStream fin = new FileInputStream("./src/ex.txt");
		FileChannel inChannel = fin.getChannel();

		FileOutputStream fout = new FileOutputStream("./src/ex_backup.txt");
		FileChannel outChannel = fout.getChannel();

		/** 然后开辟一个缓冲区 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);

		/** 然后从文件中读入数据到buffer中 */
		while (-1 != inChannel.read(buffer)) {
			
			buffer.flip();
			outChannel.write(buffer);
			buffer.clear();
			
		}
		
		outChannel.close();
		fout.close();
		inChannel.close();
		fin.close();

	}

}
    好了,第一篇就写到这里,我也是写作新手,有什么不对的,希望大家海涵。

你可能感兴趣的:(java,编程,IO,nio)