Java知识:IO流

来源:互联网 发布:电脑监控软件价格 编辑:程序博客网 时间:2024/05/17 09:15

Java中IO流的体系结构如图:

这里写图片描述

Java流类的类结构图:

这里写图片描述

主要的类如下:

 1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。 2. InputStream(二进制格式操作):***抽象类***,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。 3. OutputStream(二进制格式操作):***抽象类***。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。 4.Reader(文件格式操作):***抽象类***,基于字符的输入操作。 5. Writer(文件格式操作):***抽象类***,基于字符的输出操作。 6. RandomAccessFile(随机文件操作):一个独立的类,直接继承至Object.它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。

接下来结合例子理解各个IO流

FileInputStream

/** * 字节流 *读文件 * */import java.io.*;class hello{   public static void main(String[] args) throws IOException {       String fileName="D:"+File.separator+"hello.txt";       File f=new File(fileName);       InputStream in=new FileInputStream(f);       byte[] b=new byte[1024];       int count =0;       int temp=0;       while((temp=in.read())!=(-1)){           b[count++]=(byte)temp;       }       in.close();       System.out.println(new String(b));    }}

DataOutputStream与DataInputStream

import java.io.DataOutputStream ;  import java.io.File ;  import java.io.FileOutputStream ;  public class DataOutputStreamDemo{      public static void main(String args[]) throws Exception{    // 所有异常抛出          DataOutputStream dos = null ;           // 声明数据输出流对象          File f = new File("d:" + File.separator + "order.txt") ; // 文件的保存路径          dos = new DataOutputStream(new FileOutputStream(f)) ;   // 实例化数据输出流对象          String names[] = {"衬衣","手套","围巾"} ; // 商品名称          float prices[] = {98.3f,30.3f,50.5f} ;      // 商品价格          int nums[] = {3,2,1} ;  // 商品数量          for(int i=0;i<names.length;i++){ // 循环输出              dos.writeChars(names[i]) ;  // 写入字符串              dos.writeChar('\t') ;   // 写入分隔符              dos.writeFloat(prices[i]) ; // 写入价格              dos.writeChar('\t') ;   // 写入分隔符              dos.writeInt(nums[i]) ; // 写入数量              dos.writeChar('\n') ;   // 换行          }          dos.close() ;   // 关闭输出流      }  };  
import java.io.DataInputStream ;  import java.io.File ;  import java.io.FileInputStream ;  public class DataInputStreamDemo{      public static void main(String args[]) throws Exception{    // 所有异常抛出          DataInputStream dis = null ;        // 声明数据输入流对象          File f = new File("d:" + File.separator + "order.txt") ; // 文件的保存路径          dis = new DataInputStream(new FileInputStream(f)) ; // 实例化数据输入流对象          String name = null ;    // 接收名称          float price = 0.0f ;    // 接收价格          int num = 0 ;   // 接收数量          char temp[] = null ;    // 接收商品名称          int len = 0 ;   // 保存读取数据的个数          char c = 0 ;    // '\u0000'          try{              for(int i=0;i<3;i++){       //如果是while(true)是会产生EOFException的!!!!                temp = new char[200] ;  // 开辟空间                  len = 0 ;                  while((c=dis.readChar())!='\t'){    // 接收内容                      temp[len] = c ;                      len ++ ;    // 读取长度加1                  }                  name = new String(temp,0,len) ; // 将字符数组变为String                  price = dis.readFloat() ;   // 读取价格                  dis.readChar() ;    // 读取\t                  num = dis.readInt() ;   // 读取int                  dis.readChar() ;    // 读取\n                  System.out.printf("名称:%s;价格:%5.2f;数量:%d\n",name,price,num) ;              }          }catch(Exception e){e.printStackTrace;}   //原作者没有写e.printStackTrace;这句话        dis.close() ;      }  };  

经过这个例子会对这个类有所理解,下面这两句话对理解这个类很有帮助:
DataInputStream是数据输入流,可以读取java的基本数据类型。
FileInputStream是从文件系统中,读取的单位只能是字节。

ByteArrayInputStream+PushBackInputStream

/** * 回退流操作 * */public class PushBackInputStreamDemo{    public static void main(String[] args) throwsIOException{       public static void main(String[] args) throws Exception             {                String s="123456789";               byte b[]=s.getBytes();               BufferedInputStream bis=new BufferedInputStream(new ByteArrayInputStream(b));               PushbackInputStream pis=new PushbackInputStream(bis);               int tmp=1;               int i=0;               while((tmp=pis.read())!=-1)               {                  if(tmp=='3')                 //监听               {                   pis.unread('n');         //修改                   tmp=pis.read();          //修改后的值保存到变量tmp               }               System.out.print((char)tmp);               i++;               }               pis.close();             }     }}

ByteArrayInputStream基本的介质流之一,它从Byte数组中读取数据。
FIleInputStream也是基本的介质流之一,它从文件中读取数据。
PushBackInputStream的作用:监听流中的数据,如果监听到自己感兴趣的数据可以对其进行改写。

InputStream与BufferedInputStream比较

public class DataInputStreamDemo {    private static final String FILENAME="E:\\迅雷下载\\越狱.Prison.Break.S05E06.中英字幕.HDTVrip.720P.mp4";    public static void main(String[] args) throws IOException {        long l1 = readByBufferedInputStream();        long l2 = readByInputStream();        System.out.println("通过BufferedInputStream读取用时:"+l1+";通过InputStream读取用时:"+l2);  //      System.out.println("通过BufferedInputStream读取用时:"+l1);    }    public static long readByInputStream() throws IOException {        InputStream in=new FileInputStream(FILENAME);        byte[] b=new byte[8192];        int l=0;        long start=System.currentTimeMillis();        while(in.read(b)!=-1){        }        long end=System.currentTimeMillis();        return end-start;    }    public static long readByBufferedInputStream() throws IOException {        BufferedInputStream in=new BufferedInputStream(new FileInputStream(FILENAME));        byte[] b=new byte[8192];        int l=0;        long start=System.currentTimeMillis();        while(in.read(b)!=-1){        }        long end=System.currentTimeMillis();        return end-start;    }           }

上面的程序我们再多测试几种情况:
两个new byte[8192]的情况下:
通过BufferedInputStream读取用时:325;通过InputStream读取用时:320
两个new byte[4096]的情况下:
通过BufferedInputStream读取用时:356;通过InputStream读取用时:501
两个new byte[1024]的情况下:
通过BufferedInputStream读取用时:355;通过InputStream读取用时:1467
两个new byte[512]的情况下:
通过BufferedInputStream读取用时:370;通过InputStream读取用时:2847
两个new byte[256]的情况下:
通过BufferedInputStream读取用时:374;通过InputStream读取用时:5457
我们发现BufferedInputStream的优势显现出来了,快了10多倍。

疑问1:为什么BufferedInputStream读取时间没什么变化?

其实明确一点:读取数据的快慢和程序访问磁盘的次数(IO操作)是有极大关系的,可以说是主要影响因素。
BufferedInputStream自身维护了一个默认8192字节(8K)的缓冲区,这个缓冲区有什么用呢?
如果一份文件8M,那么程序访问了8M/8=1000次
new byte[8192],new byte[4096],new byte[1024],new byte[512],new byte[256]是程序自己维护的数组,数来存储缓冲区数据,8K缓冲区仍然没有改变,那么访问磁盘次数没变,所以时间大体上是相同的。
也可以说是此例子中的BufferedInputStream类自身维护了一个8K缓冲区,程序员又为他提供了一个new Byte数组来记录缓冲区中的数据。8K的缓冲区不变,读取时间不会有很大变化。

疑问2:为什么FileInputStream读取时间变化那么大?

FileInputStream没有缓冲区,我们是通过创建一个New Byte数组来模拟缓冲区。
new byte[8192],new byte[4096],new byte[1024],new byte[512],new byte[256]不同的byte数组会使得访问磁盘次数产生很大变化,进而影响读取时间,所以时间变化大而且细心的会发现呈现指数变化。

疑问2:为什么8K的时候BufferedInputStream比FIleInputStream速度还慢?

两个类都有8K缓冲区:
BufferedInputStream类自身维护的8K缓冲区
我们为FileInputStream模拟的8Kbyte数组
所以他们访问磁盘的次数相同,但是BufferedInputStream不是单纯的维护一个数组,还要维护数组中其他“东西”(我也不想弄清楚有啥)所以他的操作慢一些。
关键是弄明白两个new byte的作用!
BufferedInputStream中的new byte[]是存储缓冲区中的数据,属于内存操作,速度快,对读取时间影响小
FileInputStream中的new byte[]起到的是缓冲区的作用,属于IO操作,速度慢,对读取时间影响大。

FileInputStream与FileOutputStream复制MP3的例子来唤醒对这两个类的记忆

FileInputStream fis = new FileInputStream("c:\\0.mp3"); FileOutputStream fos = new FileOutputStream("c:\\1.mp3");byte[] buf=new byte[1024];int len=0;while((len=fis.read())!=-1){    fos.write(buf,0,len);}fos.close();fis.close();

BufferedInputStream与BufferedOutputStream复制MP3来唤醒对这两个类的记忆

FileInputStream fis = new FileInputStream("c:\\0.mp3"); BufferedInputStream bis=new BufferedInputStream(fis);FileOutputStream fos = new FileOutputStream("c:\\1.mp3");BufferedOutputStream bos=new BufferedOutputStream(fos);byte[] buf=new byte[1024];          //这句话对效率的提升也有很大帮助,不光需要BufferedInputStream、BufferedOutputStream的内部缓冲区来提升效率。int len=0;while((len=fis.read())!=-1){    fos.write(buf,0,len);}fos.close();fis.close();

ByteArrayInputStream与ByteArrayOutputStream:使用内存操作流将一个大写字母转化为小写字母

/** * 使用内存操作流将一个大写字母转化为小写字母 * */import java.io.*;class hello{   public static void main(String[] args) throws IOException {       String str="ROLLENHOLT";       ByteArrayInputStream input=new ByteArrayInputStream(str.getBytes());       ByteArrayOutputStream output=new ByteArrayOutputStream();       int temp=0;       while((temp=input.read())!=-1){           char ch=(char)temp;           output.write(Character.toLowerCase(ch));       }       String outStr=output.toString();       input.close();       output.close();       System.out.println(outStr);    }}

PipedInputStream+PipedOutputStream实现线程间通讯

public class DataInputStreamDemo {    //private static final String FILENAME="E:\\迅雷下载\\越狱.Prison.Break.S05E06.中英字幕.HDTVrip.720P.mp4";    public static void main(String[] args) throws IOException {        PipedInputStream input=new PipedInputStream();        PipedOutputStream output=new PipedOutputStream();        input.connect(output);        new Thread(new Input(input)).start();        new Thread(new Output(output)).start();    }}class Input implements Runnable{    private PipedInputStream in;    Input(PipedInputStream in)    {        this.in=in;    }    @Override    public void run() {        // TODO Auto-generated method stub        try {            byte[] b=new byte[1024];            int len=in.read(b);            String s=new String(b,0,len);            System.out.println("s="+s);            in.close();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}    class Output implements Runnable    {        private PipedOutputStream out;        Output(PipedOutputStream out)        {            this.out=out;        }        @Override        public void run() {            // TODO Auto-generated method stub            try {                //Thread.sleep(5000);                out.write("hi.管道来了".getBytes());            } catch (Exception e) {                // TODO Auto-generated catch block                e.printStackTrace();            }         }}

控制台输出:
s=hi.管道来了
此流不建议用于单线程,可能会引起死锁。因为read方法是阻塞式方法,在单线程中读取不到数据会一直等待,write方法执行不到。

FileReader与FileWriter例程

public class Test{    private static final int BUFFER_SIZE=1024;    public static void main()    {        FileReader fr=null;        FileWriter fw=null;        try        {            fr=new FileReader("1.txt");            fw=new FileWriter("2.txt");            //创建一个临时容器,用于缓存读取到的字符            char[] buf=new char[BUFFER_SIZE];//这就是缓冲区            //定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数)            int len=0;            while((len=fr.read(buf))!=-1)            {                fw.write(buf,0,len);            }        }catch(Exception e)        {            throw new RuntimeException("读写失败");        }finally        {            if(fw!=null)            try            {            fw.close();            }catch(Exception)            {            e.printStackTrace();            }            if(fr!=null)            try            {            fr.close();            }catch(Exception)            {            e.printStackTrace();            }        }    }}

BufferedReader与BufferedWriter例程

public class test{    public static void main()    {        FileReader fr=new FileReader("1.txt");        BufferedReader bufr=new BufferedReader(fr);        FileWriter fw=new FileWriter("2.txt");        BufferedWriter bufw=new BufferedWriter(fw);        String line=null;        while((line=bufr.readLine())!=null)        {        bufw.write(line);        bufw.newLine();        bufw.flush();        }        bufr.close();        bufw.close();    }}

转换流:InputStreamReader与OutputStreamWriter例程:控制台输入字符串,控制台显示字符串

public class test{ public static void mian(String[] args)throws IOException {     //控制台输入    InputStream in=System.in;    //通过转换流转换成字符数据,方便操作    InputStreamReader isr=new InputStreamReader(in);    //加入缓冲更高效    BufferedReader bufr=new BufferedReader(isr);    OutputStream out=System.out;    OutputSteamWriter osw=new OutputStream(out);    BufferedWriter bufw=new BufferedWriter(osw);    String line=null;    while((line=bufr.readLine())!=null)    {        if("over".equals(line))        break;        bufw.write(line.toupperCase());        bufw.newLine();        bufw.flush();    } }}

不使用转换流也可以完成上述功能,本例子就是为了加深转换流的印象才用的转换流。

什么情况下必须用转换流呢?

当需要明确码表的时候

public static void main(String[] args) throws IOException {        //在IO流中,如果想指定编码读写数据,只能使用转换流。        //采用指定编码从文本文件中读取内容        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("C:\\a.txt"), "UTF-8"));        String line = null;        while ((line=br.readLine())!=null) {            System.out.println(line);        }        br.close();    }
public static void main(String[] args) throws IOException {        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("C:\\a.txt"), "UTF-8"));        bw.write("I am 。。");        bw.close();    }

IO流操作规律

想要知道开发时用到哪个对象。只要通过四个明确即可。
1、明确源和目的
源:InputStream Reader
目的:OutputStream Writer
2、明确数据是否是纯文本。
源:是纯文本:Reader
否:InputStream
目的:是纯文本:Writer
否:OutputStream
3、明确具体的设备:
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
4、是否需要额外功能
1、是否需要高效(缓冲区)
是就加上buffer
2、转换

需求1:复制一个文本文件

1、明确源和目的:
源:InputStream Reader
目的:OutputStream Writer
2、是否是纯文本?

源:Reader
目的:Writer
3、明确具体设备
源:
硬盘:File
目的;
硬盘:File
FileReader fr=new FileReader(“1.txt”);
FileWriter fw=new FileWriter(“2.txt”);
4、需要额外功能吗?
需要:高效
BufferedReader bufr=new BufferedReader(new FileReader(“1.txt”));
BufferedWriter bufw=new BufferedWriter(new FileWriter(“2.txt”));

需求2:读取键盘录入信息,并写入到一个文件中。

1、明确源和目的。
源:InputStream Reader
目的:OutputStream writer
2、是否是纯文本呢?

源:Reader
目的:Writer
3、明确具体设备
源:
键盘:System.in
目的;
硬盘:File
InputStream in=System.in;
FileWriter fw=new FileWriter(“1.txt”);
4、是否需要额外功能?
是:需要转换
InputStreamWriter isw=new InputStreamWriter(System.in);
FileWriter fw=new FileWriter(“1.txt”);
还需要功能吗?需要高效
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in))
BufferedWriter bufw=new BufferedWriter(new FileWriter(“1.txt”));

需求3:将一个文本文件数据显示在控制台上。

1、明确源和目的。
源:InputStream Reader
目的:OutputStream writer
2、是否是纯文本呢?

源:Reader
目的:Writer
3、明确具体设备
源:
硬盘:File
目的;
显示器:System.out
FileReader fr=new FileReaderr(“1.txt”);
OutputStream os=System.out;
4、需要额外功能吗?
需要,转换
FileReader fr=new FileReader(“1.txt”);
OutputStreamWriter osw=new OutputStreamWriter(System.out);
需要,高效
BufferedReader bufr=new BufferedReader(new FileReader(“1.txt”));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));

需求4:读取键盘录入数据,显示在控制台上。

1、明确源和目的。
源:InputStream Reader
目的:OutputStream writer
2、是否是纯文本呢?

源:Reader
目的:Writer
3、明确具体设备
源:
键盘:System.in
目的;
显示器:System.out
InputStream in=System.in;
OutputStream out=System.out;
4、明确额外功能
需要转换:
InputStreamReader isr=new InputStreamReader(System.in);
OutputStreamWriter osw=new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr=new BufferedReader(new InputStreamReaer(System.in));
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));

需求5:将一个中文字符串按照指定的编码表写入到一个文本文件

1、明确源和目的。
源:无
目的:OutputStream writer
2、是否是纯文本呢?

源:无
目的:Writer
3、明确具体设备
源:

目的;
硬盘:File
FileWriter fw=new FileWriter(“1.txt”);
4、需要额外功能吗?
需要转换
注意:既然需求明确了制定编码表的动作那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。只能使用其父类OutputStreamWriter,OutputStreamWriter接受一个字节输出流对象,既然是操作文件,那么应该是FileOutputStream
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(“1.txt”),charset);
需要高效吗?
需要
BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(“1.txt”),charset))

Java IO所采用的模型

还需要详解一下
Java的IO模型设计非常优秀,它使用Decorator(装饰者)模式,按功能划分Stream,您可以动态装配这些Stream,以便获得您需要的功能。
例如,您需要一个具有缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。

参考资料:
http://blog.csdn.net/tanqian351/article/details/51209438
https://www.2cto.com/kf/201312/262036.html
http://blog.csdn.net/jiangwei0910410003/article/details/22376895
http://blog.csdn.net/u013087513/article/details/52148934
http://bbs.csdn.net/topics/390517474/
http://blog.csdn.net/u013905744/article/details/51924258
毕向东Java讲义 PDF版

原创粉丝点击