Java io流学习总结(二)

转载: http://www.javashuo.com/article/p-vwgncrxi-a.htmljava

深刻理解Java中的IO

引言:

    对程序语言的设计者来讲,建立一个好的输入/输出(I/O)系统是一项艰难的任务 < Thinking in Java >

 

本文的目录视图以下:

 

Java IO概要

     a.Java IO中经常使用的类

     b.Java流类的类结构图

1.流的概念和做用

2.Java IO所采用的模型  :

3.IO流的分类

4.Java IO流对象

     1.输入字节流InputStream

     2.输出字节流OutputStream

     3.字符输入流Reader

     4.字符输出流Writer

5.字符流的输入与输出的对应

6.字符流与字节流转换

7.字节流和字符流的区别

8.File类

9.RandomAccessFile类

Java IO概要

 

为了方便理解与阐述,先引入两张图:

a、Java IO中经常使用的类

在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable.掌握了这些IO的核心操做那么对于Java中的IO体系也就有了一个初步的认识了数组

 

Java I/O主要包括以下几个层次,包含三个部分:安全

  1.流式部分――IO的主体部分;网络

  2.非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;多线程

  3.其余类--文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操做系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。dom

主要的类以下:函数

     1. File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。工具

     2. InputStream(二进制格式操做):抽象类,基于字节的输入操做,是全部输入流的父类。定义了全部输入流都具备的共同特征。测试

     3. OutputStream(二进制格式操做):抽象类。基于字节的输出操做。是全部输出流的父类。定义了全部输出流都具备的共同特征。编码

     4.Reader(文件格式操做):抽象类,基于字符的输入操做。

     5. Writer(文件格式操做):抽象类,基于字符的输出操做。

     6. RandomAccessFile(随机文件操做):一个独立的类,直接继承至Object.它的功能丰富,能够从文件的任意位置进行存取(输入输出)操做

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

 

 

 

 

 

 

 

 

b、Java流类的类结构图:

一、流的概念和做用

流:表明任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象<Thinking in Java>

流的本质:数据传输,根据数据传输特性将流抽象为各类类,方便更直观的进行数据操做。 

 

流的做用:为数据源和目的地创建一个输送通道。

 

     Java中将输入输出抽象称为流,就好像水管,将两个容器链接起来。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流.

二、Java IO所采用的模型  


     Java的IO模型设计很是优秀,它使用Decorator(装饰者)模式,按功能划分Stream,您能够动态装配这些Stream,以便得到您须要的功能。

       例如,您须要一个具备缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。 

三、IO流的分类

·        根据处理数据类型的不一样分为:字符流和字节流

·        根据数据流向不一样分为:输入流和输出流

·        按数据来源(去向)分类:

         一、File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter 
         二、byte[]:ByteArrayInputStream, ByteArrayOutputStream 
         三、Char[]: CharArrayReader,CharArrayWriter 
         四、String:StringBufferInputStream, StringReader, StringWriter 
         五、网络数据流:InputStream,OutputStream, Reader, Writer 

字符流和字节流

流序列中的数据既能够是未经加工的原始二进制数据,也能够是经必定编码处理后符合某种格式规定的特定数据。所以Java中的流分为两种:


 1)  字节流:数据流中最小的数据单元是字节
 2)  字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

 

字符流的由来: Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。由于数据编码的不一样,而有了对字符进行高效操做的流对象。本质其实就是基于字节流读取时,去查了指定的码表。

 

输入流和输出流

根据数据的输入、输出方向的不一样对而将流分为输入流和输出流。

1) 输入流

     程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),便是将数据源读入到程序的通讯通道

2) 输出流

   程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通讯通道。

采用数据流的目的就是使得输出输入独立于设备。

输入流( Input  Stream )不关心数据源来自何种设备(键盘,文件,网络)。
输出流( Output Stream )不关心数据的目的是何种设备(键盘,文件,网络)。

3)特性

  相对于程序来讲,输出流是往存储介质或数据通道写入数据,而输入流是从存储介质或数据通道中读取数据,通常来讲关于流的特性有下面几点:

一、先进先出,最早写入输出流的数据最早被输入流读取到。

二、顺序存取,能够一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile能够从文件的任意位置进行存取(输入输出)操做

三、只读或只写,每一个流只能是输入流或输出流的一种,不能同时具有两个功能,输入流只能进行读操做,对输出流只能进行写操做。在一个数据传输通道中,若是既要写入数据,又要读取数据,则要分别提供两个流。 

 

四、Java IO流对象

1.输入字节流InputStream

IO 中输入字节流的继承图可见上图,能够看出:

1.    InputStream是全部的输入字节流的父类,它是一个抽象类。

2.    ByteArrayInputStream、StringBufferInputStream(上图的StreamBufferInputStream)、FileInputStream是三种基本的介质流,它们分别从Byte数组、StringBuffer、和本地文件中读取数据。

3.    PipedInputStream是从与其它线程共用的管道中读取数据.

4.    ObjectInputStream和全部FilterInputStream的子类都是装饰流(装饰器模式的主角)。

InputStream中的三个基本的读方法
      abstract int read() :读取一个字节数据,并返回读到的数据,若是返回-1,表示读到了输入流的末尾。
      int read(byte[] b) :将数据读入一个字节数组,同时返回实际读取的字节数。若是返回-1,表示读到了输入流的末尾。
      int read(byte[] b, int off, int len) :将数据读入一个字节数组,同时返回实际读取的字节数。若是返回-1,表示读到了输入流的末尾。off指定在数组b中存放数据的起始偏移位置;len指定读取的最大字节数。

流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。

其它方法
      long skip(long n):在输入流中跳过n个字节,并返回实际跳过的字节数。
      int available() :返回在不发生阻塞的状况下,可读取的字节数。
      void close() :关闭输入流,释放和这个流相关的系统资源。
      void mark(int readlimit) :在输入流的当前位置放置一个标记,若是读取的字节数多于readlimit设置的值,则流忽略这个标记。
      void reset() :返回到上一个标记。
      boolean markSupported() :测试当前流是否支持mark和reset方法。若是支持,返回true,不然返回false。

2.输出字节流OutputStream

IO 中输出字节流的继承图可见上图,能够看出:

1.    OutputStream是全部的输出字节流的父类,它是一个抽象类。

2.    ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte数组、和本地文件中写入数据。PipedOutputStream是向与其它线程共用的管道中写入数据。

3.    ObjectOutputStream和全部FilterOutputStream的子类都是装饰流。

outputStream中的三个基本的写方法
     abstract void write(int b):往输出流中写入一个字节。
     void write(byte[] b) :往输出流中写入数组b中的全部字节。
     void write(byte[] b, int off, int len) :往输出流中写入数组b中从偏移量off开始的len个字节的数据。

其它方法   

  void flush() :刷新输出流,强制缓冲区中的输出字节被写出。    

  void close() :关闭输出流,释放和这个流相关的系统资源。

3.字符输入流Reader

在上面的继承关系图中能够看出:

1.    Reader是全部的输入字符流的父类,它是一个抽象类。

2.    CharReader、StringReader是两种基本的介质流,它们分别将Char数组、String中读取数据。PipedReader是从与其它线程共用的管道中读取数据。

3.    BufferedReader很明显就是一个装饰器,它和其子类负责装饰其它Reader对象。

4.    FilterReader是全部自定义具体装饰流的父类,其子类PushbackReader对Reader对象进行装饰,会增长一个行号。

5.    InputStreamReader是一个链接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader能够说是一个达到此功能、经常使用的工具类,在其源代码中明显使用了将FileInputStream转变为Reader的方法。咱们能够从这个类中获得必定的技巧。Reader中各个类的用途和使用方法基本和InputStream中的类使用一致。后面会有Reader与InputStream的对应关系。

主要方法:

     (1) public int read() throws IOException; //读取一个字符,返回值为读取的字符 

     (2) public int read(char cbuf[]) throws IOException; /*读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量*/ 
     (3) public abstract int read(char cbuf[],int off,int len) throws IOException; 
/*读取len个字符,从数组cbuf[]的下标off处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现*/ 

4.字符输出流Writer

在上面的关系图中能够看出:

1.    Writer是全部的输出字符流的父类,它是一个抽象类。

2.    CharArrayWriter、StringWriter是两种基本的介质流,它们分别向Char数组、String中写入数据。PipedWriter是向与其它线程共用的管道中写入数据,

3.    BufferedWriter是一个装饰器为Writer提供缓冲功能。

4.    PrintWriter和PrintStream极其相似,功能和使用也很是类似。

5.    OutputStreamWriter是OutputStream到Writer转换的桥梁,它的子类FileWriter其实就是一个实现此功能的具体类(具体能够研究一SourceCode)。功能和使用和OutputStream极其相似.

主要方法:

(1)  public void write(int c) throws IOException; //将整型值c的低16位写入输出流 
(2)  public void write(char cbuf[]) throws IOException; //将字符数组cbuf[]写入输出流 
(3)  public abstract void write(char cbuf[],int off,int len) throws IOException; //将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流 
(4)  public void write(String str) throws IOException; //将字符串str中的字符写入输出流 
(5)  public void write(String str,int off,int len) throws IOException; //将字符串str 中从索引off开始处的len个字符写入输出流 

5.字节流的输入与输出的对应

图中蓝色的为主要的对应部分,红色的部分就是不对应部分。从上面的图中能够看出JavaIO中的字节流是极其对称的。“存在及合理”咱们看看这些字节流中不太对称的几个类吧!

 

1.    LineNumberInputStream主要完成从流中读取数据时,会获得相应的行号,至于何时分行、在哪里分行是由改类主动肯定的,并非在原始中有这样一个行号。在输出部分没有对应的部分,咱们彻底能够本身创建一个LineNumberOutputStream,在最初写入时会有一个基准的行号,之后每次遇到换行时会在下一行添加一个行号,看起来也是能够的。好像更不入流了。

2.    PushbackInputStream的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream几乎实现相近的功能。

3.    StringBufferInputStream已经被Deprecated,自己就不该该出如今InputStream部分,主要由于String应该属于字符流的范围。已经被废弃了,固然输出部分也没有必要须要它了!还容许它存在只是为了保持版本的向下兼容而已。

4.    SequenceInputStream能够认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。彻底能够从IO包中去除,还彻底不影响IO包的结构,却让其更“纯洁”――纯洁的Decorator模式。

5.    PrintStream也能够认为是一个辅助工具。主要能够向其余输出流,或者FileInputStream写入数据,自己内部实现仍是带缓冲的。本质上是对其它流的综合运用的一个工具而已。同样能够踢出IO包!System.out和System.out就是PrintStream的实例!

字符流的输入与输出的对应

6.字符流与字节流转换

转换流的特色:

1.    其是字符流和字节流之间的桥梁

2.    可对读取到的字节数据通过指定编码转换成字符

3.    可对读取到的字符数据通过指定编码转换成字节

 

什么时候使用转换流?

1.    当字节和字符之间有转换动做时;

2.    流操做的数据须要编码或解码时。

 

具体的对象体现:

转换流:在IO中还存在一类是转换流,将字节流转换为字符流,同时能够将字符流转化为字节流。

 

1.    InputStreamReader:字节到字符的桥梁

2.     OutputStreamWriter:字符到字节的桥梁

 

OutputStreamWriter(OutStreamout):将字节流以字符流输出。

InputStreamReader(InputStream in):将字节流以字符流输入。

 

这两个流对象是字符体系中的成员,它们有转换做用,自己又是字符流,因此在构造的时候须要传入字节流对象进来。

7.字节流和字符流的区别(重点)

字节流和字符流的区别:(详细能够参见http://blog.csdn.net/qq_25184739/article/details/51203733)    

 

         节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。所以在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则须要手动调用flush()方法。

 

·        读写单位不一样:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

·        处理对象不一样:字节流能处理全部类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

 

结论:只要是处理纯文本数据,就优先考虑使用字符流。除此以外都使用字节流。

8.非流式文件类--File类

 

从定义看,File类是Object的直接子类,同时它继承了Comparable接口能够进行数组的排序。

File类的操做包括文件的建立、删除、重命名、获得路径、建立时间等,如下是文件操做经常使用的函数。

File类是对文件系统中文件以及文件夹进行封装的对象,能够经过对象的思想来操做文件和文件夹。File类保存文件或目录的各类元数据信息,包括文件名、文件长度、最后修改时间、是否可读、获取当前文件的路径名,判断指定文件是否存在、得到当前目录中的文件列表,建立、删除文件和目录等方法。 

File类共提供了三个不一样的构造函数,以不一样的参数形式灵活地接收文件和目录名信息。

构造函数:
1)File (String   pathname)   
     例:File  f1=new File("FileTest1.txt"); //建立文件对象f1,f1所指的文件是在当前目录下建立的FileTest1.txt
2)File (String  parent  ,  String child)
     例:File f2=new  File(“D:\\dir1","FileTest2.txt") ;//  注意:D:\\dir1目录事先必须存在,不然异常
3)File (File    parent  , String child)
     例:File  f4=new File("\\dir3");
          File  f5=new File(f4,"FileTest5.txt");  //在若是 \\dir3目录不存在使用f4.mkdir()先建立

        一个对应于某磁盘文件或目录的File对象一经建立, 就能够经过调用它的方法来得到文件或目录的属性。    
       1)public boolean exists( ) 判断文件或目录是否存在
       2)public boolean isFile( ) 判断是文件仍是目录 
       3)public boolean isDirectory( ) 判断是文件仍是目录
       4)public String getName( ) 返回文件名或目录名
       5)public String getPath( ) 返回文件或目录的路径。
       6)public long length( ) 获取文件的长度 
       7)public String[ ] list ( ) 将目录中全部文件名保存在字符串数组中返回。 
       File类中还定义了一些对文件或目录进行管理、操做的方法,经常使用的方法有:
       1) public boolean renameTo( File newFile );    重命名文件
       2) public void delete( );   删除文件
       3)  public boolean mkdir( ); 建立目录

例子:

public class FileDemo1 {     
      public static void main(String[] args) {    
         File file = new File("D:" + File.separator + "test.txt");     
         if (file.exists()) {     
              file.delete();    
          } else {     
             try {     
                 file.createNewFile();    
              } catch (IOException e) {    
                // TODO Auto-generated catch block     
                 e.printStackTrace();    
             }    
         }
  } }

 

9.RandomAccessFile类

该对象并非流体系中的一员,其封装了字节流,同时还封装了一个缓冲区(字符数组),经过内部的指针来操做字符数组中的数据。该对象特色:

1.    该对象只能操做文件,因此构造函数接收两种类型的参数:a.字符串文件路径;b.File对象。

2.    该对象既能够对文件进行读操做,也能进行写操做,在进行对象实例化时可指定操做模式(r,rw)

 

注意:该对象在实例化时,若是要操做的文件不存在,会自动建立;若是文件存在,写数据未指定位置,会从头开始写,即覆盖原有的内容。 能够用于多线程下载或多个线程同时写数据到文件。

十、System类对IO的支持

 针对一些频繁的设备交互,Java语言系统预约了3个能够直接使用的流对象,分别是:

·        System.in(标准输入),一般表明键盘输入。

·        System.out(标准输出):一般写往显示器。

·        System.err(标准错误输出):一般写往显示器。

 标准I/O
      Java程序可经过命令行参数与外界进行简短的信息交换,同时,也规定了与标准输入、输出设备,如键盘、显示器进行信息交换的方式。而经过文件能够与外界进行任意数据形式的信息交换。

1. 命令行参数

public class TestArgs {    
    public static void main(String[] args) {    
        for (int i = 0; i < args.length; i++) {    
            System.out.println("args[" + i + "] is <" + args[i] + ">");    
        }    
    }    
}

  运行命令:java Java C VB


运行结果:

args[0] is <Java>  
  
  
args[1] is <C>  
  
  
args[2] is <VB>

  

2. 标准输入,输出数据流

java系统自带的标准数据流:java.lang.System:

java.lang.System     
public final class System  extends Object{     
   static  PrintStream  err;//标准错误流(输出)    
   static  InputStream  in;//标准输入(键盘输入流)    
   static  PrintStream  out;//标准输出流(显示器输出流)    
}

注意:
(1)System类不能建立对象,只能直接使用它的三个静态成员。
(2)每当main方法被执行时,就自动生成上述三个对象。

1) 标准输出流 System.out

   System.out向标准输出设备输出数据,其数据类型为PrintStream。方法:

      Void print(参数)
      Void println(参数)
2)标准输入流 System.in

    System.in读取标准输入设备数据(从标准输入获取数据,通常是键盘),其数 据类型为InputStream。方法:

        int read()  //返回ASCII码。若,返回值=-1,说明没有读取到任何字节读取工做结束。
        int read(byte[] b)//读入多个字节到缓冲区b中返回值是读入的字节数
例如:

import java.io.*;    
public class StandardInputOutput {    
    public static void main(String args[]) {    
        int b;    
        try {    
            System.out.println("please Input:");    
            while ((b = System.in.read()) != -1) {    
                System.out.print((char) b);    
            }    
        } catch (IOException e) {    
            System.out.println(e.toString());    
        }    
    }    
}

等待键盘输入,键盘输入什么,就打印出什么:

3)标准错误流

   System.err输出标准错误,其数据类型为PrintStream。可查阅API得到详细说明。

    标准输出经过System.out调用println方法输出参数并换行,而print方法输出参数但不换行。println或print方法都通 太重载实现了输出基本数据类型的多个方法,包括输出参数类型为boolean、char、int、long、float和double。同时,也重载实现 了输出参数类型为char[]、String和Object的方法。其中,print(Object)和println(Object)方法在运行时将调 用参数Object的toString方法。

import java.io.BufferedReader;    
import java.io.IOException;    
import java.io.InputStreamReader;    
    
public class StandardInputOutput {    
    public static void main(String args[]) {    
        String s;    
        // 建立缓冲区阅读器从键盘逐行读入数据    
        InputStreamReader ir = new InputStreamReader(System.in);    
        BufferedReader in = new BufferedReader(ir);    
        System.out.println("Unix系统: ctrl-d 或 ctrl-c 退出"    
                + "\nWindows系统: ctrl-z 退出");    
        try {    
            // 读一行数据,并标准输出至显示器    
            s = in.readLine();    
            // readLine()方法运行时若发生I/O错误,将抛出IOException异常    
            while (s != null) {    
                System.out.println("Read: " + s);    
                s = in.readLine();    
            }    
            // 关闭缓冲阅读器    
            in.close();    
        } catch (IOException e) { // Catch any IO exceptions.    
            e.printStackTrace();    
        }    
    }    
}

在Java语言中使用字节流和字符流的步骤基本相同,以输入流为例,首先建立一个与数据源相关的流对象,而后利用流对象的方法从流输入数据,最后执行close()方法关闭流。


附加:

 IOException异常类的子类
1.public class  EOFException :   非正常到达文件尾或输入流尾时,抛出这种类型的异常。    

2.public class FileNotFoundException:   当文件找不到时,抛出的异常。

3.public class InterruptedIOException: 当I/O操做被中断时,抛出这种类型的异常。

相关文章
相关标签/搜索