黑马程序员_IO流基本概念

一、流的概念
        流(stream)的概念源于UNIX中管道(pipe)的概念。在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备、外部文件等。
       一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL。
        流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流。用户可以从输入流中读取信息,但不能写它。相反,对输出流,只能往输入流写,而不能读它。
       实际上,流的源端和目的端可简单地看成是字节的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。 
       形象的比喻——水流 ,文件======程序 ,文件和程序之间连接一个管道,水流就在之间形成了,自然也就出现了方向:可以流进,也可以流出.便于理解,这么定义流: 流就是一个管道里面有流水,这个管道连接了文件和程序。
二、流的分类
        按流向分为输入流和输出流;
        按传输单位分为字节流(Stream)结尾的和字符流(Reader和Writer);
        按功能还可以分为节点流和过滤流。
        节点流:负责数据源和程序之间建立连接;(相当于裸枪)
        过滤流:用于给节点增加功能。(相当于功能零部件)
        过滤流的构造方式是以其他流位参数构造(这样的设计模式称为装饰模式)。
                                        
                                         
   注:I/O流是一类很宝贵的资源,使用完后必须调用close()方法关闭流并释放资源。在关闭流时只用关闭最外层的流。   

        stream代表的是任何有能力产出数据的数据源,或是任何有能力接收数据的接收源。在Java的IO中,所有的stream(包括Input和Out stream)都包括两种类型:

  2.1 以字节为导向的stream以字节为导向的stream,表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型:

       Input stream:
                          1) ByteArrayInputStream:把内存中的一个缓冲区作为InputStream使用
                          2) StringBufferInputStream:把一个String对象作为InputStream
                          3) FileInputStream:把一个文件作为InputStream,实现对文件的读取操作
                          4) PipedInputStream:实现了pipe的概念,主要在线程中使用
                          5) SequenceInputStream:把多个InputStream合并为一个InputStream
       Out stream:
                         1) ByteArrayOutputStream:把信息存入内存中的一个缓冲区中
                         2) FileOutputStream:把信息存入文件中
                         3) PipedOutputStream:实现了pipe的概念,主要在线程中使用
                         4) SequenceOutputStream:把多个OutStream合并为一个OutStream

2.2 以Unicode字符为导向的stream
     以Unicode字符为导向的stream,表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几种类型:
        Input stream:
                         1) CharArrayReader:与ByteArrayInputStream对应
                         2) StringReader:与StringBufferInputStream对应
                         3) FileReader:与FileInputStream对应
                         4) PipedReader:与PipedInputStream对应
        Out stream:
                         1) CharArrayWrite:与ByteArrayOutputStream对应
                         2) StringWrite:无与之对应的以字节为导向的stream
                         3) FileWrite:与FileOutputStream对应
                         4) PipedWrite:与PipedOutputStream对应

       以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同,只是在操作时的导向不同。如CharArrayReader:和

ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用,所不同的是后者每次从内存中读取一个字节的信息,而后者每次从内存中读取一个字符。

      2.3 两种不现导向的stream之间的转换
  InputStreamReader和OutputStreamReader:把一个以字节为导向的stream转换成一个以字符为导向的stream

JAVA字节流

  • FileInputStream和FileOutputStream
    这两个类属于结点流,第一个类的源端和第二个类的目的端都是磁盘文件,它们的构造方法允许通过文件的路径名来构造相应的流。如: 
    FileInputStream infile = new FileInputStream("myfile.dat");
    FileOutputStream outfile = new FileOutputStream("results.dat");
  • 要注意的是,构造FileInputStream, 对应的文件必须存在并且是可读的,而构造FileOutputStream时,如输出文件已存在,则必须是可覆盖的。
  • BufferedInputStream和BufferedOutputStream
    它们是过滤器流,其作用是提高输入输出的效率。
  • DataInputStream和DataOutputStream
    这两个类创建的对象分别被称为数据输入流和数据输出流。这是很有用的两个流,它们允许程序按与机器无关的风格读写Java数据。所以比较适合于网络上的数据传输。这两个流也是过滤器流,常以其它流如InputStream或OutputStream作为它们的输入或输出。
  •   1.字节输入流:io包中的InputStream为所有字节输入流的父类。
             Int read();读入一个字节(每次一个);
             可先使用new byte[]=数组,调用read(byte[] b)
             read (byte[])返回值可以表示有效数;read (byte[])返回值为-1 表示结束。 
      2.在流中close()方法由程序员控制。因为输入输出流已经超越了JVM的边界,所以有时可能无法回收资源。
             原则:凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。
  • 字节流的字符编码:
         字符编码把字符转换成数字存储到计算机中,按ASCii 将字母映射为整数。
         把数字从计算机转换成相应的字符的过程称为解码。
         乱码的根源在于编解码方式不统一。在世界上任何一种编码方式中都会向上兼容ASCII码。所以英文没有乱码。
         编码方式的分类:
         ASCII(数字、英文):1 个字符占一个字节(所有的编码集都兼容ASCII)
         ISO8859-1(欧洲):1 个字符占一个字节
         GB-2312/GBK:1 个字符占两个字节。GB代表国家标准。
         GBK是在GB-2312上增加的一类新的编码方式,也是现在最常用的汉字编码方式。
         Unicode: 1 个字符占两个字节(网络传输速度慢)
         UTF-8:变长字节,对于英文一个字节,汉字三个字节。
         原则:保证编解码方式的统一,才能不至于出现错误。   
        I/O学习种常范的两个错误 1。忘了加flush2.没有加换行。
Java的字符流
    字符流主要是用来处理字符的。Java采用16位的Unicode来表示字符串和字符,对应的字符流按输入和输出分别称为readers和writers。
  • InputStreamReader和OutputStreamWriter
    在构造这两个类对应的流时,它们会自动进行转换,将平台缺省的编码集编码的字节转换为Unicode字符。对英语环境,其缺省的编码集一般为ISO8859-1。
  • BufferedReader和BufferedWriter
    这两个类对应的流使用了缓冲,能大大提高输入输出的效率。这两个也是过滤器流,常用来对InputStreamReader和OutputStreamWriter进行处理。如: 
    [java]  view plain copy
    1. import java.io.*;  
    2. public class Echo {  
    3.   public static void main(String[] args) {  
    4.     BufferedReader in =  
    5.       new BufferedReader(  
    6.         new InputStreamReader(System.in));  
    7.     String s;  
    8.     try {  
    9.       while((s = in.readLine()).length() != 0)  
    10.         System.out.println(s);  
    11.       // An empty line terminates the program  
    12.     } catch(IOException e) {  
    13.       e.printStackTrace();  
    14.     }  
    15.   }  
    16. }  
该程序接受键盘输入并回显。
对BufferedReader类,该类的readLine()方法能一次从流中读入一行,但对于BufferedWriter类,就没有一次写一行的方法,所以若要向流中一次写一行,可用PrintWriter类将原来的流改造成新的打印流,PrintWriter类有一个方法println(),能一次输出一行。如: 
[java]  view plain copy
  1. ............  
  2. PrintWriter out = new PrintWriter(new BufferedWriter(  
  3.       new FileWriter("D:\javacode\test.txt")));  
  4. out.println("Hello World!");  
  5. out.close();  
  6. ............  

例子:

1,与控制台相关。的读入/写出。 实现了字符串的复制。

[java]  view plain copy
  1. import java.io.*;  
  2. public class TextRead{  
  3.   
  4. public static void main(String[] args){  
  5.    BufferedReader bf = null;/*BufferedReader相当于一个大桶,其实就是内存,这里实现了大量大量的读写 ,而不是读一个字节或字符就直接写如硬盘,加强了对硬盘的保护。*/  
  6.    try{  
  7.     while(true){ // while(true){}循环保证程序不会结束  
  8.       
  9.        bf = new BufferedReader(new InputStreamReader(System.in));  
  10.        /*System.in 为标准输入,System.out为标准输出*/  
  11.        /*InputStreamReader用语将字节流到字符流的转化,这也就是处理流了 
  12.         *在这里相当与2个管道接在System.in与程序之间。 
  13.         *readLine()方法功能比较好用,也就通过处理流来实现更好功能。 
  14.         **/  
  15.      String line = bf.readLine();  
  16.      System.out.println(line);  
  17.     }    
  18.    }catch(Exception e){  
  19.     e.printStackTrace();   
  20.    }finally{   
  21.     //一定要关闭流,用完后。最好放在  
  22. filally 里面。    
  23.     try{     
  24.      if(bf!=null){  
  25.       bf.close();  
  26.      }  
  27.     }catch(Exception e){  
  28.        e.printStackTrace();   
  29.     }  
  30.    }     
  31. }  
  32. }  

2,与文件 相关的 读写。    实现了文件的复制。

[java]  view plain copy
  1. import java.io.*;  
  2. public class TextRead{  
  3. public static void main(String[] args){  
  4.    File fin,fout;     
  5.    BufferedReader bf = null;   
  6.    PrintWriter pw = null;  
  7.    try{  
  8.     fin = new File("zzc.txt"); //注意文件与程序都要在同一个文件夹下。zzc.txt为要被复制的文件。  
  9.     fout = new File("copyzzc.txt"); //如果没有会自动创建。  
  10.     bf = new BufferedReader(new FileReader(fin));  
  11.     pw = new PrintWriter(fout); //PrintWriter为打印流,也可以使用BufferedWriter.  
  12.     String line = bf.readLine();  
  13.     while(line!=null){  
  14.      pw.println(line);     
  15.      line = bf.readLine();   
  16.     }  
  17.    }catch(Exception e){  
  18.     e.printStackTrace();    
  19.    }finally{  
  20.     try{  
  21.     //关闭 文件。   
  22.      if(bf!=null){  
  23.       bf.close();  
  24.       bf = null;  
  25.      }  
  26.      if(pw!=null){  
  27.       pw.close();  
  28.       pw = null;  
  29.      }  
  30.     }catch(Exception e){  
  31.      e.printStackTrace();   
  32.     }  
  33.    }  
  34. }  
  35. }  

三. stream添加属性
  3.1 “为stream添加属性”的作用
运用上面介绍的Java中操作IO的API,我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类,我们可以为stream添加属性。下面以一个例子来说明这种功能的作用。如果我们要往一个文件中写入数据,我们可以这样操作:
FileOutStream fs = new FileOutStream(“test.txt”);
然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是,如果我们想实现“先把要写入文件的数据先缓存到内存中,再把缓存中的数据写入文件中”的功能时,上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的子类,为FileOutStream添加我们所需要的功能。
3.2 FilterInputStream的各种类型
3.2.1 用于封装以字节为导向的InputStream
      1) DataInputStream:从stream中读取基本类型(int、char等)数据。
      2) BufferedInputStream:使用缓冲区
      3) LineNumberInputStream:会记录input stream内的行数,然后可以调用getLineNumber()和setLineNumber(int)
      4) PushbackInputStream:很少用到,一般用于编译器开发
3.2.2 用于封装以字符为导向的InputStream
      1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader,否则使用DataInputStream
      2) BufferedReader:与BufferedInputStream对应
      3) LineNumberReader:与LineNumberInputStream对应
      4) PushBackReader:与PushbackInputStream对应
3.3 FilterOutStream的各种类型
2.3.1 用于封装以字节为导向的OutputStream
      1) DataIOutStream:往stream中输出基本类型(int、char等)数据。
      2) BufferedOutStream:使用缓冲区
      3) PrintStream:产生格式化输出
3.3.2 用于封装以字符为导向的OutputStream
      1) BufferedWrite:与对应
      2) PrintWrite:与对应

四. RandomAccessFile

        1) 可通过RandomAccessFile对象完成对文件的读写操作
        2) 在产生一个对象时,可指明要打开的文件的性质:r,只读;w,只写;rw可读写
        3) 可以直接跳到文件中指定的位置

五、File

 File 类(java.io.*)可表示一个文件,也有可能是一个目录(在JAVA 中文件和目录都属于这个类中,而且区分不是非常的明显 )。Java.io 下的方法是对磁盘上的文件进行磁盘操作,但是无法读取文件的内容。
注意:创建一个文件对象和创建一个文件在JAVA 中是两个不同的概念。前者是在虚拟机中创建了一个文件,
      但却并没有将它真正地创建到OS 的文件系统中,随着虚拟机的关闭,这个创建的对象也就消失了。
      而创建一个文件才是在系统中真正地建立一个文件。          
File类:我们猛看起来像是文件,其实他也可以指代一个文件集,当作为文件集时我们可以对此调用List方法。
       这个类的常用构造有:File("路径"),File(“前边路径”,“后边路径”)File(File,“路径”)
      这个类的常用方法:exists(),delete(),getName(),getPath(),isDirectory(),isFile(),length(),
                        listFile(FileFilter),主要用来过滤文件这里有(可以用来嵌套内部类) 
                        mkdir(),mkdirs(),toString(),
       FileFilter接口只有accept方法,其返回值为Boolean型,可以在其里边写一些正则表达式来对文件进行筛选。
       这里需要注意的是传入accept的参数必须是final类型(匿名内部类的要求),这样他才能使用该类范围之外的队像。
       顺便讲讲:内部类优点 高聚拢性,缺点在于不易阅读,谨慎使用。

六 、对象序列化
   1. 定义:把一个对象通过I/O流写到文件(持久性介质)上的过程叫做对象的序列化。
   2. 序列化接口:Serializable
       此接口没有任何的方法,这样的接口称为标记接口。
   3. 不是所有对象都能序列化的,只有实现了Serializable的类,他的实例对象才是可序列化的。
   4. 在Java种定义了一套序列化规范,对象的编码和解码方式都是已经定义好的。
   5. class ObjectOutputStream 和ObjectInputStream也是过滤流,使节点流直接获得输出对象。
       ganbin@tarena.com.cn
      最有用的方法:
    (1)writeObject(Object b)
    (2)readObject();该方法返回的是读到的一个对象,但是需要我们注意的是,该方法不会以返回null表示读到文件末尾。
     而是当读到文件末尾时会抛出一个IOException;
    6. 序列化一个对象并不一定会序列化该对象的父类对象
    7. 瞬间属性(临时属性)不参与序列化过程。
    8. 所有属性必须都是可序列化的,特别是当有些属性本身也是对象的时候,要尤其注意这一点。序列化的集合就要求集合中的每一个元素都是可序列化的。
    9. 用两次序列化把两个对象写到文件中去(以追加的方式),
    和用一次序列化把两个对象写进文件的大小是不一样的。
    因为每次追加时都会要在文件中加入一个开始标记和结束标记。所以对于对象的序列化不能以追加的方式写到文件中。

                                                                                                                                                                                                                    参考博客:

                                                                                                                                                                                                                                                   cj1240.zhmy.com/archives/2008/148832.html

morle.iteye.com/blog/197357

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值