`

nio入门

    博客分类:
  • java
阅读更多
nio中主要是要学会Buffer和Channel的使用.
Buffer从它的名字就可以知道它表示的是一个缓冲. Buffer是一个抽象类, 它主要封装了与缓冲有关的一些抽象方法. 主要的方法有这几个:

int capacity() 这块缓冲的容量.

Buffer clear() 清空缓冲.

Buffer flip() 清算缓冲的实际使用大小(字节).

int limit() 缓冲实际使用了多少字节.

Buffer position(int newPosition) 将读取位置移动到指定的位置.

int position()
当前的读取位置, 以字节为单位, 每读取一个byte就会向后加1, 没读取一个char就会向后加2, 以此类推.

int remaining() 还有多少字节没读取(实际的字节数).

boolean hasRemaining() 是否还有没读取的字节数. 等同于remaining() > 0

Buffer rewind() 重新读取. (就是将当前位置放到开始处, 并重置mark标记)

boolean hasArray() 是否是以数组作为缓冲且数组缓冲是可读可写的.

abstract boolean isDirect()
该缓冲是否是一块直接缓冲(直接缓冲就是在系统层面创建的一块缓冲, 非直接缓冲说简单点就是使用一个数组作为缓冲)

boolean isReadOnly() 这个缓冲是不是只读的

Buffer limit(int newLimit) 调整实际使用的字节数.

Buffer mark() 在当前位置做一个标记, 重置时就可以回到这个位置

Buffer reset() 将当前位置重置到之前做的mark处 (注意没做mark是不允许重置的)

Object array() 如果是数组作为缓冲的话, 返回内部的数组; 直接缓冲不支持该方法

int arrayOffset()
如果是数组作为缓冲的话, 返回该Buffer是从数组中的哪个index开始的. (因为可能只是用了array的offset~offset+length部分来用作缓冲)


Channel: 可以理解为导管, 数据之间的传输使用导管来进行. 他仅仅是一个接口, 有两个很容易理解的方法:
void close() 关闭导管

boolean isOpen() 导管是否是打开着的(可以传输数据的)


Buffer和Channle的大体关系可以用下图来表示:



Buffer层次中最重要的是ByteBuffer, 因为其它几个Buffer都是基于ByteBuffer的:
其实ByteBuffer的使用和RandomAccessFile的使用时非常类似的:
1. 读取时, RandomAccessFile用的都是readXxx, ByteBuffer中用的都是getXxx
2. 写入时, RandomAccessFile用的都是writeXxx, ByteBuffer中用的都是putXxx

好了接下来就直接上一些代码了:
首先来看一段理解capacity和limit的代码:
// 假设我申请了一块20B大小的缓冲, 然后添加了一个byte和一个char, 那么实际使用时多少呢? 3个字节
ByteBuffer arrayBuffer = ByteBuffer.allocate(20);
arrayBuffer.capacity(); // 20个字节
arrayBuffer.put((byte) 1); // 添加一个byte
arrayBuffer.putChar('a'); // 添加一个char
arrayBuffer.flip(); // 清算实际使用了多少字节, 并将position放到开始处
arrayBuffer.limit(); // 实际使用了3个字节


直接缓冲和非直接缓冲的区别:
ByteBuffer arrayBuffer = ByteBuffer.allocate(20); // 可读写数组缓冲
arrayBuffer.isDirect(); // false, 因为是数组缓冲
arrayBuffer.isReadOnly(); // false
arrayBuffer.hasArray(); // true, 是数组缓冲且不是可读的

byte[] bytes = new byte[20];
arrayBuffer = ByteBuffer.wrap(bytes);
if (bytes == arrayBuffer.array()) {
    System.out.println("equals");
}

ByteBuffer directBuffer = ByteBuffer.allocateDirect(20);
directBuffer.isDirect();
directBuffer.isReadOnly(); // false
directBuffer.hasArray(); // false, 因为是直接缓冲


操作ByteBuffer, 向缓冲写入数据和从缓冲读取数据:
ByteBuffer arrayBuffer = ByteBuffer.allocate(50);
arrayBuffer.put((byte) 1);
arrayBuffer.putChar('2');
arrayBuffer.putInt(3);
arrayBuffer.putLong(4);
arrayBuffer.putFloat(5.0F);
arrayBuffer.putDouble(6.0);
arrayBuffer.position(); // 27
arrayBuffer.flip(); // 清算实际使用了多少字节(1+2+4+8+4+8=27), 并将position放到开始处

arrayBuffer.limit(); // 27

// 必须按照写入顺序读取, 否则读出来的结果将不正确
arrayBuffer.get();
arrayBuffer.getChar();
arrayBuffer.getInt();
arrayBuffer.getLong();
arrayBuffer.mark(); // 在当前位置做一个标记, 重置时就可以回到这个位置
arrayBuffer.getFloat();
arrayBuffer.getDouble();

arrayBuffer.reset(); // 重置到标记处, 即getLong()处
arrayBuffer.getFloat(); // 5.0F

arrayBuffer.rewind(); // 重读并清掉mark
arrayBuffer.get();
arrayBuffer.getChar(); // ...
// arrayBuffer.reset(); // mark清掉后再调用就要出错的


hasRemaining(), clear()的使用:
// 假设要读取的数据很大, 缓冲一次性装不下, 那就需要分成多次来读取了.
public static void copy() throws IOException {
	FileInputStream fis = null;
	FileOutputStream fos = null;
	try {
		fis = new FileInputStream("d:/src.txt");
		FileChannel src = fis.getChannel();
		fos = new FileOutputStream("d:/dst.txt");
		FileChannel dst = fos.getChannel();
		
		ByteBuffer arrayBuffer = ByteBuffer.allocate(256);
		while (src.read(arrayBuffer) != -1) {
			arrayBuffer.flip(); // src中的数据读取到缓冲中后, 清算实际读取大小
			dst.write(arrayBuffer); // 写出将缓冲中的数据写入目标
			arrayBuffer.clear(); // 本次数据读完, 清掉之前读取的数据, 继续下一次的读取
		}
	} finally {
		closeSinently(fis);
		closeSinently(fos);
	}
}


static void closeSinently(Closeable c) {
    if (c != null) {
        try {
            c.close();
        } catch (IOException ex) {
            // ignore
        }
    }
}


其它的基础类型Buffer类, 这些类可以看成是ByteBuffer的包装类.
比如: 用ByteBuffer是面向byte的, 用它来进行面向char的操作就会比较繁琐, 好那就把它包装为CharBuffer, 这样所有操作都以char为单位, 这样就方便多了.
CharBuffer 提供以char为单位的操作 (ByteBuffer.asCharBuffer())
ShortBuffer 提供以short为单位的操作 (ByteBuffer.asShortBuffer())
IntBuffer 提供以int为单位的操作 (ByteBuffer.asIntBuffer())
LongBuffer 提供以long为单位的操作 (ByteBuffer.asLongBuffer())
FloatBuffer 提供以float为单位的操作(ByteBuffer.asFloatBuffer())
DoubleBuffer 提供以double为单位的操作(ByteBuffer.asDoubleBuffer())

内存映射文件Buffer, MappedByteBuffer
有时候我们想在内存中修改一个很大的文件, 比如: 10G, 但一次性加载电脑又会吃不消, 有了MappedByteBuffer就可以了.
它帮我们处理好了如何把这10G的内容加载到内存中, 我们就当是这10G是内存数据一样使用.

用MappedByteBuffer来计算大文件的hash值的代码:
/***
 * <p> 获取文件的哈希校验值. 使用内存映射文件的方式来提升性能, 在获取大文件的哈希值时有用. </p>
 */
public static String getFileHashNio(long sizePerRead, String fileName, String hashType) throws IOException {
    FileChannel fchannel = null;
    try {  
        MessageDigest digest = MessageDigest.getInstance(hashType);  
        RandomAccessFile file = new RandomAccessFile(fileName, "r");
        fchannel = file.getChannel();
        
        long length = file.length();
        // long sizePerRead = 1<<29; // 还和操作系统和jdk版本有关, win7 32bit, jdk 32bit就不能设为1<<30, 会报错
        long time = (int) (length / sizePerRead);
        byte[] buffer = new byte[1024];
        if (time == 0) {
            digest(buffer, fchannel, digest, 0, length);
        } else {
            for (int i = 0; i < time; i++) {
                digest(buffer, fchannel, digest, i * sizePerRead, sizePerRead);
            }
            int remaining = (int) (length % sizePerRead);
            if (remaining > 0) {
                digest(buffer, fchannel, digest, time * sizePerRead, remaining);
            }
        }
        
        return bytes2Hex(digest.digest());
    } catch (NoSuchAlgorithmException ex) {  
        throw new IllegalArgumentException(hashType + " is not implemented by Providers");
    } finally {
        // nio中close Channel
        closeSilently(fchannel);  
        System.gc(); // 尝试释放map所占用的文件句柄(它要到gc的时候才会释放), jdk中没提供api来释放
    }
}

private static void digest(byte[] buffer, FileChannel fchannel, MessageDigest digest, long position, long size) throws IOException {
    // map会占用底层的文件句柄
    MappedByteBuffer mbuffer = fchannel.map(MapMode.READ_ONLY, position, size);
    int bufferLength = buffer.length;
    while (mbuffer.remaining() >= bufferLength) {
        mbuffer.get(buffer);
        digest.update(buffer);
    }
    int remaining = mbuffer.remaining();
    if (remaining > 0) {
        mbuffer.get(buffer, 0, remaining);
        digest.update(buffer, 0, remaining);
    }
    // mbuffer.clear();
}

/** hex char look up table */
private static final char[] HEX_CHARS = {
	'0','1','2','3','4','5','6','7','8','9',
	'A','B','C','D','E','F'
};

public static String bytes2Hex(byte[] bytes) {
	StringBuilder buffer = new StringBuilder();  
	for (int i = 0, len = bytes.length; i < len; i++) {
		byte b = bytes[i];
		buffer.append(HEX_CHARS[b>>4 & 0x0f]); // high
		buffer.append(HEX_CHARS[b & 0x0f]); // low
	}
	return buffer.toString();
}
  • 大小: 7.1 KB
分享到:
评论

相关推荐

    Java NIO入门

    IBM的NIO入门教程通常会涵盖这些基础概念,并通过实例演示如何使用NIO进行实际的I/O操作。教程可能还会深入讲解NIO的高级特性,如scatter/gather(分散/聚集)读写,以及如何使用`Selector`进行多路复用。通过学习这...

    NIO入门pdf分享

    《NIO入门》一书是理解Java NIO(New Input/Output)的重要参考资料,NIO在Java编程中扮演着至关重要的角色,特别是在处理高并发、大数据传输等场景下。本PDF文档将引领读者深入理解这一核心概念。 NIO,全称New ...

    nio入门 IBM教材,pdf格式

    ### NIO 入门知识点详解 #### 一、NIO 的引入及其背景 - **NIO 的起源**:NIO(New Input/Output)库是在 JDK 1.4 中引入的,旨在解决传统 Java I/O 操作的局限性和性能瓶颈。 - **改进目标**:NIO 通过提供高速...

    IBM Java文档库 NIO 入门

    《IBM Java文档库 NIO 入门》这篇教程主要针对的是Java 1.4引入的New Input/Output (NIO)库,这是一个重要的更新,旨在提高Java程序的I/O性能,特别是面向块的I/O操作。NIO弥补了传统I/O(基于java.io.*包)的不足,...

    NIO入门学习

    NIO 入门 JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的、面向块的 I/O。本实用教程从高级概念到底层的编程细节,非常详细地介绍了 NIO 库。您将学到诸如缓冲区和通道这样的关键 I/O 元素的...

    NIO 入门.chm,NIO 入门.chm

    **NIO(New Input/Output)是Java编程语言中用于替代标准I/O(BIO,Blocking I/O)的一组API,它提供了非阻塞式的I/O操作方式,极大地提升了Java在处理I/O密集型应用时的性能。NIO在Java 1.4版本中被引入,之后在...

    java nio 入门

    Java NIO(New Input/Output)是Java标准库中的一部分,自Java 1.4版本引入,为开发者提供了更高效的数据处理方式。相比于传统的IO模型,NIO具有非阻塞和选择器等特性,适用于高并发、大数据量的场景。在本文中,...

    javaNIO入门(良好排版格式).pdf

    javaNIO入门(良好排版格式).pdf

    Java NIO入门的源码

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从1.4版本开始引入的一种I/O模型,旨在提供一种更高效、更具控制力的I/O操作方式。与传统的-blocking I/O(阻塞I/O)相比,NIO的关键在于它...

    nio入门文档及示例代码

    本入门文档及示例代码旨在帮助开发者快速理解并掌握Java NIO的基本概念和用法。 一、NIO基础概念 1. **通道(Channels)**:NIO的核心组件之一,它是连接到数据源(如文件、套接字)的通道,可以读写数据。常见的...

    java nio入门学习,两个pdf

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java标准库提供的一种替代传统I/O模型的新技术。在Java 1.4版本中引入,NIO提供了一种全新的I/O编程方式,使得Java开发者能够更高效地处理I/O操作...

    NIO入门学习源码

    **NIO(New Input/Output)**,全称为New IO,是Java中的一种I/O模型,它是相对于传统的BIO(Blocking I/O)模型而言的。NIO在Java 1.4版本引入,主要目的是为了提高在高并发环境下的I/O性能。与BIO不同,NIO是非...

    NIO入门

    **NIO(Non-blocking I/O)入门** 在Java编程领域,NIO(Non-blocking Input/Output,非阻塞I/O)是一种重要的I/O模型,它与传统的BIO(Blocking I/O)模型相比,提供了更高的并发性能和更有效的资源利用。NIO在...

    java NIO入门(中英+代码)

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从1.4版本开始引入的一种I/O模型,旨在提供一种更高效、更具控制力的I/O操作方式。与传统的 Blocking I/O(同步阻塞I/O)相比,NIO的核心特点...

    java_NIO_入门

    本教程旨在为读者提供一个全面的NIO入门指南,不仅涵盖了NIO的基础理论知识,还包括了实际编程中使用到的代码示例。 在学习NIO之前,读者需要掌握Java语言的一些基本概念,包括类、继承、包等。此外,如果读者对...

    java-NIO-入门教程.docx

    "Java NIO 入门教程" Java NIO(New I/O)是 Java 语言中的一种新的输入/输出机制,自 JDK 1.4 开始引入。不同于传统的面向流的 I/O,NIO 采用面向块的 I/O 方式,提供了高速的 I/O 操作。NIO 库的主要特点是使用...

    java_NIO_入门(良好排版格式).pdf

    ### Java NIO 入门详解 #### 一、NIO 的背景与意义 **NIO (New Input/Output)** 是 Java 在 JDK 1.4 中引入的一个全新的输入输出库,旨在改进原有的 IO 库(主要位于 `java.io.*` 包中)的性能和功能。传统的 Java...

Global site tag (gtag.js) - Google Analytics