Java I/O系統學習筆記(File以及Stream)

Java I/O系統學習筆記

對於程序語言的設計者來講,建立一個好的輸入、輸出(I/O)系統是一個項艱難的任務。算法

在本章學習筆記中,我將會記錄關於File、I/O流的相關學習筆記。編程

File類

File類這個名字有必定的誤導性;咱們會認爲它代指一個文件,實際上不是這樣。FilePath對這個類來講是更好的名字,由於它既能表明一個文件,也能夠表明一個文件集(文件夾),咱們能夠經過調用list()方法來獲取文件集中的文件(集)。數組

目錄列表器

FilenameFilter至關於一個過濾器,建立擴展於該接口的類的目的在於把accept()方法提供給list()使用,使list()能夠回調accept()方法,進而決定哪些文件將包含在這個數組中。演示以下:多線程

/**
 * Created by Mr.W on 2017/11/8.
 */
public class FileClass {

    public static void main(String[] args) {
       	// "."表示項目根目錄
        File file = new File("./src/com/stupidzhe/jdklearning/io/file-test");
        System.out.println("FileName: " + file.getName());
        System.out.println("isDirectory: " + file.isDirectory());
        
        // 經過lambda表達式來實例化FilenameFilter的匿名類
        String[] fileNameArray = file.list((dir, name) -> Pattern.matches("^[file].*", name));
        if (null == fileNameArray) {
            return;
        }
        for (String fileName : fileNameArray) {
            System.out.println("fileName: " + fileName + " " + fileName.hashCode());
        }
        // 這裏經過hashCode大小來排序
		Arrays.sort(fileNameArray, ((o1, o2) -> (o1.hashCode() > o2.hashCode()?-1:1)));
        for (String fileName : fileNameArray) {
            System.out.println("fileName: " + fileName + " " + fileName.hashCode());
        }
    }
}

--------------output--------------
FileName: file-test
isDirectory: true
fileName: file1.c -855046422
fileName: file2.c -855045461
fileName: file3.c -855044500
fileName: file4.c -855043539
----------after sort--------
fileName: file4.c -855043539
fileName: file3.c -855044500
fileName: file2.c -855045461
fileName: file1.c -855046422
-----------------------------------

這種結構經常被稱爲「回調」。更具體地說,這是一個策略模式的例子,由於list()實現了基本的功能,並且按照FilenameFilter的形式提供了這個策略,以完善list()在提供服務時鎖需的算法。編程語言

策略的目的就是提供了代碼行爲的靈活性。學習

目錄的檢查和建立

由於File類不只僅只表明存在的文件或目錄。也能夠用File對象來建立新的目錄或尚不存在的這個目錄路徑。咱們還能夠查看文件的特性(如:大小,最後修改日期,讀/寫),檢查某個File對象表明的是一個文件仍是一個目錄,並能夠刪除文件。下面是例子:this

public class FileClass {

    public static void main(String[] args) {
        File file = new File("./src/com/stupidzhe/jdklearning/io/file-test/t");
        if (!file.exists()) {
            System.out.println("this is not a file");
            return;
        }
        System.out.println("is Directory: " + file.isDirectory());
        System.out.println("is File: " + file.isFile());
        System.out.println("its parent: " + file.getParent());
        System.out.println("can write: " + file.canWrite());
        System.out.println("can read: " + file.canRead());
        System.out.println("absolute path: " + file.getAbsolutePath());
        // 返回最近一次修改的時間戳
        System.out.println("last modify: " + file.lastModified());
        if (!file.delete()) {
            System.out.println("delete file fail");
            return;
        }
        file = new File("./src/com/stupidzhe/jdklearning/io/file-test/t");
        if (file.exists()) {
            return;
        }
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("create dir: " + file.mkdir());
        
		 // 能夠對文件(集)的位置和名稱進行修改
		 //file.renameTo(new File("./src/com/stupidzhe/jdklearning/io/file-test/k"));

    }
}

--------------output-----------
is Directory: true
is File: false
its parent: ./src/com/stupidzhe/jdklearning/io/file-test
can write: true
can read: true
absolute path: /Users/x72/Downloads/jdk-test/./src/com/stupidzhe/jdklearning/io/file-test/t
last modify: 1510140825000
create dir: true
-------------------------------

輸入和輸出

編程語言的I/O類庫中常使用這個抽象概念,它表明任何有能力產出數據的數據源對象或是有能力接收數據的接收端對象。「流」屏蔽了實際的I/O設備中處理數據的細節。線程

Java類庫中的I/O類分紅輸入和輸出兩部分,能夠在JDK文檔裏的類層次中查看到。經過繼承,任何自InputStream或Reader派生而來的類都含有名爲read()的基本方法,用於讀取單個字節或者字節數組。一樣,任何自OutputStream或Write派生而來的類都含有名爲write()的基本方法,用於寫單個字節或者字節數組。可是,咱們一般不會用到這些方法,它們之因此存在是由於別的類可使用它們,以便提供更有用的接口。所以,咱們不多使用單一的類來建立流對象。設計

InputStream類型

InputStream的做用是用來表示那些從不一樣數據源產生輸入的類,這些數據包括:code

  • 字節數組
  • String對象
  • 文件
  • 「管道」,工做方式和實際管道類似,即從一端輸入,從另外一端輸出
  • 一個由其餘種類的流組成的序列,以便咱們能夠將它們收集合併到一個流中
  • 其餘數據源,如Internet連接等

每一種數據源都有相應的InputStream子類,另外,FilterInputStream也屬於一種InputStream,爲裝飾器類提供基類,其中,裝飾類能夠把屬性或有用的接口與輸入流鏈接起來。

下面的表格是InputStream類型的實現:

<table> <thead> <tr> <th>類</th> <th align="left">功能</th> <th align="left">構造器參數</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>ByteArrayInputStream</td> <td align="left">容許將內存的緩衝區看成InputStream使用</td> <td align="left">緩衝區,字節將從中取出</td> <td align="left">做爲一種數據源:將其與FilterInputStream對象相連以提供有用接口</td> </tr> <tr> <td>StringBufferInputStream</td> <td align="left">將String轉換成InputStream</td> <td align="left">字符串。底層實現實際使用StringBuffer</td> <td align="left">做爲一種數據源:將其與FilterInputStream對象相連以提供有用接口</td> </tr> <tr> <td>FileInputStream</td> <td align="left">用於從文件讀取信息</td> <td align="left">字符串,表示文件名、文件或FileDescriptor對象</td> <td align="left"></td> </tr> <tr> <td>PipedInputStream</td> <td align="left">產生用於寫入相關PipedOutputStream的數據。實現「管道化」概念</td> <td align="left">PipedOutputStream</td> <td align="left">做爲多線程中的數據源:將其與FilterInputStream對象相連以提供有用接口</td> </tr> <tr> <td>SequenceInputStream</td> <td align="left">將兩個或多個InputStream對象轉換成單一InputStream</td> <td align="left">兩個InputStream對象或一個容納InputStream對象的容器Enumeration</td> <td align="left">做爲一種數據源:將其與FilterInputStream對象相連以提供有用接口</td> </tr> <tr> <td>FilterInputStream</td> <td align="left">抽象類,做爲「裝飾器」的接口。其中,「裝飾器」爲其餘的InputStream類提供有用功能。</td> <td align="left"></td> <td align="left"></td> </tr> </tbody></table>

OutputStream

該類別的類決定了輸出所要去往的目標:字節數組、文件、管道。

另外,FilterOutputStream爲「裝飾器」類提供了一個基類,「裝飾器」類把屬性或者有用的接口和輸出流鏈接了起來。

下面是繼承OutputStream的類型:

<table> <thead> <tr> <th>類</th> <th align="left">功能</th> <th align="left">構造器參數</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>ByteArrayOutputStream</td> <td align="left">在內存中建立緩衝區。全部送往「流」的數據都要放置在此緩衝區</td> <td align="left">緩衝區初始化尺寸(可選的)</td> <td align="left">用於指定數據的目的地:將其與FilterOutputStream對象相連以提供有用接口</td> </tr> <tr> <td>FileOutputStream</td> <td align="left">用於將信息寫至文件</td> <td align="left">字符串,表示文件名、文件或者FileDescriptor對象</td> <td align="left">指定數據的目的地:將其與FilterOutputStream對象相連以提供有用接口</td> </tr> <tr> <td>PipedOutputStream</td> <td align="left">任何寫入其中的信息都會自動做爲相關PipedInputStream的輸出。實現「管道化」概念</td> <td align="left">PipedInputStream</td> <td align="left">指定用於多線程的數據的目的地:將其與FilterOutputStream對象相連以提供有用接口</td> </tr> <tr> <td>FilterOutputStream</td> <td align="left">抽象類,做爲「裝飾器」的接口。其中,「裝飾器」爲其餘的InputStream類提供有用功能。</td> <td align="left"></td> <td align="left"></td> </tr> </tbody> </table>

關於FilterInputStream、FilterOutputStream

FilterInputStream類可以完成兩件徹底不一樣的事情,其中,DataInputStream容許咱們讀取不一樣的基本類型數據以及String對象(全部方法都以「read」開頭,例如readByte()、readFloat()等等)。搭配相應的FilterOutputStream,咱們能夠經過數據「流」將基本類型的數據從一個地方遷移到另外一個地方。具體是那些「地方」是由InputStream類型的實現的表格決定。

其餘FilterInputStream類則在內部修改InputStream的行爲方式:是否緩衝,是否保留它所讀過的行(容許咱們查詢行數或設置行數),以及是否把單一字符推回輸入流等等。

下面是FilterInputStream的實現類:

<table> <thead> <tr> <th>類</th> <th align="left">功能</th> <th align="left">構造器參數</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>DataInputStream</td> <td align="left">與DataOutputStream搭配使用,所以咱們能夠按照可移植方式從流讀取基本數據類型(int,char,long等)</td> <td align="left">InputStream</td> <td align="left">包含用於讀取基本類型數據的所有接口</td> </tr> <tr> <td>BufferedInputStream</td> <td align="left">使用它能夠防止每次讀取時都得進行實際寫操做。表明「使用緩衝區」</td> <td align="left">InputStream,能夠指定緩衝區大小(可選的)</td> <td align="left">本質上不提供接口,只不過是向進程中添加緩衝區所必須的。與接口對象搭配</td> </tr> <tr> <td>LineNumberInputStream</td> <td align="left">跟蹤輸入流中的行號;可調用getLineNumber()和setLineNumber(int)</td> <td align="left">InputStream</td> <td align="left">僅增長了行號,所以可能要與接口對象搭配使用</td> </tr> <tr> <td>PushbackInputStream</td> <td align="left">具備「能彈出一個字節的緩衝區」。所以能夠將讀到的最後一個字符回退</td> <td align="left">InputStream</td> <td align="left">一般做爲編譯器的掃描器,之因此包含在內是由於Java編譯器的須要,咱們基本上用不到</td> </tr> </tbody></table>

與DataInputStream對應的是DataOutputStream,它能夠將各類基本數據類型以及String對象格式化輸出到流中;這樣一來,任何機器的任何DataInputStream都能讀取他們。因此方法都以「write」開頭。

PrintStream最初的目的即是爲了以可視化格式打印全部的基本數據類型以及String對象。這和DataOutputStream不一樣,後者的目的是將數據元素置入「流」中,使DataInputStream可以可移植地重構他們

PrintStream內有兩個重要的方法:print()、println()。對他們進行了重載,以即可以打印出各類數據類型。

DataInputStream的實現類:

<table> <thead> <tr> <th>類</th> <th align="left">功能</th> <th align="left">構造器參數</th> <th align="left">如何使用</th> </tr> </thead> <tbody><tr> <td>DataOutputStream</td> <td align="left">與DatInputStream搭配使用,所以咱們能夠按照可移植方式從流讀取基本數據類型(int,char,long等)</td> <td align="left">OutputStream</td> <td align="left">包含用於寫入基本類型數據的所有接口</td> </tr> <tr> <td>PrintStream</td> <td align="left">用於產生格式化輸出。其中OutputStream處理數據的存儲,PrintStream處理顯示</td> <td align="left">OutputStream,能夠用boolean值指示是否每次換行時狀況緩衝區(可選的)應該是對OutputStream對象的「final」封裝。可能會常用到它</td> <td align="left"></td> </tr> <tr> <td>BufferedOutputStream</td> <td align="left">使用它以免每次發送數據時都要進行實際的寫操做。表明「使用緩衝區」。能夠調用flush()清空緩衝區</td> <td align="left">OutputStream,能夠指定緩衝區大小(可選的)</td> <td align="left">本質上並不提供接口,只不過向進程中添加緩衝區所必需的。與接口對象搭配</td> </tr> </tbody></table>

Reader和Writer

當咱們初次看到Reader和Writer時,可能會覺得這是兩個用來代替InputStream和OutputStream的類;但實際上並不是如此。

Reader和Writer則提供兼容Unicode與面向字符的I/O功能, 另外:

有時咱們必須把來自「字節」層次結構中的類和「字符」層次結構中的類結合起來使用。爲了這個目的,要用到適配器類:InputStreamReader能夠把InputStream轉化爲Reader,OutputStreamReader能夠把OutputStream轉化爲Writer。

相關文章
相關標籤/搜索