搞定java.io


1、java.io簡介

  Java的核心庫java.io提供了全面的IO接口。包括:文件讀寫、標準設備輸出等。Java中IO是以流爲基礎進行輸入輸出的,全部數據被串行化寫入輸出流,或者從輸入流讀入。java

  java.io經過數據流、序列化和文件系統提供系統輸入和輸出。數組


2、先說文件操做

文件File 位於java.io包中,用於描述文件和目錄的操做。
File類這個名字有必定的誤導性;咱們可能會認爲它指代的是文件,但並不是如此。它既能表明一個特定文件的名稱,又能表明一個目錄下的一組文件的名稱。實際上FilePath(文件路徑)對這個類來講是個更好的名字。
建立文件對象以下:網絡

File file = new File("input.txt");//文件位於當前目錄下
File file = new File("/home/user","input.txt");//文件位於/home/user目錄下

除了上述的File構造方法以外,還有不少和File相關的方法以下:dom

exists()                                  判斷文件或目錄是否存在
mkdir()                                          建立目錄
isFile()/isDirectory()                    判斷是文件仍是目錄
delete()                                         刪除文件
getPath()                                 獲取文件或者目錄的路徑
list()                                    將目錄中全部文件名保存在字符串數組中返回

例 文件相關操做測試

import java.io.*;
public class TestAbstract {  
    public static void main(String args[]) throws IOException {  
        File dir = new File("D:/java");  
        File file1 = new File(dir, "fileTest001.txt");  
        File file2 = new File(dir, "fileTest002.java");   
        if (!dir.exists())  
            dir.mkdir();  
        if (!file1.exists())  
            file1.createNewFile();  
        if (!file2.exists())  
            file2.createNewFile();  
        System.out.println("file1's AbsolutePath=  " + file1.getAbsolutePath());  
        System.out.println("file2's AbsolutePath=  " + file2.getAbsolutePath());  
        System.out.println("file1 Canread=" + file1.canRead());  
        System.out.println("file1's len= " + file1.length());  
        String[] fileList = dir.list(); 

        System.out.println("there are " + fileList.length + " file(s) in D:");  
    }  

}

運行結果:
D:\java>java TestAbstract
file1's AbsolutePath=  D:\java\fileTest001.txt
file2's AbsolutePath=  D:\java\fileTest002.java
file1 Canread=true
file1's len= 0
there are 133 file(s) in D:

例 刪除目錄及文件編碼

import java.io.File;
 
public class DeleteFileDemo {
  public static void main(String args[]) {
      // 這裏修改成本身的測試目錄
    File folder = new File("/tmp/java/");
    deleteFolder(folder);
  }
 
  //刪除文件及目錄
  public static void deleteFolder(File folder) {
    File[] files = folder.listFiles();
        if(files!=null) { 
            for(File f: files) {
                if(f.isDirectory()) {
                    deleteFolder(f);
                } else {
                    f.delete();
                }
            }
        }
        folder.delete();
    }
}

3、什麼是流?

概念:

  流是一個很形象的概念,當程序須要讀取數據的時候,就會開啓一個通向數據源的流,這個數據源能夠是文件,內存,或是網絡鏈接。相似的,當程序須要寫入數據的時候,就會開啓一個通向目的地的流。這時候你就能夠想象數據好像在這其中「流」動同樣。
  在計算機中,I/O流通常就是指輸入輸出過程當中傳輸的的數據序列,是一個抽象的概念,即在輸入輸出設備之間的傳輸,一種有順序的,有起點和終點的字節集合。
設計




  流具備方向性,至因而輸入流仍是輸出流則是一個相對的概念,通常以程序爲參考,若是數據的流向是程序至設備,咱們成爲輸出流,反之咱們稱爲輸入流。
  Java把這些不一樣來源和目標的數據都統一抽象爲數據流。Java語言的輸入輸出功能是十分強大而靈活的,美中不足的是看上去輸入輸出的代碼並非很簡潔,由於你每每須要包裝許多不一樣的對象。
  在Java類庫中,IO部分的內容是很龐大的,由於它涉及的領域很普遍:標準輸入輸出,文件的操做,網絡上的數據流,字符串流,對象流,zip文件流。

4、流有哪些分類?

按數據傳輸單位分:
字節流: 以字節爲單位傳輸數據的流
字符流: 以字符爲單位傳輸數據的流
按流向分:
輸入流: 程序能夠從中讀取數據的流。(由InputStream(字節流)和Reader(字符流)做爲基類)
輸出流: 程序能向其中寫入數據的流。(由OutputStream(字節流)和Writer(字符流)做爲基類)
按功能分:
節點流: 用於直接操做目標設備的流;節點流從一個特定的數據源讀寫數據,即節點流是直接操做文件,網絡等的流,例如FileInputStream和FileOutputStream,他們直接從文件中讀取或往文件中寫入字節流。
code

節點流

過濾流: 是對一個已存在的流的連接和封裝,經過對數據進行處理爲程序提供功能強大、靈活的讀寫功能。
過濾流

例如:

public static void main(String[] args) throws IOException {
            // 節點流FileOutputStream直接以A.txt做爲數據源操做
            FileOutputStream fileOutputStream = new FileOutputStream("A.txt");
            // 過濾流BufferedOutputStream進一步裝飾節點流,提供緩衝寫
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
                    fileOutputStream);
            // 過濾流DataOutputStream進一步裝飾過濾流,使其提供基本數據類型的寫
            DataOutputStream out = new DataOutputStream(bufferedOutputStream);
            out.writeInt(3);
            out.writeBoolean(true);
            out.flush();
            out.close();
            // 此處輸入節點流,過濾流正好跟上邊輸出對應,讀者可觸類旁通
            DataInputStream in = new DataInputStream(new BufferedInputStream(
                    new FileInputStream("A.txt")));
            System.out.println(in.readInt());
            System.out.println(in.readBoolean());
            in.close();
    }

5、流結構介紹

JDK所提供的全部流類位於java.io包中,都分別繼承自如下四種抽象流類。視頻

字節流 字符流
輸入流 InputStream Reader
輸出流 OutputStream Writer

InputStream:繼承自InputStream的流都是用於向程序中輸入數據的,且數據單位都是字節(8位)。
OutputStream:繼承自OutputStream的流都是程序用於向外輸出數據的,且數據單位都是字節(8位)。
Reader:繼承自Reader的流都是用於向程序中輸入數據的,且數據單位都是字符(16位)。
Writer:繼承自Writer的流都是程序用於向外輸出數據的,且數據單位都是字符(16位)。對象

提示:在最底層,全部的輸入/輸出都是字節形式的。基於字符的流只爲處理字符提供方便有效的方法。

字節流

字節流的最頂層是兩個抽象類:InputStream和OutputStream,其餘關於處理字節的類都是它們的子類,這些子類對不一樣的外設進行處理,例如磁盤文件,網絡鏈接,甚至是內存緩衝區。

類名                                             含義
BufferedInputStream                             緩衝輸入流
BufferedOutputStream                            緩衝輸出流
ByteArrayInputStream                       從字節數組讀取的輸入流
ByteArrayOutputStream                      向字節數組寫入的輸出流
DataInputStream                            包含讀取Java標準數據類型方法的輸入流
DataOutputStream                           包含編寫Java 標準數據類型方法的輸出流
FileInputStream                            讀取文件的輸入流
FileOutputStream                           寫文件的輸出流
FilterInputStream                          實現 InputStream
FilterOutputStream                         實現 OutputStream
InputStream                                描述流輸入的抽象類
OutputStream                               描述流輸出的抽象類
PipedInputStream                           輸入管道
PipedOutputStream                          輸出管道
PrintStream                                包含print( ) 和 println( )的輸出流
PushbackInputStream                        支持向輸入流返回一個字節的單字節的「unget」的輸入流
RandomAccessFile                           支持隨機文件輸入/輸出
SequenceInputStream                        兩個或兩個以上順序讀取的輸入流組成的輸入流

抽象類InputStream 和 OutputStream中定義了實現其餘流類的關鍵方法read()和write(),它們分別對數據的字節進行讀寫。兩種方法都是抽象方法,被子類重載。

繼承自InputStream/OutputStream的流都是用於向程序中輸入/輸出數據,且數據的單位都是字節(byte=8bit),如圖,深色的爲節點流,淺色的爲過濾流。

 

例:文件按字節流的方式拷貝

import java.io.*;
//文件按字節流的方式拷貝
public class Main {

    public static void main(String[] args) throws IOException {
        FileInputStream in = null;
        FileOutputStream out = null;
        try{
            in = new FileInputStream("input.txt");
            out = new FileOutputStream("output.txt");
            int c = 0;
            while((c = in.read())!=-1)
            {
                out.write(c);
            }
        }finally{
            if(in != null)
            {
                in.close();
            }
            if(out != null)
            {
                out.close();
            }
        }
    }
}

上例中使用的是文件名來建立FileInoutStream和FileOutputStream,實際上能夠還可使用文件對象來建立輸入輸出流。字節流的每次操做都是一個數據單位——字節,假如input.txt文件中包含 Hello world ,那麼它將複製完「H」以後,再複製「e」,接着就是「l」,如此類推直到其結束。in.read()每次從輸入流中讀取一個字節,若是達到文件末尾就返回-1。使用完了,還要關閉這些字節流,調用close()方法。

字符流

java是使用16-bits來存儲字符數據的,涉及到的大可能是字符操做,在程序中使用字符流會比字節流更加合適。相似於字節流,字符流的兩個頂層抽象類是Reader和Writer,一下是它們的子類處理字符流。

類名                                             含義
BufferedReader                                 緩衝輸入字符流
BufferedWriter                                 緩衝輸出字符流
CharArrayReader                                從字符數組讀取數據的輸入流
CharArrayWriter                                向字符數組寫數據的輸出流
FileReader                                     讀取文件的輸入流
FileWriter                                     寫文件的輸出流
FilterReader                                   過濾讀
FilterWriter                                   過濾寫
InputStreamReader                              把字節轉換成字符的輸入流
LineNumberReader                               計算行數的輸入流
OutputStreamWriter                             把字符轉換成字節的輸出流
PipedReader                                    輸入管道
PipedWriter                                    輸出管道
PrintWriter                                    包含print( )和println( )的輸出流
PushbackReader                                 容許字符返回到輸入流的輸入流
Reader                                         描述字符流輸入的抽象類
StringReader                                   讀取字符串的輸入流
StringWriter                                   寫字符串的輸出流
Writer                                         描述字符流輸出的抽象類

相似於字節,字符的抽象類Reader和 Writer中也定義了關鍵方法read()和write(),它們分別對字符進行讀寫。兩種方法也都是抽象方法,被子類重載。

繼承自Reader/Writer的流都是用於向程序中輸入/輸出數據,且數據的單位都是字符(2byte=16bit),如圖,深色的爲節點流,淺色的爲過濾流。



例:文件按字符流的形式拷貝

import java.io.*;
public class CopyFileCharacter {
   public static void main(String args[]) throws IOException
   {
      FileReader in = null;
      FileWriter out = null;
      try {
         in = new FileReader("input.txt");
         out = new FileWriter("output.txt");

         int c;
         while ((c = in.read()) != -1) {
            out.write(c);
         }
      }finally {
         if (in != null) {
            in.close();
         }
         if (out != null) {
            out.close();
         }
      }
   }
}

對比發現,只有聲明的I/O流的類名不一樣,這裏使用的FileReader和FileWriter,它們操做的最小單位是一個字符16bits,而FileInputStream和FileOutputStream最小單位則是一個字節8bits.

Java自定義的標準流

標準輸入流 System.in 讀取標準輸入設備數據,例如鍵盤輸入(默認),其類型是InputStream,三個重要的讀入方法:

int read()  從輸入流中讀取數據的下一個字節,返回ASCII碼。若,返回值=-1,說明沒有讀取到任何字節讀取工做結束
int read(byte[] b)      從輸入流中讀取必定數量的字節,並將這些數據存儲到緩衝區數組b中
int read(byte[] b, int off, int len)      將輸入流中最多len個字節讀入到字節數組b中

標準輸出流 System.out 向標準的輸出設備寫入數據,默認狀況下指控制檯,其類型是PrintStream,包含兩個重要的方法:print()(不換行)和println()(輸出以後換行)

標準錯誤流 System.err 默認也是控制檯,類型和System.out相同是PrintStream,

提示:這些流都有默認的設備,但它們能夠重定向到任何兼容的輸入/輸出設備。

6、何時用字符流,何時用字節流,傻傻分不清?

InputStream 和OutputStream,兩個是爲字節流設計的,主要用來處理字節或二進制對象,
Reader和 Writer.兩個是爲字符流(一個字符佔兩個字節)設計的,主要用來處理字符或字符串.

字符流處理的單元爲2個字節的Unicode字符,操做字符、字符數組或字符串,
字節流處理單元爲1個字節,操做字節和字節數組。
因此字符流是由Java虛擬機將字節轉化爲2個字節的Unicode字符爲單位的字符而成的,
因此它對多國語言支持性比較好!
若是是音頻文件、圖片、歌曲,就用字節流好點,
若是是關係到中文(文本)的,用字符流好點

全部文件的儲存是都是字節(byte)的儲存,在磁盤上保留的並非文件的字符而是先把字符編碼成字節,再儲存這些字節到磁盤。在讀取文件(特別是文本文件)時,也是一個字節一個字節地讀取以造成字節序列
字節流可用於任何類型的對象,包括二進制對象,而字符流只能處理字符或者字符串;
字節流提供了處理任何類型的IO操做的功能,但它不能直接處理Unicode字符,而字符流就能夠
字節流是最基本的,全部的InputStrem和OutputStream的子類都是,主要用在處理二進制數據,它是按字節來處理的
但實際中不少的數據是文本,
又提出了字符流的概念,
它是按虛擬機的encode來處理,也就是要進行字符集的轉化
這兩個之間經過 InputStreamReader,OutputStreamWriter來關聯,
其實是經過byte[]和String來關聯
在實際開發中出現的漢字問題實際上都是在字符流和字節流之間轉化不統一而形成的

Reader類的read()方法返回類型爲int :做爲整數讀取的字符(佔兩個字節共16位),範圍在 0 到 65535 之間 (0x00-0xffff),若是已到達流的末尾,則返回 -1

inputStream的read()雖然也返回int,但因爲此類是面向字節流的,一個字節佔8個位,因此返回 0 到 255 範圍內的 int 字節值。若是由於已經到達流末尾而沒有可用的字節,則返回值 -1。所以對於不能用0-255來表示的值就得用字符流來讀取!好比說漢字.

字節流和字符流的主要區別是什麼呢?

  1. 字節流在操做時不會用到緩衝區(內存),是直接對文件自己進行操做的。而字符流在操做時使用了緩衝區,經過緩衝區再操做文件。
  2. 在硬盤上的全部文件都是以字節形式存在的(圖片,聲音,視頻),而字符值在內存中才會造成。

上面兩點能說明什麼呢?
針對第一點,
  咱們知道,若是一個程序頻繁對一個資源進行IO操做,效率會很是低。此時,經過緩衝區,先把須要操做的數據暫時放入內存中,之後直接從內存中讀取數據,則能夠避免屢次的IO操做,提升效率
針對第二點,
  真正存儲和傳輸數據時都是以字節爲單位的,字符只是存在與內存當中的,因此,字節流適用範圍更爲寬廣

7、經常使用流類介紹

節點流類型常見的有:

對文件操做的字符流有FileReader/FileWriter,字節流有FileInputStream/FileOutputStream。

過濾流類型常見的有:

緩衝流:緩衝流要「套接」在相應的節點流之上,對讀寫的數據提供了緩衝的功能,提升了讀寫效率,同時增長了一些新的方法。

  字節緩衝流有BufferedInputStream/BufferedOutputStream;
  字符緩衝流有BufferedReader/BufferedWriter,字符緩衝流分別提供了讀取和寫入一行的方法ReadLine和NewLine方法。

  對於輸出地緩衝流,寫出的數據,會先寫入到內存中,再使用flush方法將內存中的數據刷到硬盤。因此,在使用字符緩衝流的時候,必定要先flush,而後再close,避免數據丟失。

轉換流:用於字節數據到字符數據之間的轉換。

  僅有字符流InputStreamReader/OutputStreamWriter。其中,InputStreamReader須要與InputStream「套接」,OutputStreamWriter須要與OutputStream「套接」。

數據流:提供了讀寫Java中的基本數據類型的功能。

  DataInputStream和DataOutputStream分別繼承自InputStream和OutputStream,須要「套接」在InputStream和OutputStream類型的節點流之上。

對象流:用於直接將對象寫入寫出。

  流類有ObjectInputStream和ObjectOutputStream,自己這兩個方法沒什麼,可是其要寫出的對象有要求,該對象必須實現Serializable接口,來聲明其是能夠序列化的。不然,不能用對象流讀寫。

  還有一個關鍵字比較重要,transient,因爲修飾實現了Serializable接口的類內的屬性,被該修飾符修飾的屬性,在以對象流的方式輸出的時候,該字段會被忽略。

相關文章
相關標籤/搜索