Java复习--IO(输入/输出){Java NIO}

http://my.oschina.net/u/2263278/blog/508770中介绍的BufferedReader时提到它的一个特征----当BufferedReader读取输入流中的数据时,如果没有读到有效数据,程序将在此处阻塞该线程的执行(使用InputStream的read()方法从流中读取数据时,如果数据源中没有数据,它也会阻塞该线程),也就是前面介绍的输入流、输出流都是阻塞式的输入、输出。传统的输入流、输出流都是通过字节的移动来处理的,即使我们不直接去处理字节流,但底层的实现还是依赖于字节处理,面向流的输入/输出系统一次只能处理一个字节,因此面向流的输入/输出系统通常效率不高。从JDK1.4开始,Java提供了一些新IO,这些类都放在java.nio包及其子包下。

一、Java新IO概述

Java的NIO采用了内存映射文件的方式来处理输入/输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了(这种方式模拟了操作系统上的虚拟内存的概念),通过这种方式进行输入/输出比传统的输入/输出要快的多。

Channel和Buffer是NIO中两个核心对象,Channel是对传统的输入/输出系统的模拟,在NIO系统中所有的数据都需要通过通道Channel传输。Channle和传统的InputStream、OutputStream最大的区别在于提供了一个map()方法,通过该map()方法可以直接将"一块数据"映射到内存中。如果说传统的输入/输出系统式面向流的处理,则新IO则是面向快的处理。Buffer可以被理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象必须首先爱你放到Buffer中,而从Channel中读取的数据也必须先放在Buffer中。

二、Buffer的使用

1、从内部结构来看,Buffer就像一个数组,他可以保存多个类型相同的数据。BUffer是一个抽象类,其子类:ByteBuffer、CharBuffer、IntBuffer、LongBuffer等。这些Buffer类没有提供构造器,而是通过  allocat(int capacity)方法返回一个对象。ByteBuffer还有一个子类:MappedByteBuffer,它用于表示Channel将磁盘文件的部分或全部内容映射到内存后得到的结果,通常MappredByteBuffer是Channel的map()方法返回的。

2、Buffer中的3个概念:

①容量 capacity:缓冲区的容量表示该Buffer的最大数据容量,即最多可以存储多少数据。缓冲区的容量不能为负值。

②界限limit:第一个不应该被读出或写入的缓冲区位置索引。位于limit后面的数据既不可读也不可被写。

③位置position:记录指针。当使用BUffer从Channel中读取数据时,其position为0,如果从Channel中读取了2个数据到该Buffer中,则position为2,指向Buffer中第三个位置。(第一个位置的索引为0)

④mark:可选的标记

0<=mark<=position<=limit<=capacity

3、Bufferde 的主要作用就是装入数据,然后输出数据,开始的时候Buffer的position为0,Limit为capacity,程序可通过put()方法向Buffer中放入一些数据,每放入一些数据,Buffer的position相应的向后移动一些位置。当Buffer装入数据结束后,调用Buffer的flip()方法,该方法将Limit设置为position所在位置,并将position设为0,这就使得Buffer的读写指针又移到了开始位置。Buffer调用filp()方法之后,BUffer为输出数据做好了准备。当Buffer输出数据结束后,BUffer调用clear()方法,clear()方法不是清空BUffer数据,它仅仅将Position设为0,将limit设为capactiy,这样为再次向Buffer中装入数据做好准备。

Buffer中的两个中重要方法:flip()和clear(),flip()方法为从BUffer中取出数据做好准备,clear()方法为再次向Buffer中装入数据做好准备。还有put()和get()方法,用于向Buffer中放入数据和从Buffer中取出数据。

当使用put()和get()莱访问Buffer中的数据时,分为相对和绝对两种:

①、相对:从Buffer的当前position出开始读取或写入数据,然后将position的值按处理元素的个数增加。

②、绝对:直接根据索引向Buffer中读取或写入数据,使用绝对方式访问Buffer里的数据时,并不会影响位置的值。

 public static void main(String[] args) {
  CharBuffer buff=CharBuffer.allocate(8);
  System.out.println(buff.capacity()+":"+buff.limit()+":"+buff.position());
  buff.put(‘a‘);
  buff.put(‘a‘);
  buff.put(‘a‘);
  System.out.println(buff.position());
  buff.flip();
  System.out.println(buff.limit());
  
  
 }

通过allocate()方法创建的Buffer对象是普通的Buffer,ByteBuffer还提供了一个allocateDirect()方法来创建直接Buffer。直接Buffer的创建成本比普通的Buffer创建成本高,但是直接buffer的读取效率很高。

三、Channel的使用

Channel类与传统的流对象的区别:

①、Channel可以直接将指定的 文件的部分或全部映射成buffer。

②、程序不能直接访问Channel中的数据,包括读取、写入都不行,Channel只能与Buffer进行交互,也就说,如果从Channel中取得数据,必须先用Buffer从Channel中取出一些数据,然后让程序从Buffer中取出这些数据。将数据写入Channle一样,需要先写入Buffer中。Java为Channle接口提供了DatagramChannel、FileChannel、Pipe.SinkChannel、Pepe.SourceChannel、SelectableChannel、ServerSoketChannel等。

所有的Channel都不应该通过构造器来创建,而是通过传统的节点InputStream,OutputStream的getChannel()方法来返回对应的Channle。Channle中最常用的3类方法:map()、read()、write(),其中map()方法用于将Channel对应的部分或全部数据映射成ByteBuffer,而read()和write()方法用于从Buffer中直接读取数据或写入数据。

public static void main(String[] args) {
  try{
   File f=new File("wang.txt");
   FileInputStream fileIn=new FileInputStream(new File("wang.txt"));
   FileChannel inChannel=fileIn.getChannel();
   FileChannel outChannel=new FileOutputStream(new File("test.txt")).getChannel();
   MappedByteBuffer buffer=inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
   outChannel.write(buffer);
   buffer.clear();
  }catch(Exception e){
   e.printStackTrace();
  }
 }

在RandomAccessFile类中也包含了一个getChannel()方法

public static void main(String[] args) {
  try{
   File file=new File("test.txt");
   FileChannel channel=new RandomAccessFile(file, "rw").getChannel();
   MappedByteBuffer buff=channel.map(FileChannel.MapMode.READ_WRITE, 0, file.length());
   channel.position(file.length());
   channel.write(buff);
  }catch(Exception e){
   e.printStackTrace();
  }
 }

四、字符集合Channel

编码Encode:把明文的字符序列转换成计算机理解的二进制序列称为编码。

解码Decode:把二进制序列转换为普通人能看懂的明文字符串称为解码。

Java默认使用Unicode字符集,JDK1.4提供了Charset来处理字节序列与字符序列之间的转换关系,该类包含了用于创建解码器和编码器的方法,还提供了Charset所支持字符集的方法,Charset类是不可变的。

CharsetDecoder和CharsetEncoder对象代表Charset的编码器和解码器,调用CharsetDecoder的decode()方法将Bytebuffer字节序列转换成Charbuff字符序列,调用CharsetEncoder的encode()方法就可以将CharBuffer或String字符序列转换成ByteBuffer字节序列。

public static void main(String[] args) throws CharacterCodingException {
  Charset cn=Charset.forName("gbk");
  CharsetEncoder encoder=cn.newEncoder();
  CharsetDecoder decoder=cn.newDecoder();
  CharBuffer cbuff=CharBuffer.allocate(8);
  cbuff.put(‘王‘);
  cbuff.put(‘宁‘);
  cbuff.flip();
  ByteBuffer bbuff=encoder.encode(cbuff);
  for(int i=0;i<bbuff.capacity();i++){
   System.out.println(bbuff.get(i));
  }
  System.out.println(decoder.decode(bbuff));
 }

五、文件锁

文件锁在操作系统中是很平常的事情,如果多个运行程序需要并发修改同一个文件时,程序之间需要某种机制来进行通信,使用文件锁可以有效地阻止多个进程并发修改同一个文件,所以现在的大部分操作系统都提供了文件锁的功能。

Java提供了FileLock类支持文件锁功能,在FileChannel中提供的lock()/tryLock()方法可以获得文件锁FileLock对象,从而锁定文件。lock()方法和trylock()方法的区别是:当lock()试图锁定某个文件时,如果无法得到文件锁,程序将一直阻塞,而tryLock()方法时尝试锁定文件,它将直接返回而不是阻塞,如果获得了文件锁,该方法则返回该文件锁,否则将返回Null。

文件锁虽然可以用于控制并发访问,但对于高并发访问的情形,还是推荐使用数据库来保存程序信息,而不是使用文件。

六、NIO.2

Java7对原有的NIO进行了重大改变,称为NIO.2。提供了全面的文件IO和文件系统的访问支持。提供了基于异步Channel的IO。

1、Path、Paths和FIles核心API

Java提供的File类功能比较有限,他不能利用特定文件系统的特性,File所提供的方法的性能也不高,而且,其大多数方法在出错时仅仅返回失败,并不提供异常信息。NIO.2为了弥补这种不足,引入了Path接口,还提供了Files\Paths工具类。

2、FileVisitor遍历文件和目录

3、WatchService监控文件变化

时间: 2024-10-12 14:42:57

Java复习--IO(输入/输出){Java NIO}的相关文章

JAVA之IO技术-将java程序的异常信息保存在文件中

package ioTest.io2; import java.io.FileNotFoundException; import java.io.PrintStream; import java.util.Properties; /* * 将应用程序的异常信息输出到指定的log文件中 */ public class ExceptionToFile { public static void main(String[] args) throws FileNotFoundException { int

Java——IO输入/输出

在学习输入/输出流的前提下先了解一下File类. 使用 File 类操作文件 import java.io.File; import java.io.IOException; public class FIleTest { public static void main(String[] args) { //创建File 类对象 File file = new File("files/file"); //创建文件夹,并且只能创建一级文件夹 //System.out.println(fi

IO输入输出流——Java面向对象基础(29)

一.概念 IO(输入输出流) InputStream 输入流 OutputStream 输出流   输入输出流广义上就是物理存储介质.设备.容器数据的传输过程.   Java中提供一个强大的api操作IO,java.io包 二.常见的File类 File常常用于操作系统文件或目录,其中常用的方法务必掌握. File file = new File("填写文件|文件目录的路径"); createNewFile()  创建文件 delete()   删除文件或目录 exists()  判断

Java 对象流(输入-输出)objectoutputstream序列化报错

报错:java.io.notserializableexception 解决方法:添加下面代码实现 对象类 1 package com.etc._07ObjectDemo; 2 3 import java.io.Serializable; 4 5 public class Person implements Serializable { 6 private int id; 7 private String name; 8 private String sex; 9 public int getI

Java的IO输入输出流类的介绍(有图)

一.字节流 1.InputStream/OutputStream(输入流与输出流几乎一一对应) 读取的方法   int read()   int read(byte[] buffer)   int read(byte[] buffer,int offset,int length) 2.各种类的区分,常用的9种(按处理的基本单位划分) 2.1.以字节数组为颗粒(颗粒就是基本单位的意思) ByteArrayInputStream 2.2.以文件为颗粒 FileInputStream 2.3.管道流(

java 的输入/输出

java 的输入/输出java的 I/O是通过java.io包下的类和接口支持,其中最重要的是5个类,分别是 File,OutputStream,InputStream, Write,Reader及一个接口Serializable.File类:可以对文件进行操作字节流:(OutputStream/InputStream),可以使用字节流读取文件内容字符流:(Writer/Reader)转换流:(InputStreamReader/OutputStreamWriter)将字节流转换为字符流缓冲流:

Thinking in java Chapter18 IO

1 File类 1.1 目录列表器 1.2 目录实用工具 1.3 目录的检查及创建 2 输入和输出 3 添加属性和有用的接口 4 Reader和Writer 5 自我独立的类RandomAccessFile 6 I/O流的典型使用方式 6.1 缓冲输入文件 6.2 从内存输入 6.3 格式化的内存输入 6.4 基本的文件输出 6.5 存储和恢复数据 6.6 读写随机访问文件 7 文件读写的使用工具 8 标准I/O 9 进程控制 10 新I/O复习时候再review补 11 压缩 11.1 用GZ

Java的IO体系

现在返回来看之前一直觉得迷迷糊糊的IO这一部分,顿时真的觉得阔然开朗.所以现在觉得自学的顺序应该是这样的,先过一遍基本的知识体系,然后通过实践.练习和项目来巩固,最后再回头复习这些知识点,真的会有不一样的体会.下面稍稍对自己了解到Java的IO体系做一个小小总结. 1.Java流式输入/输出的原理: 在Java程序中,对于数据的输入/输出操作都是以流(stream)的方式进行的,JDK提供了各种各样的流,可以获取不同种类的数据,程序可以通过标准的方法输入或输出数据. 2.Java流的分类: ja

Java之IO流总结

IO流·Java流式输入/输出原理·Java流类的分类·输入/输出流类·常见的节点流和处理流·文件流·缓冲流·转换流·数据流·Print流·Object流 ①Java流式输入/输出原理            00101...-->    文件 ------------------ 程序 <-- ...00101    文件 ------------------ 程序               00101...-->    网络连接 ------------------ 程序 00101