1. 程式人生 > >java輸入與輸出流

java輸入與輸出流

概述

從14年10月份開始工作,到今天做Android已經兩年半了。可是到現在也沒搞清楚Java的I/O機制,痛定思痛,覺得好好整理一下。古人云“格物致知”,今天就好好格一格I/O機制吧!

常見問題

什麼是流

“流”是一連串流動的字元,同時也說明了資料傳輸的一種狀態:“均勻與連續”。java使用“流”進行資料傳輸。而傳輸的雙方一般是“你的程式”和“裝置(手機)”。

輸入和輸出流

根據的方向,可以分為輸入流輸出流流的輸入(Input)和輸出(Output)是以應用程式為參考中心的。輸入流就是從裝置流入到應用程式,也即常說的讀(Read);輸出流就是從應用程式流到裝置,也即我們常說的寫(Write)。

原理圖如下:
這裡寫圖片描述

注:輸入流命名規則是包含Input或Reader,如InputStream、BufferedReader;輸出流命名規則是包含Output或Writer,如OutputStream、BufferedWriter。

位元組流和字元流

資料傳輸的基本單位是位元組(bytes),但1個位元組是8位,表示範圍是0-255。面對諸多漢字,1個位元組很明顯是不足的。所以生成了一種新的單位:字元,而字元和位元組之間的對映關係就是Unicode。在java中1個字元等於2個位元組。根據流的資料型別,可分為位元組流和和字元流。

位元組流和字元流的區別

1. 讀寫單位:位元組流處理的基本單元是位元組;字元流處理的基本單元是Unicode碼,2個位元組
2. 處理物件:位元組流能處理所有型別的資料(圖片,視訊,音訊等),字元流只能處理字元型資料(字串,純文字文件)
3. 是否快取:字元流傳輸之前要先經過Unicode轉換成位元組流,批量轉換後的位元組流儲存到快取當中(提示效率),然後再進行讀寫。位元組流則不用快取(位元組流本身是不使用位元組流的,但是如果想提升效率,可以自定義快取區)。

注:位元組流一般命名規則是以Stream結尾,如InputStream或OutputStream;字元流一般命名規則是以Reader或Writer結尾。如BufferedReader、BufferedWriter。

節點和處理流

根據流是否直接與資料來源相連,可以劃分為節點流和處理流。節點流是可以直接從/向資料來源(裝置,硬碟,記憶體等)讀/寫資料;處理流是對一個已存在的流的連線、封裝和處理。

注:處理流命名規則是包含buffered,如BufferedInputStream、BufferedReader。

介紹

I/O家族譜系

這裡寫圖片描述
根據操作的資料型別,分為位元組流和字元流;根據資料的流向,又分為輸入流和輸出流;根據功能,分為節點流和處理流。

InputStream和OutputStream

位元組流處理的抽象基類是InputStream和OutputStream。InputStream是位元組輸入流,OutputStream是位元組輸出流。

Reader和Writer

字元流處理的抽象基類是Reader和Writer。Reader是字元輸入流,Writer是字元輸出流。

實戰演示

介紹的差不多了,接下來就是實戰演示了。
1.從本地讀取圖片
1. 讀取說明資料流是從裝置到應用,因此使用輸入流,那就是InputStream或Reader
2. 操作的資料物件是圖片,使用位元組流的話效率較高(不用經由Unicode轉換)
3. 所以排除Reader,選擇InputStream

 public void readImg(ImageView iv) {
        File file = new File("/sdcard/temp.jpg");
        if (file.exists()) {
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(file);
                byte[] img = new byte[(int) file.length()];// 根據檔案長度建立位元組陣列
                fileInputStream.read(img);  // 將圖片位元組資訊寫入到位元組陣列
                Bitmap bitmap = BitmapFactory.decodeByteArray(img,0,img.length);
                iv.setImageBitmap(bitmap);  // 顯示圖片
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

2.儲存圖片到本地
1. 儲存到本地說明資料流是從應用到裝置,因此使用輸出流,所以選擇OutputStream或Writer
2. 由於操作的資料物件是圖片,使用位元組流的話效率較高(不用經由Unicode轉換)
3. 所以排除Writer,選擇OutputStream

 public void saveImg() {
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            URL url = new URL("https://ss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D360/sign=e572f27e9058d109dbe3afb4e158ccd0/b7fd5266d0160924933e331bd60735fae6cd3492.jpg");
            URLConnection urlConnect = url.openConnection();
            urlConnect.setConnectTimeout(5000);
            inputStream = urlConnect.getInputStream();

            File tempFile = new File("/sdcard/temp.jpg");// 儲存到臨時檔案中
            if(tempFile.isDirectory()){
                tempFile.delete();
            }
            if (!tempFile.exists()) {
                tempFile.createNewFile();
            }
            fileOutputStream = new FileOutputStream(tempFile);// 建立輸出流,引數tempFile為寫入的目標
            byte[] buffer = new byte[1024];
            int length = -1;
            while ((length = inputStream.read(buffer)) != -1) {  // 讀取位元組,先儲存到快取區
                fileOutputStream.write(buffer, 0, length);        // 從快取區提取資料,可以提升效率
            }
            inputStream.close();
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.從本地讀取純文件
1. 由於是讀取,說明資料流是從裝置流到應用,因此使用輸入流,所以選擇InputStream或Reader
2. 操作的資料物件是純文件,需要使用字元流(純文件需要經由Unicode轉碼)
3. 所以排除InputStream,選擇Reader

  public String readWold() {
        try {
            FileReader fileReader = new FileReader("/sdcard/test.txt");
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line = null;
            StringBuilder builder = new StringBuilder();
            while ((line = bufferedReader.readLine()) != null) {
                builder.append(line + "\n");
            }
            bufferedReader.close();
            return builder.toString();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

4.寫入到本地純文件
1.寫入說明資料流是從應用到裝置,因此使用輸出流,所以選擇OutputStream或Writer
2. 操作的資料物件是純文件,需要使用字元流(純文件或字串需要經由Unicode轉碼)
3. 所以排除OutputStream,選擇Writer

public void saveWord() {
        try {
            File file = new File("/sdcard/test.txt");
            if (file.isDirectory()){
                file.delete();
            }
            if (!file.exists()){
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(file);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
            bufferedWriter.write("Hi,I am Chaos!");
            bufferedWriter.newLine();
            bufferedWriter.write("I am come from China!");
            bufferedWriter.newLine();
            bufferedWriter.flush();
            bufferedWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

參考資料