1. 程式人生 > >JavaIO流分類詳解與常用流用法例項

JavaIO流分類詳解與常用流用法例項

Java流概念:

Java把所有的有序資料都抽象成流模型,簡化了輸入輸出,理解了流模型就理解了Java IO。可以把流想象成水流,裡面的水滴有序的朝某一方向流動。水滴就是資料,且代表著最小的資料流動單位,在位元組流中,水滴就是一位元組(byte),在字元流中,水滴就是一字元(char)。

Java流的分類方法大致分為以下幾種:

1、按流向劃分,分為輸入流、輸出流

請注意,這裡的流向是以程式的執行時記憶體為參照的。
輸入流類名中包含關鍵字InputStream或Reader,輸出流類名中包含關鍵字OutputStream或Writer。

2、按操作的資料單元型別劃分,分為位元組流、字元流

位元組流操作的資料單元是8位的位元組(byte),字元流操作的是16位的字元。
位元組流類名中包含關鍵字InputStream或OutputStream,字元流類名中包含關鍵字Reader或Writer。
請注意,系統輸入輸出(System.in與System.out)都為位元組流。

3、按流的角色來劃分,分為節點流與處理流

節點流是指程式可以向一個特定的節點讀寫資料,直接連線資料來源;
這個節點最常見的是檔案,類名中包含關鍵字File;還可以是陣列、管道、字串,關鍵字分別為ByteArray/CharArray,Piped,String。

處理流並不直接連線資料來源,它大多情況是對已存在的節點流進行包裝,是一種典型的裝飾器設計模式。使用處理流主要是為了更方便的執行輸入輸出工作,如PrintStream,輸出功能很強大,推薦輸出時都使用處理流包裝。

注意:一個IO流可以即是輸入流又是位元組流又或是以其他方式分類的流型別,是不衝突的。比如FileInputStream,它既是輸入流又是位元組流還是檔案節點流。

4、一些特別的的流型別

轉換流,轉換流只有位元組流轉換為字元流,因為字元流使用起來更方便,我們只會向更方便使用的方向轉化。如:InputStreamReader與OutputStreamWriter。

緩衝流,有關鍵字Buffered,也是一種處理流,為其包裝的流增加了快取功能,提高了輸入輸出的效率,增加緩衝功能後需要使用flush()才能將緩衝區中內容寫入到實際的物理節點。但是,在現在版本的Java中,只需記得關閉輸出流(呼叫close()方法),就會自動執行輸出流的flush()方法,可以保證將緩衝區中內容寫入。

物件流,有關鍵字Object,主要用於將目標物件儲存到磁碟中或允許在網路中直接傳輸物件時使用(物件序列化),具體可參看部落格Java序列化與反序列化

推回輸入流,有關鍵字PushBack,當程式呼叫推回輸入流的unread()方法時,系統回把指定陣列內容的內容推回到一個推回緩衝區中,在呼叫read()方法讀入內容時,就先從推回緩衝區中讀取,直到讀完推回緩衝區中內容後才會從原輸入流中讀取。

必須要掌握的流用法例項:
1、FileInputStream\FileOutputStream\FileReader\FileWriter(使用方法類似)

//檔案位元組輸入流FileInputStream用法
public class TestFileIO1 {
    public static void main(String[] args)throws IOException{
        //此處路徑可以使用相對路徑與絕對路徑
        FileInputStream fileInputStream = new FileInputStream("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO1.java");
        //一個位元組陣列作為緩衝,意為每次讀取1024個位元組,提高效率
        byte[] buffer = new byte[1024];
        //記錄讀取的位元組數
        int hasRead = 0;
        //呼叫read()方法,返回實際讀取的位元組數
        while((hasRead = fileInputStream.read(buffer)) > 0){
            System.out.print(new String(buffer, 0, hasRead));
        }
        //關閉流
        fileInputStream.close();
    }
}
//檔案字元輸入流FileReader用法
public class TestFileIO2 {
    public static void main(String[] args)throws IOException{
        FileReader fileReader = new FileReader("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO2.java");
        char[] buffer = new char[32];
        int hasRead = 0;
        while((hasRead = fileReader.read(buffer)) > 0){
            System.out.print(new String(buffer, 0, hasRead));
        }
        fileReader.close();
    }
}
//檔案位元組輸入流FileInputStream與檔案位元組輸出流FileOutputStream結合
public class TestFileIO3 {
    public static void main(String[] args)throws IOException{
        File result = new File("output.txt");
        FileInputStream fileInputStream = new FileInputStream("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO3.java");
        FileOutputStream fileOutputStream = new FileOutputStream(result);
        byte[] buffer = new byte[1024];
        int hasRead = 0;
        while((hasRead  = fileInputStream.read(buffer)) > 0){
            fileOutputStream.write(buffer, 0, hasRead);
        }
        System.out.println(result.getAbsolutePath());
        fileInputStream.close();
        fileOutputStream.close();
    }
}
//檔案字元輸出流FileWriter用法
public class TestFileIO4 {
    public static void main(String[] args)throws IOException{
        File result = new File("output.txt");
        FileWriter fileWriter = new FileWriter(result);
        fileWriter.write("飛流直下三千尺,\r\n");
        fileWriter.write("疑是銀河落九天.\r\n");
        fileWriter.close();
    }
}

2、輸出處理流PrintStream用法

public class TestFileIO5 {
    public static void main(String[] args)throws IOException{
            File result = new File("output.txt");
        FileOutputStream fileOutputStream = new FileOutputStream(result);
        //PrintStream處理流功能極其強大,所有位元組輸出流都應使用PrintStream包裝
        PrintStream printStream = new PrintStream(fileOutputStream);
        printStream.println("床前明月光,");
        fileOutputStream.close();
        printStream.close();
    }
}

3、轉換流inputStreamReader與緩衝流BufferedReader用法

public class TestFileIO6 {
    public static void main(String[] args)throws IOException{
        //系統輸入為System.in,預設為從鍵盤輸入,是位元組輸入流InputStream型別
        //使用轉換流將InputStream轉換為Reader字元輸入流物件
        InputStreamReader inputStreamReader = new InputStreamReader(System.in);
        //將Reader包裝為字元快取處理流BufferedReader物件
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        //定義快取行
        String bufferString = null;
        //使用BufferedReader特色readLine()方法逐行讀取輸入
        while((bufferString = bufferedReader.readLine()) != null){
            //直到輸入exit,停止程式
            if(bufferString.equals("exit")){
                System.exit(0);
            }
            //控制檯輸出輸入內容
            System.out.println("輸入內容為:" + bufferString);
        }
    }
}

4、推回輸入流PushbackInputStream

//通過一個返回某個字串之前的檔案內容的demo,理解推回輸入流的讀取快取機制
public class TestFileIO7 {
    public static void main(String[] args)throws IOException{
        //建立一個推回位元組輸入流物件,指定緩衝區為64
        PushbackReader pushbackInputStream = new PushbackReader(new FileReader("D:\\Git\\TCCP\\IO\\src\\package1\\TestFileIO7.java"), 64);
        char[] buffer = new char[32];
        //記錄上次讀取的字串
        String lastContent = "";
        int hasRead = 0;
        while((hasRead = pushbackInputStream.read(buffer)) > 0){
            //將讀取的字元轉換為字串
            String content = new String(buffer, 0, hasRead);
            int targetIndex = 0;
            //將上次讀取的字串和本次讀取的字串拼接
            //查詢拼接後的字串是否包含"new PushbackReader"(檔案為此段原始碼),返回位置由targetIndex記錄
            if((targetIndex = (lastContent + content).indexOf("targetIndex")) > 0){
                //將拼接後字串轉化成字元陣列後推回緩衝區
                String newContent = lastContent + content;
                pushbackInputStream.unread(newContent.toCharArray());
                //定義一個長度為targetIndex的char陣列,如果新大小大於32,則需要重新定義
                if(targetIndex > 32){
                    buffer = new char[targetIndex];
                }
                //再次讀取targetIndex長度的內容,其實就是目標字串之前的內容
                pushbackInputStream.read(buffer, 0, targetIndex);
                //輸出結果
                System.out.println(new String(buffer, 0, targetIndex));
                //退出程式
                System.exit(0);
            }
        }
    }
}

5、重定向標準輸入\輸入
Java的標準輸入為System.in預設為鍵盤輸入,標準輸入為System.out預設為螢幕輸出。可通過setInt(InputStream in)方法與setOut(PrintStream out)方法修改(在這裡,連標準輸出的位元組輸出流都被包裝成了PrintStream,我們在程式設計時有什麼理由不適用輸出流呢?)。

public class TestFileIO8 {
    public static void main(String[] args)throws IOException{
        FileInputStream fileInputStream = new FileInputStream("input.txt");
        //重定向預設輸入
        System.setIn(fileInputStream);
        //一次性建立PrintStream輸出流物件(先建立檔案位元組輸出流物件,再包裝)
        PrintStream printStream = new PrintStream(new FileOutputStream("output.txt"));
        //重定向預設輸出
        System.setOut(printStream);
        //獲取System.in(input.txt檔案中)的輸入
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNext()){
            //下面這段標準輸出會輸出在Output.txt中
            System.out.println("輸入的內容為:" + scanner.next());
        }
}

6、物件處理流ObjectInputStream\ObjectOutputStream
物件要想儲存在磁碟或在網路上傳輸,其實體類必須可序列化,它是將物件轉化為位元組序列,使其可以離線執行。
要想實現序列化,實體類必須實現java.io.serializable介面。

//建立一個可序列化的實體類
public class Person implements Serializable{

    private String username;
    private int age;
    public Person(String username, int age){
        this.username = username;
        this.age = age;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class TestObjectIO {
    public static void main(String[] args) throws Exception{
        //ObjectOutputStream是一個處理流,必須建立在節點流上才能工作
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("output.txt"));
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("output.txt"));
        objectOutputStream.writeObject(new Person("Leeon", 21));
        Person person = (Person)objectInputStream.readObject();
        System.out.println(person.getUsername() + person.getAge());
        objectInputStream.close();
        objectOutputStream.close();
    }
}