今天正好要写个小程序需要用到io操作,突然想起来java有个nio一直没用过,就找了点资料研究了一下,顺便做点笔记,以便日后查阅。
-------------------------------------------------------------------------------------------------------------------------------------------
编程语言的I/O类库中常使用流的概念,但它屏蔽了实际的I/O设备中处理数据的细节。在Java中很少使用单一的类来创建流对象,而是通过叠合多个对象来提供所期望的功能(用到了装饰器模式)。InputStream和OutputStream就是用来输入和输出的。在Java1.1中,Reader和Writer利用适配器转换了它们来实现国际化,因为老的I/O流继承结果仅支持8位字节流,不能很好的处理16位的Unicode字符。
文件操作的类:FileInputReader,FileOutputReader,RandomAccessFile。其中RandomAccessFile是一个自我独立的类,虽然可读可写,但都是自己内部实现,并没继承输入输出流的层次结构。在JDK1.4中,其大部分功能已经由nio内存映射文件所取代。
基本的文件输出:FileWriter对象可以向文件写入数据,实际上,我们通常会用BufferedWriter将其包装起来用以缓冲输出,为了提供格式化机制,它又被装饰成PrintWriter。例如:new PrintWriter(new BufferedWriter(new FileWriter(file))),在J2SE 5中甲了一个辅助构造器,可以简写成new PrintWriter(file)。
管道流:PipedInputStream,PipedOutputStream,PipedReader及PipedWriter,它们的价值在多线程中才能体现,因为管道流用于任务之间的通信。
-------------------------------------------------------------------------------------------------------------------------------------------
new I/O:目的是为了提高速度,在新版本的JDK中,旧的I/O也使用nio重新实现,有性能提升。速度的提高来自于所使用的结构更接近于操作系统的执行I/O方式——通道和缓冲器。
流使用getChannel()方法会产生一个FileChannel。通道是一种相当基础的东西:可以向它传送用于读写的ByteBuffer,并且可以锁定文件的某些区域用于独占式访问(后面有提到)。
- import java.nio.*;
- import java.nio.channels.*;
- import java.io.*;
- public class GetChannel {
- private static final int BSIZE = 1024;
- public static void main(String[] args) throws Exception {
- // Write a file:
- FileChannel fc =
- new FileOutputStream("data.txt").getChannel();
- fc.write(ByteBuffer.wrap("Some text ".getBytes()));
- fc.close();
- // Add to the end of the file:
- fc = new RandomAccessFile("data.txt", "rw").getChannel();
- fc.position(fc.size()); // Move to the end
- fc.write(ByteBuffer.wrap("Some more".getBytes()));
- fc.close();
- // Read the file:
- fc = new FileInputStream("data.txt").getChannel();
- ByteBuffer buff = ByteBuffer.allocate(BSIZE);
- fc.read(buff);
- buff.flip();
- while(buff.hasRemaining())
- System.out.print((char)buff.get());
- }
- }
将字节存放于ByteBuffer有2种方法:一是用put方法直接填充一个或多个字节,或基本数据类型;另一种是用warp方法将已存在的字节数组包装到ByteBuffer中。对于只读访问,我们必须显示的使用静态的allocate方法分配ByteBuffer。nio的目标就是快速移动大量数据,因此ByteBuffer的大小就很重要——需要在实际运行的程序中测试来找到最佳值。要达到更高的速度也有可能,方法就是使用allocateDirect()而不是allocate(),以产生一个与操作系统有更高耦合性的“直接”缓冲器,但效果需要实际测试一下。
- import java.nio.*;
- import java.nio.channels.*;
- import java.io.*;
- public class ChannelCopy {
- private static final int BSIZE = 1024;
- public static void main(String[] args) throws Exception {
- if(args.length != 2) {
- System.out.println("arguments: sourcefile destfile");
- System.exit(1);
- }
- FileChannel in = new FileInputStream(args[0]).getChannel();
- FileChannel out = new FileOutputStream(args[1]).getChannel();
- //有一种特殊的办法,将2个通道直接相连
- // in = transferTo(0, in.size(), out); 或者 out = transferFrom(in, 0, in.size());
- ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
- while(in.read(buffer) != -1) {
- buffer.flip(); // Prepare for writing
- out.write(buffer);
- buffer.clear(); // Prepare for reading
- }
- }
- }
BufferWriter转换成char型来操作,很不方便,我们利用CharBuffer的toString()方法来转换成String型就方便多了。
- import java.nio.*;
- import java.nio.channels.*;
- import java.nio.charset.*;
- import java.io.*;
- public class BufferToText {
- private static final int BSIZE = 1024;
- public static void main(String[] args) throws Exception {
- FileChannel fc =
- new FileOutputStream("data2.txt").getChannel();
- fc.write(ByteBuffer.wrap("Some text".getBytes()));
- fc.close();
- fc = new FileInputStream("data2.txt").getChannel();
- ByteBuffer buff = ByteBuffer.allocate(BSIZE);
- fc.read(buff);
- buff.flip();
- // Doesn't work:
- System.out.println(buff.asCharBuffer());
- // Decode using this system's default Charset:
- buff.rewind();
- String encoding = System.getProperty("file.encoding");
- System.out.println("Decoded using " + encoding + ": "
- + Charset.forName(encoding).decode(buff));
- // Or, we could encode with something that will print:
- fc = new FileOutputStream("data2.txt").getChannel();
- fc.write(ByteBuffer.wrap(
- "Some text".getBytes("UTF-16BE")));
- fc.close();
- // Now try reading again:
- fc = new FileInputStream("data2.txt").getChannel();
- buff.clear();
- fc.read(buff);
- buff.flip();
- System.out.println(buff.asCharBuffer());
- // Use a CharBuffer to write through:
- fc = new FileOutputStream("data2.txt").getChannel();
- buff = ByteBuffer.allocate(24); // More than needed
- buff.asCharBuffer().put("Some text");
- fc.write(buff);
- fc.close();
- // Read and display:
- fc = new FileInputStream("data2.txt").getChannel();
- buff.clear();
- fc.read(buff);
- buff.flip();
- System.out.println(buff.asCharBuffer());
- }
- }
-------------------------------------------------------------------------------------------------------------------------------------------
内存映射文件:它允许我们创建和修改那些因为太大而不能放入内存的文件。
- import java.nio.*;
- import java.nio.channels.*;
- import java.io.*;
- import static net.mindview.util.Print.*;
- public class LargeMappedFiles {
- static int length = 0x8FFFFFF; // 128 MB
- public static void main(String[] args) throws Exception {
- MappedByteBuffer out =
- new RandomAccessFile("test.dat", "rw").getChannel()
- .map(FileChannel.MapMode.READ_WRITE, 0, length);
- for(int i = 0; i < length; i++)
- out.put((byte)'x');
- print("Finished writing");
- for(int i = length/2; i < length/2 + 6; i++)
- printnb((char)out.get(i));
- }
- }
文件加锁:通过对FileChannel调用tryLock()和lock(),就可以获得整个文件的FileLock。启动tryLock()是非阻塞式的,它设法获取锁,但如果不能获得,将直接放方法调用返回。lock()是阻塞式的,它要阻塞线程直到获得锁,或者调用lock()的线程中断,或者调用lock()的通道关闭。
- import java.nio.channels.*;
- import java.util.concurrent.*;
- import java.io.*;
- public class FileLocking {
- public static void main(String[] args) throws Exception {
- FileOutputStream fos= new FileOutputStream("file.txt");
- FileLock fl = fos.getChannel().tryLock();
- if(fl != null) {
- System.out.println("Locked File");
- TimeUnit.MILLISECONDS.sleep(100);
- fl.release();
- System.out.println("Released Lock");
- }
- fos.close();
- }
- }
对映射文件的部分加锁:
- import java.nio.*;
- import java.nio.channels.*;
- import java.io.*;
- public class LockingMappedFiles {
- static final int LENGTH = 0x8FFFFFF; // 128 MB
- static FileChannel fc;
- public static void main(String[] args) throws Exception {
- fc =
- new RandomAccessFile("test.dat", "rw").getChannel();
- MappedByteBuffer out =
- fc.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
- for(int i = 0; i < LENGTH; i++)
- out.put((byte)'x');
- new LockAndModify(out, 0, 0 + LENGTH/3);
- new LockAndModify(out, LENGTH/2, LENGTH/2 + LENGTH/4);
- }
- private static class LockAndModify extends Thread {
- private ByteBuffer buff;
- private int start, end;
- LockAndModify(ByteBuffer mbb, int start, int end) {
- this.start = start;
- this.end = end;
- mbb.limit(end);
- mbb.position(start);
- buff = mbb.slice();
- start();
- }
- public void run() {
- try {
- // Exclusive lock with no overlap:
- FileLock fl = fc.lock(start, end, false);
- System.out.println("Locked: "+ start +" to "+ end);
- // Perform modification:
- while(buff.position() < buff.limit() - 1)
- buff.put((byte)(buff.get() + 1));
- fl.release();
- System.out.println("Released: "+start+" to "+ end);
- } catch(IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
本文转自passover 51CTO博客,原文链接:http://blog.51cto.com/passover/425926,如需转载请自行联系原作者