IO是Java及众多编程语言很重要的一块,同时很多程序的瓶颈和耗时操作也都在IO这块。所以能够很好的解决IO问题对提高程序性能有很大的帮助!本章我们将要系统的对Java IO做个分析,通过理论加实践,希望能真正彻底的理解并且掌握了它。
一、简介
IO操作面临很多问题,信息量的巨大,网络的环境等等,因为IO不仅仅是对本地文件、目录的操作,有时对二进制流、还有一部分是网络方面的资源,所以多种原因直接造成IO操作无疑是耗时且复杂多变的。Java对IO的支持是个不断的演变过程,经过了很多的优化,直到JDK1.4以后,才趋于稳定,在JDK1.4中,加入了nio类,解决了很多性能问题,虽然我们有足够的理由不去了解关于Java IO以前的情况,但是为了学好现在的类,我们还是打算去研究下,通过掌握类的优化情况来彻底理解IO的机制!Java IO主要主要在java.io包下,分为四大块近80个类:
1、基于字节操作的I/O接口:InputStream和OutputStream
2、基于字符操作的I/O接口:Writer和Reader
3、基于磁盘操作的I/O接口:File
4、基于网络操作的I/O接口:Socket(不在java.io包下)
影响IO性能的无非就是两大因素:数据的格式及存储介质,前两类主要是数据格式方面的,后两个类是地址方面的:本地和网络。所以策划好这两个方面的活动,有助于我们合理使用IO。
二、基于字节的I/O操作(InputStream和OutputStream)
我们先来看看类图:
public abstract class InputStream extends Object implements Closeable
图1
图2
二者类似,我只详细讲解InputStream类,OutputStream留给大家自己去学习。InputStream类是个抽象类,里面核心的方法就是read()、read(byte b[])、read(byte b[], int off, int len),这三个方法是用于读取数据的底层的方法,他们可以用来读取以下这些类型的数据:
A. 字节数、B. String对象、C. 文件、D. 管道,从一端进入,从另一端输出、E. 流、F. internet资源
每一种数据源都有相应的InputStream子类,因为InputStream是个处于顶层的类,用来处理各种数据源的类都继承了InputStream类,我们来看看这些类:
ByteArrayInputStream:处理字节数组的类,允许将内存的缓冲区当做InputStream使用。
StringBufferInputStream:将String转换成InputStream,内部实现用的是StringBuffer。
FileInputStream:从文件中读取数据。
PipedInputStream:用于从管道中读取数据。
SequenceInputStream:将多个流对象转化成一个InputStream。
FilterInputStream:装饰器类,为其它InputStream类提供功能。
做过关于IO操作的读者知道,我们很少单独使用哪个类来实现IO操作,平时都是几个类合起来使用,这其实体现了一种装饰器模式(详见:http://blog.csdn.net/zhangerqing)的思想,在后面的分析中我们会详细的分析。从上面的图1中我们可以看出,FilterInputStream虽说是Inputstream的子类,但它依然是BufferedInputStream、DataInputStream、LineNumberInputStream、PushbackInputStream类的父类,这四个类分别提供了最贴近我们程序员使用的方法,如:readInt() 、readLine()、readChar()等等。对于IO操作,不管是磁盘还是网络,最终都是对字节的操作,而我们平时写的程序都是字符形式的,所以在传输的过程中需要进行转换。在字符到字节的转换过程中,我们需要用到一个类:InputStreamReader。
三、基于字符的I/O操作(Writer和Reader)
图3
图4
Writer和Reader操作的目的就是操作字符不是字节,和InputStream和OutputStream配合增加IO效果。通过InputStreamReader和OutputStreamReader可以进行字节和字符的转换,设计Writer和Reader的目的是国际化,使IO操作支持16位的Unicode。
四、基于磁盘的I/O操作(File)
五、基于网络的I/O操作(Socket)
六、NIO
七、经典IO操作
Library:1、从本地字符流文件(绝对、相对)地址读取文件
import java.io.BufferedReader; import java.io.FileReader; import java.io.StringReader; class InputStreamTest { public static String read(String filename) throws Exception { BufferedReader br = new BufferedReader(new FileReader(filename)); //Reader实例 String s; StringBuffer sb = new StringBuffer(); while ((s = br.readLine()) != null) { sb.append(s + "\n"); } br.close(); return sb.toString(); } }//sb就是文件的字符串,下面将其一个一个读出 public class MemoryInput { public static void main(String[] args) throws Exception { StringReader in = new StringReader( InputStreamTest.read("../ReaderStream/wwww.txt")); int c; while ((c = in.read()) != -1) System.out.print((char) c+" "); } }2、 System.in 、 System.out
System.in返回的是未经包装的InputStream对象,所以需要进行装饰,经InputStreamReader转换为Reader对象,放入BufferedReader的构造方法中。除此之外,System.out和System.err都是直接的PriintStream对象,可直接使用。
public final class System { public final static InputStream in = null; public final static PrintStream out = null; public final static PrintStream err = null; private static volatile SecurityManager security = null; public abstract class InputStream implements Closeable { public class PrintStream extends FilterOutputStream implements Appendable, Closeable { public void println() { newLine(); } public void print(int i) { write(String.valueOf(i)); //class Writer.writer(String) write(str, 0, str.length()); } private void newLine() { try { synchronized (this) {
也可以使用java.util包下的Scanner类来实现上述程序;
3、读取二进制文件
public class BinaryFile { /* the parameter is a file */ public static byte[] read(File file) throws IOException { BufferedInputStream bf = new BufferedInputStream(new FileInputStream( file)); try { byte[] data = new byte[bf.available()]; bf.read(data); return data; } finally { bf.close(); } } /* the param is the path of a file */ public static byte[] read(String file) throws IOException { return read(new File(file).getAbsoluteFile()); } public static void main(String[] args) throws IOException{ byte[] a=BinaryFile.read("E:/jsL/20130415161705906.jpg"); String s=new String(a); System.out.println(s+a.length); } } 读出来的都是乱码
4、单个文件复制(边读编写)
public class CopyBinaryFile{ //private static final int MAX_SKIP_BUFFER_SIZE = 2048; public static void copyFile(String oldPath,String newPath) throws IOException{ File oldfile=new File(oldPath); if(oldfile.exists()){ FileInputStream inf=new FileInputStream(oldPath); FileOutputStream outf=new FileOutputStream(newPath); byte[] b=new byte[1024]; int length=0; while((length=inf.read(b))>0){ outf.write(b, 0, length); //边读边写 } inf.close(); outf.close(); } } public static void main(String...args) throws IOException{ String oldPath="E:/jsL/20130415161705906.jpg"; String newPath = "e:/jsl/sb.jpg"; copyFile(oldPath,newPath); } }
5、文件目录复制(太慢)
package springc; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; public class CopyBinaryFolder { public static void copyFolder(String oldPath,String newPath) { try{ new File(newPath).mkdirs(); //新建文件目录(会自行判断是否存在) File oldfile=new File(oldPath); String[] context=oldfile.list(); File temp=null; for(int x=0;x<context.length;x++){ if(oldPath.endsWith("/")){ temp=new File(oldPath+context[x]); }else{ temp=new File(oldPath+"/"+context[x]); } if(temp.isFile()){ FileInputStream inf=new FileInputStream(temp); FileOutputStream outf=new FileOutputStream(newPath+"/"+temp.getName().toString()); byte[] buffer=new byte[1024*5]; int l=0; while((x=inf.read(buffer))>0){ outf.write(l); } inf.close(); outf.close(); }else if(temp.isDirectory()){ copyFolder(oldPath+"/"+context[x], newPath+"/"+context[x]); } } } catch(Exception e){ System.out.println("复制出现异常"); try { throw e; } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } public static void main(String...strings){ System.out.println("复制中。。。"); copyFolder("E:/jsL/new1/","E:/jsL/new2"); System.out.println("复制完成!"); } }
6、文件管道复制(较快)
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; public class CopyChannelFile { public static void fileChannelCopy(String oldfile,String newfile) { FileInputStream fi=null; FileOutputStream fo =null; FileChannel in = null; FileChannel out = null; try { fi = new FileInputStream(new File(oldfile)); fo = new FileOutputStream(new File(newfile)); in = fi.getChannel();//得到对应的文件通道 out =fo.getChannel();//得到对应的文件通道 in.transferTo(0, in.size(), out);//连接两个通道,并且从in通道读取,然后写入out通道 } catch (IOException e) { e.printStackTrace(); } finally { try { fi.close(); in.close(); fo.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } } } static void exeChannel(String oldfile,String newfile) throws InterruptedException{ long start,end; start=System.currentTimeMillis(); System.out.println(start); fileChannelCopy(oldfile,newfile); end=System.currentTimeMillis(); System.out.println("管道耗时:"+(end-start)+"ms"); //没有打印,不过很快 } static void exeStream(String oldfile,String newfile) throws IOException{ long start,end; CopyBinaryFile f=new CopyBinaryFile(); start=System.currentTimeMillis(); f.copyFile(oldfile, newfile); end=System.currentTimeMillis(); System.out.println("输出流耗时:"+(end-start)+"ms"); } public static void main(String...s) throws IOException{ fileChannelCopy("F:G/test.pdf","E:/jsL/new1/test.pdf"); //74M // exeStream("F:G/test.pdf","E:/jsL/new1/test.pdf"); } }
l