输入和输出--java的NIO

  • Java的NIO

实际开发中NIO使用到的并不多,我并不是说NIO使用情景不多,是说我自己接触的并不是很多,前面我在博客园和CSDN上转载了2篇别人写的文章,这里来大致总结下Java的NIO,大概了解下。

  • NIO和传统IO的对比:

在使用传统IO的时候,不管是使用节点流这种底层流还是使用处理流这种高级流,在底层操作的都是字节,所以性能就不会很好,在使用BufferedReader这种高级流的时候还会阻塞该线程,所以Java1.4后出现了NIO。

NIO相关的类都放在了Java.nio包下,他的功能比较强大的。NIO使用了不同的方式(内存映射文件)来处理输入和输出,也就是说NIO将文件或文件的一段区域映射到内存中,这样子就可以象访问内存一样访问文件了。

NIO的2个核心:Channel(通道)和Buffer(缓存)。

  • 说明下这2个核心:

Buffer就是一个数组。他的作用就是装入数据和输出数据。Buffer有2个重要的方法:flip(从Buffer中取出数据)和clear(向Buffer中放入数据)

Channel可以直接将指定文件的部分或者全部直接映射成buffer。程序也不能直接和Channel来交互数据,包括读和写,都要经过Buffer这个中间的东西才可以。

  • 从理论到实践:NIO 中的读和写

读和写是 I/O 的基本过程。

1,从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中。

2,写入也相当简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作。

  • 从文件中读取

如果使用原来的 I/O,那么我们只需创建一个 FileInputStream 并从它那里读取。而在 NIO 中,情况稍有不同:我们首先从FileInputStream 获取一个 FileInputStream 对象,然后使用这个通道来读取数据。

在 NIO 系统中,任何时候执行一个读操作,都是从通道中读取,但是不能直接从通道读取。因为所有数据最终都驻留在缓冲区中,所以要从通道读到缓冲区中。

因此读取文件涉及三个步骤:(1) 从 FileInputStream 获取 Channel,(2) 创建 Buffer,(3) 将数据从 Channel 读到 Buffer 中。

第一步:从FileInputStream获取通道:

FileInputStream fin = new FileInputStream( "LinkinPark.txt" );

FileChannel fc = fin.getChannel();

第二步:创建缓冲区:

ByteBuffer buffer = ByteBuffer.allocate( 1024 );

第三步:将数据从通道读到缓冲区中:

fc.read( buffer );

注意:我们不需要告诉通道要读多少数据到缓冲区中。每一个缓冲区都有复杂的内部统计机制,它会跟踪已经读了多少数据以及还有多少空间可以容纳更多的数据

  • 写入文件

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

第一步:从 FileOutputStream 获取一个通道:

FileOutputStream fout = new FileOutputStream( "LinkinPark.txt" );

FileChannel fc = fout.getChannel();

第二步:创建一个缓冲区并在其中放入一些数据

ByteBuffer buffer = ByteBuffer.allocate( 1024 );

for (int i=0; i<message.length; ++i) {

buffer.put( message[i] );

}

buffer.flip();

第三步:写入缓冲区中

fc.write( buffer );

注意:在这里同样不需要告诉通道要写入多数据。缓冲区的内部统计机制会跟踪它包含多少数据以及还有多少数据要写入。

  • 读写结合

下面我们将看一下在结合读和写时会有什么情况。我们以一个名为 CopyFile.java 的简单程序作为这个练习的基础,它将一个文件的所有内容拷贝到另一个文件中。CopyFile.java 执行三个基本操作:首先创建一个 Buffer,然后从源文件中将数据读到这个缓冲区中,然后将缓冲区写入目标文件。这个程序不断重复 ― 读、写、读、写 ― 直到源文件结束。

CopyFile 程序让您看到我们如何检查操作的状态,以及如何使用 clear() 和 flip() 方法重设缓冲区,并准备缓冲区以便将新读取的数据写到另一个通道中。下面是一个例子:

1,因为缓冲区会跟踪它自己的数据,所以 CopyFile 程序的内部循环 (inner loop) 非常简单

fcin.read( buffer );

fcout.write( buffer );

第一行将数据从输入通道 fcin 中读入缓冲区,第二行将这些数据写到输出通道 fcout 。

2,检查状态,检查拷贝何时完成。当没有更多的数据时,拷贝就算完成,并且可以在 read() 方法返回 -1 是判断这一点

int r = fcin.read( buffer );

if (r==-1) {

break;

}

3,重设缓冲区

最后,在从输入通道读入缓冲区之前,我们调用 clear() 方法。同样,在将缓冲区写入输出通道之前,我们调用 flip() 方法

buffer.clear();

int r = fcin.read( buffer );

if (r==-1) {

break;

}

buffer.flip();

fcout.write( buffer );

clear() 方法重设缓冲区,使它可以接受读入的数据。 flip() 方法让缓冲区可以将新读入的数据写入另一个通道。

下面代码大致用来演示下Java的NIO,我从来没有用过,所以也就直接从资料粘代码过来了:

import java.nio.*;

public class BufferTest
{
	public static void main(String[] args)
	{
		// 创建Buffer
		CharBuffer buff = CharBuffer.allocate(8);    //①
		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(2));    // ⑥
		System.out.println("执行绝对读取后,position = "
			+ buff.position());
	}
}
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

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

public class RandomFileChannelTest
{
	public static void main(String[] args)
		throws IOException
	{
		File f = new File("a.txt");
		try(
			// 创建一个RandomAccessFile对象
			RandomAccessFile raf = new RandomAccessFile(f, "rw");
			// 获取RandomAccessFile对应的Channel
			FileChannel randomChannel = raf.getChannel())
		{
			// 将Channel中所有数据映射成ByteBuffer
			ByteBuffer buffer = randomChannel.map(FileChannel
				.MapMode.READ_ONLY, 0 , f.length());
			// 把Channel的记录指针移动到最后
			randomChannel.position(f.length());
			// 将buffer中所有数据输出
			randomChannel.write(buffer);
		}
	}
}
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class ReadFile
{
	public static void main(String[] args)
		throws IOException
	{
		try(
			// 创建文件输入流
			FileInputStream fis = new FileInputStream("ReadFile.java");
			// 创建一个FileChannel
			FileChannel fcin = fis.getChannel())
		{
			// 定义一个ByteBuffer对象,用于重复取水
			ByteBuffer bbuff = ByteBuffer.allocate(64);
			// 将FileChannel中数据放入ByteBuffer中
			while( fcin.read(bbuff) != -1 )
			{
				// 锁定Buffer的空白区
				bbuff.flip();
				// 创建Charset对象
				Charset charset = Charset.forName("GBK");
				// 创建解码器(CharsetDecoder)对象
				CharsetDecoder decoder = charset.newDecoder();
				// 将ByteBuffer的内容转码
				CharBuffer cbuff = decoder.decode(bbuff);
				System.out.print(cbuff);
				// 将Buffer初始化,为下一次读取数据做准备
				bbuff.clear();
			}
		}
	}
}

最后,关于Java的NIO还有2个知识,1个是编码和解码,1个是文件锁

编码和解码:  我们在玩j2ee的时候经常要最数据做编码和解码,因为要前后台交互数据,这里的编码和解码主要是说我们在使用NIO的时候对文件做读写操作时用来的编码和解码。编码就是把明文的字符串序列转换成计算机理解的字节序列,解码就是把字节序列转化成普通人能看得懂的明文字符串。 对于我来说使用最大的就2种字符串集,一个是GBK(简体中文字符集)一个是UTF-8(8位的UCS转化格式)。

import java.nio.charset.*;
import java.util.*;

public class CharsetTest
{
	public static void main(String[] args)
	{
		// 获取Java支持的全部字符集
		SortedMap<String,Charset>  map = Charset.availableCharsets();
		for (String alias : map.keySet())
		{
			// 输出字符集的别名和对应的Charset对象
			System.out.println(alias + "----->"
				+ map.get(alias));
		}
	}
}
import java.nio.*;
import java.nio.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(8);
		cbuff.put('孙');
		cbuff.put('悟');
		cbuff.put('空');
		cbuff.flip();
		// 将CharBuffer中的字符序列转换成字节序列
		ByteBuffer bbuff = cnEncoder.encode(cbuff);
		// 循环访问ByteBuffer中的每个字节
		for (int i = 0; i < bbuff.capacity() ; i++)
		{
			System.out.print(bbuff.get(i) + " ");
		}
		// 将ByteBuffer的数据解码成字符序列
		System.out.println("\n" + cnDecoder.decode(bbuff));
	}
}

文件锁在操作系统上是很平常的事情,使用文件锁可以有效的阻止多个进程并发修改同一个文件。其实和数据库的锁是差不多的。

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

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

		try(
			// 使用FileOutputStream获取FileChannel
			FileChannel channel = new FileOutputStream("a.txt")
				.getChannel())
		{
			// 使用非阻塞式方式对指定文件加锁
			FileLock lock = channel.tryLock();
			// 程序暂停10s
			Thread.sleep(10000);
			// 释放锁
			lock.release();
		}
	}
}

时间: 2024-10-04 22:53:41

输入和输出--java的NIO的相关文章

输入和输出--java序列化机制

对象的序列化 什么是Java对象的序列化? 对象序列化的目标是将对象保存到磁盘上,或允许在网络中直接传输对象.对象序列化机制允许把内存中的Java对象转换成与平台无关的二进制流,从而保存或者传输.其他的程序一旦获得这种二进制流,还可以把这种二进制流恢复成原来的Java对象 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础.因此序列化机制是 JavaEE 平台的基础.要养成良好的习惯,在写一个ja

Java输入及输出处理(I/O流)

IO流的分类: 按流向分类:输入流.输出流按操作对象分类: 字节流.字符流按功能分类: 节点流.处理流 Input 输入Output 输出Java.io.File类,文件输入输出类 字节流和字符流: 1,字节流InputStream(读),OutputStream(写).2,字符流:Reader(读),Writer(写).基本的读写操作方式:因为数据通常都以文件形式存在.所以就要找到 IO 体系中可以用于操作文件的流对象.通过名称可以更容易获取该对象.因为 IO 体系中的子类名后缀绝大部分是父类

1.java.io包中定义了多个流类型来实现输入和输出功能,

1.java.io包中定义了多个流类型来实现输入和输出功能,可以从不同的角度对其进行分 类,按功能分为:(C),如果为读取的内容进行处理后再输出,需要使用下列哪种流?(G)   A.输入流和输出流 B.字节流和字符流 C.节点流和处理流   D.File stream E.Pipe stream F.Random stream G.Filter stream

JAVA问题总结13-从键盘输入并输出

JAVA问题总结13-从键盘输入并输出,加从键盘输入三个数,输出并按从大到小排序. package java2; /** * 这是一个从键盘输入并输出的一个test * @author xubo601450868 * @time 2015.8.26 * @version v1.0 * */ import java.util.Scanner; public class test4 { public static void main(String[] args){ Scanner s=new Sca

Java 调用有输入和输出的存储过程

--有输入和输出的存储过程 SQL> create or replace procedure sp_pro8( spNo in varchar2, spName out varchar2) is begin select sname into spName from student where sid= spNo; end; 1 package oracle; 2 3 import java.sql.CallableStatement; 4 import java.sql.Connection;

java 线程 生产者-消费者与队列,任务间使用管道进行输入、输出 讲解示例 --thinking java4

package org.rui.thread.block2; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedB

Java如何处理输入和输出

在Java中为了进行I/O操作,需要用适当的I/O类创建对象,这些对象包含从文件中读数据的方法和向文件中写数据的方法. 例如:要将文本写入temp.txt的文件, 1 PrintWriter pw = new PrintWriter("temp.txt"); 2 pw.print("Hello Java!"); 3 pw.close(); 4 5 Scanner input = new Scanner(new File("temp.txt"));

Java学习笔记—第十章 数据输入与输出

第十章  数据输入与输出 输入流与输出流: (1)输入流:以程序为基准,向程序中输入数据的流定义为输入流.从输入流向程序中输入数据称为读数据(read). (2)输出流:以程序为基准,从程序输出数据的流称为输出流.从程序中将数据输出到输出流称为写数据(write). 字节流和字符流 (1)字节流:按照字节的形式读/写数据.Java中抽象类InputStream和OutputStream及其派生子类用来处理字节流的输入和输出. (2)字符流:按照字符的形式读/写数据.Java中抽象类Reader和

java 输入和输出 代码示例

此处省略介绍, package org.rui.io.inout; import java.io.BufferedReader; import java.io.FileReader; /** * 缓冲输入文件 * @author lenovo * */ public class BufferedInputFile { public static String read(String filename) throws Exception { BufferedReader br=new Buffer