Java NIO学习笔记(二)

版权声明:本文为博主原创文章,转载请注明出处! https://blog.csdn.net/q1406689423/article/details/85247962

上一篇文章写了NIO中缓冲区(Buffer)的部分,这篇写通道(Channel)的部分。

首先呢,先说一下什么是通道。通道是一种类似输入输出流的东西,用来读取和写入数据,只是它不是直接操作通道,而是先读取到缓冲区,再通过缓冲区进行读取或写入。
通道本身是进行双向操作的,也就是既可以完成输入操作,还可以完成输出操作。
Channel本身是一个接口,接口里只定义了两个方法:

  1. isOpen():判断此通道是否是打开的
  2. close():关闭通道

平时常用的类主要是FileChannel类,这个类是进行文件读写操作的。
常用的方法有:

  1. read(ByteBuffer dst):将内容读入缓冲区
  2. write(ByteBuffer src):将内容从缓冲区写入到通道
  3. write(ByteBuffer src):将内容从缓冲区写入到通道
  4. write(ByteBuffer src):将内容从缓冲区写入到通道write(ByteBuffer src)将内容从缓冲区写入到通道
  5. close():关闭通道
  6. MappedByteBuffer map(FileChannel,MapMode mode,long position,long size):将通道的文件区域映射到内存中,同时指定映射模式,文件中的映射文件以及要映射的文件以及要映射的区域大小

要想使用FileChnnel类,可以通过FileInputStream或FileOutputStream类中getChannel()方法获取输入或输出的通道,先看一个例子:

public static void main(String[] args) throws IOException {
		String[] info = { "你好", "之华", "hello", "zhihua" };// 待输出数据
		File file = new File("d:/test.txt");// 要输出的文件
		FileOutputStream fos = new FileOutputStream(file);// 实例化文件输出流
		FileChannel fout = fos.getChannel();// 声明输出通道,并得到文件通道
		ByteBuffer buffer = ByteBuffer.allocate(1024);// 开辟缓冲
		for (int i = 0; i < info.length; i++) {// 循环将数据写入缓冲不过
			buffer.put(info[i].getBytes("UTF-8"));
		}
		buffer.flip();// 重设缓冲区,准备输出
		fout.write(buffer);// 输出
		fout.close();
		fos.close();
	}

在打开D盘下test.txt文件之后就看到了

你好之华hellozhihua

在上面的例子,我们使用了输出通道,还用到了FIleOutputStream,但是我们讲了,通道是可以双向操作的,下面进行一下演示。现在D盘下创建两个文件,t1.txt和t2.txt,并且在t1里面写入内容。运行如下代码

public static void main(String[] args) throws IOException {
		File file1 = new File("d:/t1.txt");
		File file2 = new File("d:/t2.txt");
		FileInputStream fis = null;
		FileOutputStream fos = null;
		fis = new FileInputStream(file1);
		fos = new FileOutputStream(file2);
		java.nio.channels.FileChannel fin = null;
		java.nio.channels.FileChannel fout = null;
		fin = fis.getChannel();
		fout = fos.getChannel();
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		while ((fin.read(buffer)) != -1) {
			buffer.flip();// 重置缓冲区
			fout.write(buffer);// 从缓冲区写入磁盘
			buffer.clear();
		}
		fin.close();
		fout.close();
		fis.close();
		fos.close();
		System.out.println("finish!");
	}

这时打开t2.txt,发现t2.txt中的内容已经和t1.txt中的内容是一样的了,也就是表明成功将数据存入到了t2.txt中。
通过FileChannel,还可以进行内存映射的操作。内存映射,是将文件内容存入内存,在内存里对数据进行操作,这样处理速度就会远快于在磁盘中进行操作。
要将文件映射到内存中,调用的是FileChannel类提供的 map() 方法。同样写个例子,先在D盘建一个xxx.txt文件,往里面写些内容。

public static void main(String[] args) throws IOException {
		File file = new File("d:/xxx.txt");// 定义一个文件对象
		FileInputStream fis = new FileInputStream(file);// 定义文件输入流
		FileChannel fin = fis.getChannel();// 根据文件输入流获得文件通道对象
		MappedByteBuffer mbb = fin.map(FileChannel.MapMode.READ_WRITE, 0,
				file.length());// 设置内存映射模式为制度,起始位置为0,结束位置为file.length
		byte data[] = new byte[(int) file.length()];// 新建一个文件大小的byte数组

		int i = 0;// 设置下标
		while (mbb.hasRemaining()) {
			data[i++] = mbb.get();// 通过内存映射获得每个byte数组的值,并复制给data中相应位置
		}
		System.out.println(new String(data, "utf-8"));
		fin.close();
		fis.close();
	}

这里map()方法的返回值是MappedByteBuffer类型的对象,它是一个专门用来做内存映射的Buffer,打印的结果是

春花秋月何时了,
往事知多少,
小楼昨夜又东风,
故国不堪回首月明中。
雕镂玉砌应犹在,
只是朱颜改,
问君能有几多愁,
恰似一江春水向东流。

这是正是我写入文件中的内容。在代码里可以看到map()方法的第一个参数为FileChannel.MapMode.READ_ONLY 这是一种内存映射模式,为只读模式。除此之外还有另外两种模式:

FileChannel.MapMode.PRIVATE:专用(写入时复制)映射模式
FileChannel.MapMode.READ_WRITE:读取/写入映射模式

另外需要注意,使用内存映射可能会导致磁盘上的内容也跟着改变,所以使用的时候要慎重。

通道还支持对文件进行锁定,即文件锁的功能。文件锁需要用到另一个类FileLock ,下面也是看代码

public static void main(String[] args) throws IOException,
			InterruptedException {
		File file = new File("d:/xxx.txt");
		FileOutputStream fos = new FileOutputStream(file);
		FileChannel fout = fos.getChannel();
		FileLock lock = fout.tryLock();
		if (lock != null) {
			System.out.println("锁定文件");
			Thread.sleep(3000);
			lock.release();
			System.out.println("解锁文件");
		}
		fout.close();
		fos.close();
	}

出现的结果就是在打印“锁定文件”3秒之后,打印了“解锁文件”。也就是在将文件锁定之后将其释放。当文件在一个线程里被锁定之后,别的线程就无法再对该文件进行操作了。

猜你喜欢

转载自blog.csdn.net/q1406689423/article/details/85247962
今日推荐