【IO/NIO】Java IO/NIO
来源:互联网 发布:数控车床编程自学视频 编辑:程序博客网 时间:2024/05/16 15:15
在Java程序中,对于数据的输入/输出以”流”(Stream)方式进行
java.io 包定义了多个流类型:
1) 按数据流方向分为 – 输入流和输出流
2) 按数据单位分为 – 字节流和字符流
3) 按功能分为 – 节点流和处理流
所有流类型均位于java.io包内,分别继承以下四种抽象流类型:
字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
注:输入输出流,都是站在【程序】的角度上来说,从文件读数据这叫输入流,往文件写数据叫输出流
一、字节 I/O 操作(InputStream和OutputStream)相关类图
1) InputStream是一个抽象类,核心方法是read()、read(byte b[])、read(byte b[], int off, int len),从不同的数据源读取数据。
这些数据源有:
① 字节数组。
② String对象。
③ 文件。
④ “管道”,一端输入,另一端输出。
⑤ 一个由其他种类的流组成的序列。
⑥ 其他数据源,如Internet等。
每一种数据源都对应相应的InputStream子类:
ByteArrayInputStream:缓冲区,处理字节数组,允许将内存的缓冲区当作InuputStream。
StringBufferInputStream:将String转换成 InputStream,底层使用StringBuffer实现。
FileInputStream:从文件中读取信息。
PipedInputStream:从管道读数据,最为多线程中的数据源。
SequenceInputStream:将多个流对象转换成一个InputStream。
FilterInputStream:“装饰器”类的基类(这里是装饰器模式),为其它InputStream类提供功能:
DataInputStream:用于读取基本类型数据。
BufferedInputStream:使用缓冲区,防止每次读取时都得进行实际写操作。
LineNumberInputStream:跟踪输入流中的行号,getLineNumber()、setLineNumber(int)。
PushbackInputStream:具有能弹出一个字节的缓冲区,可以将读到的最后一个字符回退。
2) OutputStream是一个抽象类,核心方法是write(int b)、write(byte b[])、write(byte b[], int off, int len),决定输出的目标:字节数组、文件或管道。
ByteArrayOutputStream:将数据写入一个byte数组缓冲区。
FileOutputStream:将数据写入文件。
PipedOutputStream:指定多线程数据的目的地,向与其它线程共用的管道中写入数据,用于多线程之间任务的通信。
FilterOutputStream:“装饰器”类,提供特定的输出流:
DataOutputStream:与DataInputStream搭配使用,用于写入基本类型数据。
BufferedOutputStream:使用缓冲区,避免每次发送数据时都进行实际的写操作,可使用 flush() 清空缓冲区。
PrintStream:产生格式化输出。
二、字符 I/O 操作(Reader和Writer)相关类图
三、基于磁盘的I/O操作(File类)
File类既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。
【1】文件、目录的创建和删除
public class FileOperation { public static void main(String[] args) { // 使用 File.separator 系统默认名称分隔符,Windows和Linux文件路径分割符不同 String fileName = "C:" + File.separator + "hello.txt"; File file = new File(fileName); // 创建一个新文件 try { // 指定的文件不存在且成功创建返回 true;文件已存在返回 false file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } // 删除文件 if(file.exists()){ // 成功删除文件或目录时返回 true file.delete(); } // 创建一个文件夹 String dirName = "C:" + File.separator + "hello"; File dirFile = new File(dirName); dirFile.mkdir(); dirFile.isDirectory(); // 判断是否为目录 }}
【2】查看目录列表
public class FileOperation { public static void main(String[] args) { File path = new File("C:\\"); String[] list; // 查看目录所有内容 list = path.list(); // 查看目录指定内容(以 .txt 结尾的) final String regStr = ".+.txt"; // 使用一个匿名的目录过滤器内部类,通过正则表达式验证过滤,list()可以回调accept()方法 list = path.list(new FilenameFilter() { private Pattern pattern = Pattern.compile(regStr); @Override public boolean accept(File dir, String name) { return pattern.matcher(name).matches(); } }); for(String dirItem : list){ System.out.println(dirItem); } }}
四、I/O流典型使用方式
尽管可以通过不同的方式组合 I/O 流类,但我们可能也就只用到其中的几种组合。
【1】缓冲输入文件(读取)
public class BufferedInputFile { public static String read(String fileName) throws Exception{ BufferedReader br = new BufferedReader(new FileReader(fileName)); String str; StringBuffer sb = new StringBuffer(); while((str = br.readLine()) != null){ sb.append(str + "\n"); } br.close(); return sb.toString(); } public static void main(String[] args) throws Exception { System.out.println(read("src/com/test/BufferedInputFile.java")); }}
【2】从内存输入(读取)
public class MemoryInput { public static void main(String[] args) throws Exception { StringReader in = new StringReader(BufferedInputFile.read("src/com/test/MemoryInput.java")); int c; while((c = in.read()) != -1){ System.out.println((char)c); } }}
【3】基本文件输出
/** * FileWriter向文件写入数据,使用BufferWriter将其封装缓冲输出,为了格式化装饰成 PrintWriter * */public class BasicFileOutput { static String file = "BasicFileOutput.out"; public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new StringReader(BufferedInputFile.read("src/com/test/BasicFileOutput.java"))); // PrintWriter有个构造函数,内部实现了BufferedWriter缓冲 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file))); //可以简写为 PrintWriter out = new PrintWriter(file); int lineCount = 1; String s; while((s = br.readLine()) != null){ out.println(lineCount++ + ": " + s); } out.close(); System.out.println(BufferedInputFile.read(file)); }}
【4】存储和恢复数据
/** * 存储和恢复数据,使用 DataOutputStream写入数据,并用 DataInputStream 恢复数据 * 这些流可以是任何形式,以文件为例,对读写都进行缓冲处理 */public class StoringAndRecoveringData { public static void main(String[] args) throws Exception { DataOutputStream out = new DataOutputStream(new BufferedOutputStream( new FileOutputStream("C:" + File.separator + "Data.txt"))); out.writeDouble(3.14159265); out.writeUTF("That was PI"); out.writeDouble(1.41413); out.writeUTF("Square root of 2"); out.close(); DataInputStream in = new DataInputStream(new BufferedInputStream( new FileInputStream("C:" + File.separator + "Data.txt"))); System.out.println(in.readDouble()); System.out.println(in.readUTF()); System.out.println(in.readDouble()); System.out.println(in.readUTF()); in.close(); }}
【5】标准 I/O
Java提供了System.in、System.out和System.err,System.in是一个没有被包装过未经加工的 InputStream
public class Echo { public static void main(String[] args) throws IOException { // 将System.in包装成BufferedReader,使用readLine()一次读取一行 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str; while((str = br.readLine()) != null && str.length() != 0){ System.out.println(str); } // 将System.out转换成PrintWriter PrintWriter out = new PrintWriter(System.out, true); // 第二个参数 true 开启自动清空功能 out.println("Hello, world"); // 标准 I/O 重定向 操作的是字节流 // System类提供了一些简单的静态方法,允许对标准输入、输出和错误I/O流进行重定向 // System.setIn(InputStream) System.setOut(PrintStream) System.setErr(PrintStream) PrintStream console = System.out; // System.out对象的引用 BufferedInputStream in = new BufferedInputStream(new FileInputStream("src/com/test/Echo.java")); PrintStream _out = new PrintStream(new BufferedOutputStream(new FileOutputStream("test.out"))); System.setIn(in); System.setOut(_out); System.setErr(_out); str = null; while((str = br.readLine()) != null && str.length() != 0){ System.out.println(str); // 标准输出将重定向到另一个文件 } _out.close(); System.setOut(console); // 恢复了System.out对象的引用 }}
五、Java NIO
JDK1.4 的java.nio.* 包中引入了新的java I/O类库,目的在于提高速度。与传统的 IO 相比主要有以下区别:
① Java IO是面向流的,Java NIO是面向缓冲区(块)的
② Java IO各种流是阻塞的,当一线程调用read()或write()时,该线程被阻塞,直到有数据被读取或写入;Java NIO是非阻塞的,一线程从某通道请求读取数据,若目前没有数据可用,则不保持线程阻塞,直到有数据可供读取,该线程可继续做其他的事情,在写入的时候也是,不等待它完全写入,线程也可以同时去做其他的事情
Java NIO 由以下3个核心部分组成:Channel(通道)、Buffer(缓冲区)、Selector(选择器)。
(1)Channel(通道):Java NIO的通道类似流,但又有些不同:
① 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
② 通道可以异步地读写。
③ 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入
在Java NIO中最重要的通道的实现:
① FileChannel:从文件中读写数据
② DatagramChannel:能通过UDP读写网络中的数据
③ SocketChannel:能通过TCP读写网络中的数据
④ ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel
(2)Buffer(缓冲区):用于和NIO通道进行交互,数据是从通道读入缓冲区,从缓冲区写入到通道中的
使用Buffer读写数据一般遵循以下四个步骤:
① 写入数据到Buffer
② 调用flip()方法
③ 从Buffer中读取数据
④ 调用clear()方法或者compact()方法
向Buffer写入数据,当要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式,读取之前写入到buffer的所有数据。一旦数据读取完毕,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据,任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
(2.1)Buffer的capacity、position和limit
① capacity:不管Buffer处于读模式还是写模式,其返回缓冲区的容量
② position:初始值为0,最大值为capacity-1 。写数据到Buffer时,表示当前位置,没插入一条数据,向前移动一个单元;读取数据时,从某个位置读取数据,当Buffer切换为读模式时,position会被重置为0,读取一条数据时,会移动到下一个可读的位置。
③ limit:在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
Buffer的分配,要想获得一个Buffer对象首先要进行分配,如分配48字节capacity的ByteBuffer:ByteBuffer buf = ByteBuffer.allocate(48),分配一个可存储1024个字符的CharBuffer:CharBuffer buf = CharBuffer.allocate(1024);
向Buffer写数据,两种方式:
① 从Channel写到Buffer
int bytesRead = inChannel.read(buf); //read into buffer
② 通过Buffer的put()方法写到Buffer里
buf.put(127);
从Buffer中读数据,两种方式:
① 从Buffer读取数据到Channel
int bytesWritten = inChannel.write(buf);
② 使用get()方法从Buffer中读取数据
byte aByte = buf.get();
rewind()方法
Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)
mark()与reset()方法
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。
buffer.mark();//call buffer.get() a couple of times, e.g. during parsing. buffer.reset(); //set position back to mark.
【1】FileChannel
用于读取、写入、映射和操作文件的通道,在现版本中,可从现有的 FileInputStream、FileOutputStream 或 RandomAccessFile 对象获得文件通道,方法是调用该对象的 getChannel 方法,这会返回一个连接到相同底层文件的文件通道。
public class GetFileChannel { public static void main(String[] args) throws Exception { FileChannel fileChannel = new FileOutputStream("data.txt").getChannel(); // 将 byte 数组包装到缓冲区中,并将数据写入通道(文件) fileChannel.write(ByteBuffer.wrap("FileOutputStream--FileChannel Test".getBytes())); fileChannel.close(); // 关闭此通道 // 向文件末尾增加数据 rw读写 r只读 fileChannel = new RandomAccessFile("data.txt", "rw").getChannel(); // 调用position()方法获取FileChannel的当前位置,调用position(long pos)方法设置FileChannel的当前位置 // FileChannel实例的size()方法将返回该实例所关联文件的大小 fileChannel.position(fileChannel.size()); // 设置此通道的文件位置,末尾 fileChannel.write(ByteBuffer.wrap("RandomAccessFile--FileChannel Test".getBytes())); fileChannel.close(); // FileChannel.force(boolean metaData)方法将通道里尚未写入磁盘的数据强制写到磁盘上 // true 表示是否同时将文件元数据(权限信息等)写到磁盘上 // 读取文件 fileChannel = new FileInputStream("data.txt").getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配一个缓冲对象 fileChannel.read(buffer); // 将字节序列从此通道读入给定的缓冲区,返回读取的字节数 buffer.flip(); // 切换到读模式,让别人做好读取字节的准备 while(buffer.hasRemaining()){ System.out.print((char)buffer.get()); } }}
上述代码中,在读取通道数据并输出时,使用(char)buffer.get()每次读取一个字节,然后强制转换为char类型(这种方法显然比较原始),如果不强制转换,将会是乱码,这是因为ByteBuffer 存储的是普通的字节,为了把它们转换成字符,有两种方式可以解决:① 在输入它们的时候对其进行编码;② 从缓冲器输出时对它们进行解码。
从api文档中可以看到CharBuffer有一个toString()方法可以返回缓冲器包含的所有的字符,而ByteBuffer可以调用asCharBuffer()方法作为 char 类型缓冲区
public static void main(String[] args) throws Exception { final int BSIZE = 1024; FileChannel fileChannel = new RandomAccessFile("data.txt", "rw").getChannel(); fileChannel.write(ByteBuffer.wrap("some text".getBytes())); fileChannel.close(); ByteBuffer buffer = ByteBuffer.allocate(BSIZE); fileChannel = new FileInputStream("data.txt").getChannel(); fileChannel.read(buffer); buffer.flip(); // 在这里直接调用asCharBuffer()显示不行输出乱码 System.out.println(buffer.asCharBuffer()); buffer.rewind(); // 将position设回0,可以重读Buffer中的所有数据 /** 1) 在输出之前进行解码 Decoding**/ // 获取系统编码,使用java.nio.charset.Charset进行解码 // decode(ByteBuffer bb) 返回一个CharBuffer对象 String encoding = System.getProperty("file.encoding"); System.out.println("使用 " + encoding +" 解码输出:" + Charset.forName(encoding).decode(buffer)); /** 2) 在写入通道之前进行编码,这样输出才有意义 **/ fileChannel = new FileOutputStream("data1.txt").getChannel(); // 使用 UTF-8 写入 fileChannel.write(ByteBuffer.wrap("some text2".getBytes("UTF-16BE"))); fileChannel.close(); // 读取 fileChannel = new FileInputStream("data1.txt").getChannel(); buffer.clear(); fileChannel.read(buffer); buffer.flip(); System.out.println(buffer.asCharBuffer());// 正常输出 /** 3) 在写入通道使用buffer.asCharBuffer() **/ fileChannel = new FileOutputStream("data3.txt").getChannel(); buffer = ByteBuffer.allocate(24); buffer.asCharBuffer().put("some text3"); fileChannel.write(buffer); fileChannel.close(); // 读取 fileChannel = new FileInputStream("data3.txt").getChannel(); buffer.clear(); fileChannel.read(buffer); buffer.flip(); System.out.println(buffer.asCharBuffer());// 正常输出}
Java IO学习总结:
http://www.cnblogs.com/rollenholt/archive/2011/09/11/2173787.html
http://blog.csdn.net/zhangerqing/article/details/8466532
http://www.importnew.com/17735.html
http://blog.csdn.net/maritimesun/article/details/7973603
http://ifeve.com/java-nio-vs-io/
- 【IO/NIO】Java IO/NIO
- 【IO/NIO】Java NIO浅析
- java IO & NIO
- Java NIO vs. IO
- Java NIO vs. IO
- Java NIO vs. IO
- JAVA IO/NIO简介
- java io和nio
- Java IO NIO AIO
- Java NIO与IO
- Java NIO与IO
- Java NIO与IO
- java NIO vs IO
- Java NIO与IO
- Java NIO vs. IO
- java文件io、nio
- java IO和NIO
- Java NIO与IO
- 关于package ——java笔记
- echarts结合poi-word导出的简单处理
- java 3年经验面试题
- 浅谈欧拉定理
- ASP.NET
- 【IO/NIO】Java IO/NIO
- 如何在排序数组中,找出给定数字出现的次数
- 【RabbitMQ】——环境搭建
- 单例模式下的双重检验锁Double Checked Locking
- 【linux相识相知】磁盘分区及文件系统管理详解
- 内存泄漏以及各种不同的优化方法
- Ubuntu
- Cleaning Shifts
- 洛谷P3375 【模板】KMP字符串匹配