java进阶 ------ Java NIO

Java NIO

I/O简介

I/O或者输入输出指的是计算机与外部世界或者一个程序与计算机的其余部分之间的接口。它对于任何计算机系统都非常关键,因而所有I/O的主体实际上是内置在操作系统中的。单独的程序一般是让系统为它们完成大部分的工作。

在Java编程中,直到最近一直使用流的方式完成I/O。所有I/O都被视为单个的字节的移动,通过一个称为Stream的对象一次移动一个字节。流I/O用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。

NIO与原来的I/O有同样的作用和目的,但是它使用不同的方式:块I/O,其效率要比流I/O高许多。

为什么要用NIO?

NIO的创建目的是为了让java程序员可以实现高速I/O而无需编写自定义的本机代码。NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大的提高速度。

流与块的比较

原来的I/O库与NIO最重要的区别是数据打包和传输方式。正如前面提到的,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。

面向流的I/O

系统一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分。但不利的一面是,面向流I/O通常相当慢。

面向块的I/O

系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块,按块处理数据的方式要比按流式处理数据的方式要快的多。但相对来说,没有面向流I/O简单。

通道与缓冲区

通道与缓冲区是NIO中的核心对象,mttud每个I/O操作中都要使用它们。

通道是对原I/O包中流的模拟,到任何目的地或者来自任何地方的所有数据都必须通过一个Channel对象。 一个Buffer对象实质上是一个容器对象,发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。

缓冲区

缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组,其还提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。

缓冲区类型

  • ByteBuffer
  • CharBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

每一个Buffer类都是Buffer接口一个实例。除了ByteBuffer,每个Buffer类都有一个完全一样的操作,只是它们所处理的数据类型不一样,因为大多数标准I/O操作都要用到ByteBuffer,所以它具有所有共享的缓冲区操作以及一些特有的操作。

通道

channel是一个对象,可以通过它读取或写入数据。正如前面所说,所有数据都通过Buffer对象来处理。你永远不会将字节直接写入通道中,相反,你是将数据写入一个包含一个或多个字节的缓冲区中。同样,你不会直接从通道中读取字节,而是将数据读取到缓冲区中,再从缓冲区中读取这个字节。

通道类型

通道与流的不同之处就在于通道是双向的,而流只是在一个方向上进行移动,而通过可以用于读,写,或者同时读写。

NIO中的读写

读写是I/O的基本过程。从一个通道中读取很简单:只需要创建一个缓冲然后让通道将数据读取到这个缓冲区中。写入也很简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作。

从文件中读取

读取文件涉及三个步骤:

  • 1获得Channel
RandomAccessFile aFile = new RandomAccessFile("testNIO.in", "rw");
FileChannel inChannel = aFile.getChannel();
  • 2 创建Buffer
ByteBuffer buf = ByteBuffer.allocate(48);
  • 3 将数据从Channel读到到Buffer
inChannel.read(buf);

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

写入文件

在NIO中写入文件类似于从文件中读取,首先还是获得通道。

RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw");
FileChannel outChannel = toFile.getChannel();

接着创建缓冲区,并把数据写入缓冲区中。

String newData = "New String to write to File!";
ByteBuffer buf = ByteBuffer.allocate(50);
buf.clear();
buf.put(newData.getBytes());
buf.flip();

最后就是将缓冲区中的内容写入通道中。

toChannel.write(buf);

在这里同样,不需要告诉通道要写入多少数据。

读写结合

下面给出一个读写结合的例子,目的是将一个文件中的内容复制到另一个文件中,同样分为三个基本操作:

  • 1 创建一个Buffer
ByteBuffer buf = ByteBuffer.allocate(50);
  • 2 从源文件中将数据读取到这个缓冲区中
inChannel.read(buf);
  • 3 将缓冲区中的数据写入目标文件中
outChannel.write(buf);

注意,这个过程不断重复 读,写,读,写,直到源文件结束。

通道转移

上面的例子给出了普通的复制文件的思路,其实,如果是FileChannel的话,还可以利用transferFrom()和transferTo()方法来完成文件的复制。下面直接给出这两种方法的用法:

    /**
     * FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中
     */
    private void channelTransferFrom() {
        try {
            RandomAccessFile fromFile = new RandomAccessFile("testNIO1.in",
                    "rw");
            FileChannel inChannel = fromFile.getChannel();

            RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw");
            FileChannel outChannel = toFile.getChannel();

            long position = 0;
            long count = inChannel.size();
            outChannel.transferFrom(inChannel, position, count);
            fromFile.close();
            toFile.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * transferTo()方法将数据从FileChannel传输到其他的channel中
     */
    private void channelTransferTo() {
        try {
            RandomAccessFile fromFile = new RandomAccessFile("testNIO2.in",
                    "rw");
            FileChannel inChannel = fromFile.getChannel();

            RandomAccessFile toFile = new RandomAccessFile("testNIO2.out", "rw");
            FileChannel outChannel = toFile.getChannel();

            long position = 0;
            long count = inChannel.size();
            inChannel.transferTo(position, count, outChannel);

            fromFile.close();
            toFile.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

可以发现两个方法的用法极其相似。

这里只是给出了NIO的一些基础,需要深入了解的可以看:http://ifeve.com/overview/

本文所涉及到的全部代码:

package com.jesson.mianshi.nio;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIODemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        NIODemo nd = new NIODemo();
        // nd.fileChannelRead();
        // nd.fileChannelWrite();
        // nd.channelTransferFrom();
        // nd.channelTransferTo();
        nd.copyFile();
    }

    /**
     * NIO读
     */
    private void fileChannelRead() {
        try {
            RandomAccessFile aFile = new RandomAccessFile("testNIO.in", "rw");
            FileChannel inChannel = aFile.getChannel();
            ByteBuffer buf = ByteBuffer.allocate(48);

            int byteRead = inChannel.read(buf);
            while (byteRead != -1) {
                System.out.println("Read " + byteRead);
                buf.flip();

                while (buf.hasRemaining()) {
                    System.out.print((char) buf.get());
                }
                buf.clear();
                byteRead = inChannel.read(buf);
                System.out.println("\n" + byteRead);
            }
            aFile.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 向FileChannel写入数据
     */
    private void fileChannelWrite() {
        String newData = "New String to write to File!";
        RandomAccessFile toFile = null;
        try {
            toFile = new RandomAccessFile("testNIO.out", "rw");

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        FileChannel toChannel = toFile.getChannel();
        ByteBuffer buf = ByteBuffer.allocate(50);
        buf.clear();
        buf.put(newData.getBytes());
        buf.flip();
        while (buf.hasRemaining()) {
            try {
                toChannel.write(buf);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            toFile.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中
     */
    private void channelTransferFrom() {
        try {
            RandomAccessFile fromFile = new RandomAccessFile("testNIO1.in",
                    "rw");
            FileChannel inChannel = fromFile.getChannel();

            RandomAccessFile toFile = new RandomAccessFile("testNIO1.out", "rw");
            FileChannel outChannel = toFile.getChannel();

            long position = 0;
            long count = inChannel.size();
            outChannel.transferFrom(inChannel, position, count);
            fromFile.close();
            toFile.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * transferTo()方法将数据从FileChannel传输到其他的channel中
     */
    private void channelTransferTo() {
        try {
            RandomAccessFile fromFile = new RandomAccessFile("testNIO2.in",
                    "rw");
            FileChannel inChannel = fromFile.getChannel();

            RandomAccessFile toFile = new RandomAccessFile("testNIO2.out", "rw");
            FileChannel outChannel = toFile.getChannel();

            long position = 0;
            long count = inChannel.size();
            inChannel.transferTo(position, count, outChannel);

            fromFile.close();
            toFile.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 复制文件
     */
    private void copyFile() {

        try {
            ByteBuffer buf = ByteBuffer.allocate(50);
            RandomAccessFile fromFile = new RandomAccessFile("testNIO3.in",
                    "rw");
            FileChannel inChannel = fromFile.getChannel();

            RandomAccessFile toFile = new RandomAccessFile("testNIO3.out", "rw");
            FileChannel outChannel = toFile.getChannel();

            int byteRead = inChannel.read(buf);
            while (byteRead != -1) {
                buf.flip();
                outChannel.write(buf);
                buf.clear();
                byteRead = inChannel.read(buf);
            }
            fromFile.close();
            toFile.close();

        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 19:34:20

java进阶 ------ Java NIO的相关文章

(转)Java进阶java int与Integer的区别

Java进阶java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象,用一个引用指向这个对象. 1.Java 中的数据类型分为基本数据类型和复杂数据类型 int 是前者而Integer 是后者(也就是一个类):因此在类进行初始化时int类的变量初始为0.而Integer的变量则初始化为null. 2.初始化时: int i =1; Integer i= new Integer

[Java 进阶]Java中的国际化

背景知识 现代软件开发,往往做出的应用程序不止给一个国家的人去使用.不同国家的人往往存在语言文字不通的问题.由此产生了国际化(internationalization).多语言(multi-language).本地化(locale)这些词,它们其实都是一个意思,支持多种语言,提供给不同国家的用户使用. 语言编码.国家/地区编码 做web 开发的朋友可能多多少少接触过类似 zh-cn, en-us 这样的编码字样. 这些编码是用来表示指定的国家地区的语言类型的.那么,这些含有特殊含义的编码是如何产

java进阶--java网络编程

java网络编程 一.概述 网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节.你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节. java.net 包中提供了两种常见的网络协议的支持: TCP:TCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信.通常用于互联网协议,被称 TCP / IP. UDP:UDP 是用户数据报协议的缩写,一个无连接的协议.提供

Java进阶知识点5:服务端高并发的基石 - NIO与Reactor模式以及AIO与Proactor模式

一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等. 一般来说,系统架构的合理程度,决定了系统在整体性能上的伸缩性(高伸缩性,简而言之就是可以很任性,性能不行就加机器,加到性能足够为止):而单节点在性能上的优化程度,决定了单个请求的时延,以及要达到期望的性能,所需集群规模的大小.两者双管齐下,才能快速构建出性能良好的系统. 今天,我们就聊聊在单节点

Java 进阶(一) JVM运行时内存模型

1.JVM运行时数据区域的划分 a.程序计数器(Program Counter Register) 一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器.每个线程拥有独立的一个计数器,如果当前执行的是Native方法,则计数器值为空. b.JVM栈(Java Virtual Machine Stack) 描述Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stacks Frame)用于存储局部变量表,操作数栈,动态链接,方法出口等信息. 每一个方法从调用直至执行完成

转:java 进阶之路

转: https://www.zhihu.com/question/39139518 一.基础篇1.1 JVM1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http://www.jcp.org/en/jsr/detail?id=133 Java内存模型 1.1.2. 了解JVM各种参数及调优1.1.3. 学习使用Java工具 jps, jstack, jmap, jconsole, jinfo, jhat, javap, … BTrace — Project Ken

Java进阶书籍推荐

学习Java,书籍是必不可少的学习工具之一,尤其是对于自学者而言.废话不多说,下边就给广大程序猿们推荐一些Java进阶的好书. 第一部分:Java语言篇 1.<Java编程规范> 适合对象:初级.中级 介绍:这本书的作者是被誉为Java之父的James Gosling,入门者推荐阅读,对基础的讲解很不错. 2.<Java编程思想> 适合对象:初级.中级 介绍:豆瓣给出了9.1的评分,全球程序员广泛赞誉.有人说这本书不适合初学者,不过小编认为作者并没有对读者已有的知识经验有过多要求,

Java进阶(三十四)Integer与int的种种比较你知道多少?

Java进阶(三十四)Integer与int的种种比较你知道多少? 前言 如果面试官问Integer与int的区别:估计大多数人只会说到两点:Ingeter是int的包装类,注意是一个类:int的初值为0,Ingeter的初值为null.但是如果面试官再问一下Integer i = 1;int ii = 1; i==ii为true还是为false?估计就有一部分人答不出来了,如果再问一下其他的,估计更多的人会头脑一片混乱.所以我对它们进行了总结,希望对大家有帮助. 首先看代码: package

java进阶08 GUI图形界面

图形化用户界面(GUI) 简而言之,就是可视化编程. 要想实现可视化界面(窗口),需要用到JFrame类. package Frame; public class JFrame1 { public static void main(String[] args){ UI ui=new UI(); } } 先建一个主函数,而主函数中的操作只有一句代码.这样做,既能直观又方便后期修改. 接下来是UI类的实现 package Frame; import javax.swing.JFrame; publi