JavaIO流(輸入輸出操作)


Java中執行輸出和輸入操作,需要通過IO流。例如最常見的System.out.println()就是一個輸出流。IO流的類比較多,但核心體系就是由File、 InputStream 、OutputStream、Reader、Writer和Serializable(接口)組成的,后續會一一詳細說明。

I/O流基礎概念

按照流的方向分為輸入流(InputStream)與輸出流(OuputStream):

  • 輸入流:只能讀取數據,不能寫入數據。
  • 輸出流:只能寫入數據,不能讀取數據。

因為程序是運行在內存中,以內存角度來理解輸入輸出概念,如下:

可以看到輸入與輸出是一個相對概念,數據寫入文件,對於程序來說是輸出流,對文件來說是輸入流。但一般是以程序作為中心,所以從程序寫入數據到其他位置,則是輸出流,將數據讀入程序中則是輸入流。

簡單的說就是:讀取數據就是輸入流,寫入數據就是輸出流

 

按照處理的數據單位分為字節流和字符流

  •  字節流:操作的數據單元是8位的字節。InputStream、OutputStream作為抽象基類。
  •  字符流:操作的數據單元是字符。以Writer、Reader作為抽象基類。
  •  字節流可以處理所有數據文件,若處理的是純文本數據,建議使用字符流。

 

IO流中的三類數據源

  • 基於磁盤文件:FileInputStream、FileOutputSteam、FileReader、FileWriter
  • 基於內存:ByteArrayInputStream ByteArrayOutputStream(ps:字節數組都是在內存中產生)
  • 基於網絡:SocketInputStream、SocketOutputStream(ps:網絡通信時傳輸數據)

根據流的作用可分為節點流和處理流

節點流:程序直接與數據源連接,和實際的輸入/輸出節點連接;處理流:對節點流進行包裝,擴展原來的功能,由處理流執行IO操作。

處理流的作用和分類:

處理流可以隱藏底層設備上節點流的差異,無需關心數據源的來源,程序只需要通過處理流執行IO操作。處理流以節點流作為構造參數。通常情況下,推薦使用處理流來完成IO操作。

緩沖流:提供一個緩沖區,能夠提高輸入/輸出的執行效率,減少同節點的頻繁操作。例如:BufferedInputStream/BufferedOutputStream、BufferedReader/BufferWriter

轉換流:將字節流轉成字符流。字節流使用范圍廣,但字符流更方便。例如一個字節流的數據源是純文本,轉成字符流來處理會更好。InputStreamReader/OutputStreamWriter

打印輸出流:打印輸出指定內容,根據構造參數中的節點流來決定輸出到何處。

PrintStream :打印輸出字節數據。
PrintWriter : 打印輸出文本數據。 

附圖:JavaIO體系的全體類

 

介紹完基礎概念后,使用IO流來完成一些簡單功能:

(一)使用字節流讀取本地文件

//File對象定位數據源
public static void getContent(File file) throws IOException { 
//創建文件緩沖輸入流
file BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); 
byte[] buf = new byte[1024];//創建字節數組,存儲臨時讀取的數據 
int len = 0;//記錄數據讀取的長度
 //循環讀取數據
 while((len = bis.read(buf)) != -1) { //長度為-1則讀取完畢 
 System.out.println(new String(buf,0,len));    
}
 bis.close(); //關閉流
}

【技巧】如果數據源是純文本數據,使用字符流效率更高。

 

(二)使用字符處理流讀取本地文件內容

    public static void getContent(String path) throws IOException {
        File f = new File(path); 
        if (f.exists()) { // 判斷文件或目錄是否存在
            if (f.isFile()) { 
                BufferedReader br = new BufferedReader(new FileReader(path));//該緩沖流有一個readLine()獨有方法
                String s = null;
                while ((s = br.readLine()) != null) {//readLine()每次讀取一行
                    System.out.println(s);
                }
            }
        }
    }

該方法比上一個增加了文件判斷,提高了程序的健壯性。使用了BufferedReader處理流來處理純文本數據,比字節流更加簡潔方便。

(三)使用字符流寫入數據到指定文件:

public static void main(String[] args) throws IOException {
        //以標准輸入作為掃描來源
        Scanner sc = new Scanner(System.in);
        File f = new File("D:\\reviewIO\\WRITERTest.txt");
        BufferedWriter bw = new BufferedWriter(new FileWriter(f));
        if(!f.exists()) {
            f.createNewFile();
        }
        while(true) {
            String s = sc.nextLine();
            bw.write(s);
            bw.flush();
            if(s.equals("結束") || s.equals("")) {
                System.out.println("寫入數據結束!");
                return;
            }
        }
    }

 

(四)使用轉換流(InputStreamReader/OutputStreamWriter),對寫入數據進行改進:

public static void testConvert(File f) throws IOException {
        if(!f.exists()) {
            f.createNewFile();
        }
        //以System.in作為讀取的數據源,即從鍵盤讀取
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new FileWriter(f,true)); //允許添加內容,不會清除原有數據源
        String s = null;
        while(!(s = br.readLine()).equals("")) {
            bw.write(s);
            bw.newLine();//空一行
        }
        bw.flush();        
        bw.close();
        br.close();
    } 

因為System.in是一個InputStream對象,緩沖字符流無法直接使用,需要通過轉換流將字節流轉成字符流。然后使用字符輸入處理流的readLine()每次讀取一行,使用newLine()完成換行。

注意點:通常使用IO流寫入文件時,寫入的數據總會覆蓋原來的數據,這是因為文件輸出流默認不允許追加內容,所以需要為FileOuputStream、FileWriter的構造參數boolean append 傳入true。

 

(五)使用字節流完成文件復制
//字節流實現文件拷貝
    public static String copyFile(String src, String dest) throws IOException, ClassNotFoundException {
        File srcFile = new File(src);//源文件數據源
        File desFile = new File(dest);//寫入到目標數據源
        //數據源不存在
        if(!srcFile.exists() || !desFile.exists()) {
            throw new ClassNotFoundException("源文件或者拷貝目標文件地址不存在!");
        }
        //非文件類型
        if(!srcFile.isFile() || !desFile.isFile()) {
            return "源文件或者目標文件不是文件類型!";
        }
        InputStream is = null;
        OutputStream os = null;
        byte[] buf = new byte[1024];//緩存區
        int len = 0;//讀取長度
        try {
            is = new BufferedInputStream(new FileInputStream(srcFile));//讀取數據源
            os = new BufferedOutputStream(new FileOutputStream(desFile));//寫入到數據源            
            while((len = is.read(buf)) != -1) { //讀取長度不為-1,繼續讀取
                os.write(buf); //讀取內容之后馬上寫入目標數據源
            }
            os.flush();//輸出
            return "文件拷貝成功!查看拷貝文件路徑:" + desFile.getPath();                        
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(is != null)
                is.close();
            if(os != null)
                os.close();
        }
        return "文件拷貝失敗";
    }
 

(六)使用打印流來完成寫入數據操作:

        //輸出內容的文件數據源
        File f = new File("D:\\reviewIO\\PW.java");
        PrintWriter pw = new PrintWriter(f);
        //把指定內容打印至數據源中
        pw.println("AAAAAAAAA");
        pw.println("BBBBBBBBB");
        pw.println("CCCCCCCCC");
        pw.flush();
        System.out.println("使用PrintWriter寫入數據完成");
        System.out.println("==========讀取寫入的數據==========");
        BufferedReader br = new BufferedReader(new FileReader(f));
        String s = null;
        StringBuilder sb = new StringBuilder();//一個可變字符串
        while((s = br.readLine()) != null) {
            sb.append(s); //把讀取的字符串組合起來
        }
        System.out.println(sb);
        br.close();
        pw.close();

一般情況下,若是輸出文本數據,建議使用打印流。PrintWriter還可以指定輸出文本使用何種字符集、在構造參數中指定是否自動刷新。如果不想覆蓋原來的數據,使用該類的append()方法,就會在文件尾部添加內容。

 

(七)使用打印流來完成文本拷貝:

    // 使用打印流PrintStream來完成文件拷貝
    public static void copyFile(File src, File dest) throws Exception {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
        PrintStream ps = new PrintStream(bos, true);
        byte[] buf = new byte[1024];
        int len = 0;
        //循環讀取數據,然后寫入到目標文件
        while ((len = bis.read(buf)) != -1) {
            ps.write(buf);
        }
        ps.close();
        bos.close();
    }

打印流實現文件拷貝操作和字節流差不多,除了用到打印流構造函數的不自動刷新。打印流還有一個好處就是無需檢查異常。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM