Java筆記:IO流

来源:https://www.cnblogs.com/guyuyun/archive/2020/06/16/13138701.html
-Advertisement-
Play Games

1. IO流理解 IO流中的I是單詞Input的縮寫,表示輸入或者讀(Read),O是單詞Output的縮寫,表示輸出或寫(Write),輸入輸出或者讀寫都是相對於記憶體而言的,輸入即從硬碟中讀取數據到記憶體中,輸出即將記憶體中的數據寫入到硬碟。IO流就是輸入和輸出時的數據流(記憶體和硬碟之間的管道),IO ...


1. IO流理解

IO流中的I是單詞Input的縮寫,表示輸入或者讀(Read),O是單詞Output的縮寫,表示輸出或寫(Write),輸入輸出或者讀寫都是相對於記憶體而言的,輸入即從硬碟中讀取數據到記憶體中,輸出即將記憶體中的數據寫入到硬碟。IO流就是輸入和輸出時的數據流(記憶體和硬碟之間的管道),IO的輸入或輸出也可以看做是文件的讀和寫,因為硬碟中的數據其實都是以文件的形式存儲的,在硬碟上對文件的讀寫操作就對應了記憶體中對數據的輸入和輸出操作。

 

2. 流的讀取方式

按位元組:一次只讀取一個位元組byte(8個二進位位bit),這種讀取方式的特點是什麼類型的文件都能讀取,包括普通文本、圖片、聲音等。

按字元:一次只讀取一個字元,這種讀取方式的特點是只能讀取普通文本,它能自動識別一個字元所占用的位元組數,然後進行讀取。如英文字母中一個字母只占用1個位元組,而中文中一個文字會占用2個位元組,如“a啊”,按字元的方式只需讀取兩次即可,而按位元組的方式會讀取三次,因為有三個位元組。

 

3. File類

java.io.File”類和文件流的操作沒有關係,也就是不能對文件進行讀和寫,File類是文件和目錄路徑名的一種抽象表示形式,即一個文件是File對象,一個目錄也是File對象,它的構造方法中只需要傳入一個文件或目錄的路徑名即可,註意,對於路徑分隔符,Windows和Linux使用的分別是“\”和“/”,但是File中傳入的路徑可以統一使用“/”,在Windows中也可以識別。

File中的常用方法:

  • boolean exists():判斷File對象表示的文件或目錄是否存在。

  • boolean createNewFile():如果File對象不存在,則將File對象創建為一個新的文件,如果存在則不能創建。

  • boolean mkdir():如果File對象不存在,則將File對象創建為一個新的目錄。註意,此方法只能創建單個目錄,不能遞歸創建目錄。

  • boolean mkdirs():如果File對象不存在,則將File對象以遞歸方式創建目錄。

  • String getParent():獲取File對象的父路徑(上一級路徑)。

  • File getParentFile():返回File對象的父路徑的File對象。

  • String getAbsolutePath():返回File對象的絕對路徑。

  • boolean delete():刪除File對象表示的文件或目錄。

  • String getName():獲取File對象的文件名或目錄名。

  • boolean isFile():判斷File對象是否是一個文件。

  • boolean isDirectory():判斷File對象是否是一個目錄。

  • long lastModified():返回File對象最後一次修改的時間的總毫秒數(從1970年1月1日開始)。

  • long length():返回File對象的總位元組數。

  • boolean renameTo(File dest):File對象重命名。

  • File[] listFiles():返回File對象的所有子文件和子目錄的File對象數組。

 

4. Java中的IO流

Java中IO流的類包都在“Java.io”中,共分為四大類:“java.io.InputStream”、“java.io.OutputStream”、“java.io.Reader”和“java.io.Writer”,前兩個以Stream結尾的屬於以位元組流方式進行讀寫,後兩個則是以字元流的方式進行讀寫,如果想要知道它們的某個子類是以位元組流還是字元流的方式來進行文件數據的讀取的,可以使用類名的結尾單詞進行快速區分,以Stream結尾的是以位元組流方式進行讀寫,而以Reader或Writer結尾的則是以字元流的方式進行讀寫,需要註意的是,類似“xxxStreamReader”這樣的類同樣是看結尾單詞,即它也是字元流的方式進行讀寫。

Java中的所有IO流都有一個close方法,使用完IO流之後一定記得要使用close方法關閉這個管道。對於輸出流,都有一個flush方法,表示將管道(緩衝)中的數據強制輸出並清空,其作用就是清空管道,在手動輸出完成後一定記得使用flush方法刷新一下,否則可能會導致輸出的數據不完整,即數據丟失。

 

5. 文件流

FileInputStream(常用,重點掌握):對文件以位元組流的方式進行讀取,構造方法可以傳入一個文件路徑,也可以傳入一個表示文件的File對象。

常用的方法有:

  • int read():從文件開始每次往後讀取一個位元組,註意返回的是位元組本身,並沒有將位元組轉換為對應的字元,例如第一個位元組的字元是“a”,讀取時則返回的是97。如果到了文件末尾則返回-1。註意文件指針一開始並沒有指向文件的第一個位元組,而是在第一次調用read()方法時才指向並返迴文件的第一個位元組,之後每次調用read()方法就會往後“挪”一個位元組。但是讀取文件內容時這個方法並不常用,因為每次只讀取一個位元組的方式導致和硬碟的交互太頻繁了。
  • int read(byte[] b):傳入一個byte數組,每次讀取數組的length個位元組,將讀取的內容覆蓋數組原有的值,並返回讀取的位元組數;如果文件剩餘位元組數不足length,那麼就會取出全部剩餘位元組,並將數組從頭開始覆蓋,而數組剩餘的部分不會做任何操作;當文件指針已處在末尾了,將返回-1,數組同樣不會做任何操作。
  • int available():返迴流當中剩餘的沒有讀到的位元組數。可以在讀之前使用這個方法查看文件的總位元組數。
  • long skip(long n):跳過指定數量的位元組(不去讀)。

 使用示例:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileInputStreamTest {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try{
            // FileInputStream中拋出了一個FileNotFoundException異常,即編譯時異常
            // 需要程式員手動處理,這裡加一個try進行捕獲
            // input.txt文件中只有一行數據:helloworld
            fis = new FileInputStream("Z:\\Study\\Java\\input.txt");

            /*
            // 以下read()只是演示,實際並不常用
            // 列印結果為:
            // 104
            // 101
            // 108
            // 108
            // 111
            // 119
            // 111
            // 114
            // 108
            // 100
            int byteData = 0;
            while ((byteData = fis.read()) != -1) {
                System.out.println(byteData);
            }
            */


            // 以下使用read(byte[] b)方法讀取文件內容
            byte[] bytes = new byte[6];
            int count = fis.read(bytes);
            System.out.println(count);  // 6
            System.out.println(new String(bytes));  // hellow
            System.out.println(new String(bytes, 0, count));  // hellow

            count = fis.read(bytes);
            System.out.println(count);  // 4
            System.out.println(new String(bytes));  // orldow
            System.out.println(new String(bytes, 0, count));  // orld

            count = fis.read(bytes);
            System.out.println(count);  // -1
            System.out.println(new String(bytes));  // orldow

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    // close方法也是拋出了一個IOException異常的編譯時異常,同樣加一個try進行捕獲
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

FileOutpuStream(常用,重點掌握):將位元組內容寫入到文件中,構造方法中同樣可以傳入一個文件路徑或者表示文件的File對象,同時還可以指定第二參數為true。當只傳入一個文件路徑或File對象,如果文件已存在,會清空文件內容,如果文件不存在,則會自動創建該文件;也可以指定第二個參數為true,表示如果文件已存在則不會清空文件,而是會往文件末尾追加數據。

常用的方法有:

  • void write(byte[] b):將byte數組中的全部內容寫入到文件中。

  • void write(byte[] b, int off, int len):將byte數組中的部分內容寫入到文件中。

  • 輸出字元串:可以將字元串通過自身的getBytes()轉換為byte數組之後再進行輸出。

使用示例:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamTest {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            // 只傳入一個文件路徑:如果文件已存在,會清空文件內容,如果文件不存在,則會自動創建該文件
            // fos = new FileOutputStream("Z:\\Study\\Java\\output.txt");

            // 第二個參數表示如果文件已存在則不會清空文件,而是會往文件末尾追加數據
            fos = new FileOutputStream("Z:\\Study\\Java\\output.txt", true);

            byte[] bytes = {97, 98, 99, 100};
            // 將bytes數組全部寫到文件中
            fos.write(bytes);
            // 將bytes數組的一部分寫到文件中
            fos.write(bytes, 0, 2);

            // 將字元串輸出到文件中
            String s = "你好,世界";
            byte[] bs = s.getBytes();
            fos.write(bs);

            // 文件輸出完成後,一定記得刷新一下
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    // 文件操作完成後一定記得調用close()方法
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

FileReader:和FileInputStream相比,在使用上都是類似的,只是需要把byte數組換成char數組即可。

FileWriter:和FileOutpuStream相比,在使用上也都是類似的,只是要把byte數組換成char數組,另外,由於字元串的特殊性,FileWriter的write方法還可以直接寫入一個字元串到文件。

 

6. 緩衝流

緩衝流表示帶有緩衝區的位元組流或字元流,特點是使用這些流的時候不需要自定義byte數組或char數組。

包裝流和節點流:緩衝流的使用涉及到一些概念,當一個流的構造方法中需要另一個流的時候,被傳入的這個流稱為節點流,外部負責包裝這個節點流的流稱為包裝流或處理流。關閉包裝流的時候也是只需要調用最外層包裝流的close()方法即可,裡面的節點流會自動關閉。同樣,對於輸出的流,也是只需要調用最外層包裝流的flush()方法即可。

BufferedInputStream:是一個包裝流,構造方法中需要傳入一個InputStream類型(位元組類型)的節點流。

BufferedOutputStream:是一個包裝流,構造方法中需要傳入一個OutputStream類型(位元組類型)的節點流。

BufferedReader:是一個包裝流,構造方法中需要傳入一個Reader類型(字元類型)的節點流。

BufferedWriter:是一個包裝流,構造方法中需要傳入一個Writer類型(字元類型)的節點流。

使用示例:

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderTest {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            // 這裡reader就是一個節點流,br就是一個包裝流或處理流
            // 文件內容只有一行:helloworld
            FileReader reader = new FileReader("Z:\\Study\\Java\\input.txt");
            br = new BufferedReader(reader);

            // 讀取一行數據,註意返回的數據是不包含末尾的換行符的
            String line = br.readLine();
            System.out.println(line);  // helloworld

            // 讀取到文件末尾,返回null
            line = br.readLine();
            System.out.println(line);  // null

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 對於包裝流來說,只需要調用最外層包裝流的close()方法即可,裡面的節點流會自動關閉。
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 

 

7. 數據流

這種流的特點是會將數據以及數據的類型一起寫入文件,每次寫入文件的時候都可以使用不同的數據類型,而讀取的時候則需要按照寫入的順序按照數據類型讀取出來。

DataInputStream:按照指定規則從文件中讀取特定類型的數據。

DataOutputStream:按照指定規則向文件中寫入特定類型的數據。

 

8. 標準輸出流

標準輸出不需要手動close()關閉。

PrintStream:標準的位元組輸出流,預設輸出到控制台。System.out其實就是一個PrintStream標準輸出位元組流,可以使用System.setOut設置標準輸出流輸出到指定指定文件,不再預設輸出到控制台。

PrintWriter:標準的字元輸出流,預設輸出到控制台,同樣可以使用System.setOut更改預設輸出方向。

使用示例:

PrintStream out = new PrintStream(new FileOutputStreasm("log.txt", true));
System.setOut(out);

 

 

9. 對象流(序列化和反序列化)

序列化和反序列化:對象流的使用涉及到兩個概念,即序列化和反序列化,序列化(Serialize)是指將java對象存儲到文件中,將java對象的狀態以文件的形式保存下來。而反序列化(Deserialize)則是序列化的反過程,即將文件中的數據恢復到記憶體當中,恢復為java對象的過程。序列化的過程需要使用到ObjectOutputStream,而反序列化的過程需要使用到ObjectInputStream。

ObjectOutputStream:構造方法中傳入一個FileOutputStream對象,然後調用writeObject方法就可以將一個需要序列化的java對象保存到指定的文件中了。如果需要序列化多個對象,可以將這些對象放到ArrayList集合中,最後將這個集合傳入writeObject方法即可,但是需要註意一點,就是ArrayList集合本身也是實現了Serializable介面的,也就是說,使用的集合以及集合中的元素都需要實現Serializable介面才能進行序列化的操作。關於序列化多個對象,建議直接使用集合的方式就好了,如果採用多次調用writeObject的方式每次寫入一個對象,這種方式是不允許的。

ObjectInputStream:構造方法中傳入一個FileInputStream對象,然後調用readObject方法就可以從指定文件的反序列化結果中讀取一個java對象(普通java對象或集合對象)到記憶體中了。

Serializable介面:參與序列化和反序列化的對象需要實現Serializable介面,但是註意,這個介面並沒有任何方法,它只是起到了一個標識的作用(這種介面也稱為標誌介面),通過這個介面告訴JVM此類的對象可能會進行序列化的操作。有了這個標誌介面,編譯時JVM就會為這個java對象自動生成一個序列化版本號,這個序列化版本號的作用就是反序列化時用來識別當下源代碼中的class定義和之前執行序列化操作時的class定義是否一致,如果不一致則會報錯,不能進行反序列化的操作,即反序列化的對象的類在當前源代碼中的定義已經被修改,無法反序列化(反序列化回來時也需要一個對應class類型的變數去接收,兩者不一樣的話,自然就無法通過了),所以由此看出,這個序列化版本號本質上就是用來區分類的,判斷兩個類的定義是否相同。通常建議這個序列化版本有我們程式員自己手動定義,而不是讓JVM自動生成。

transient關鍵字:序列化時,如果希望類中的某個欄位不要將它序列化到文件中,只需要在屬性定義的時候加上這個transient關鍵字即可。

手動生成序列化版本號:通常來講,一個類定義之後,不可能保證它永遠都不會被修改,但是當它被修改之後,它的序列化版本號就變了,這就會導致之前序列化到文件中的數據不能反序列化回來了,這樣的結果通常是不行的,所以建議需要序列化的類都自己手動編寫一個序列化版本號,而不是採用JVM自動生成的方式。手動編寫的方式就是在類定義中添加一個序列化版本號的屬性即可“private static final long serialVersionUID = 233L;”,這裡的屬性值通常來講需要是保證它是項目中唯一的編號即可,所以可以根據項目情況自定義就可以了,如果不放心,自己生成一個全球唯一的UID也可以(IDEA也是可以自動生成這個屬性的,在設置界面直接搜索serialVersionUID,可以看到Inspections下有個對應的檢查項“Serializable class without 'serialVersionUID'”,勾上就可以了)。

使用示例:

普通java對象

// 實現Serializable,但這個介面沒有任何方法需要去重寫
public class User implements Serializable {
    private int id;
    private String name;
    ...
}

 

序列化單個java對象

User u = new User(123, "zhangsan");
// 將user對象序列化到Z:\\userinfo文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Z:\\userinfo"));
oos.writeObject(s);
oos.flush();
oos.close();

 

序列化多個java對象

// 這裡使用ArrayList存放多個User對象
List<User> userList = new ArrayList<>();
userList.add(new User(111, "zhangyi"));
userList.add(new User(222, "zhanger"));
userList.add(new User(333, "zhangsan"));

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Z:\\userinfo"));
oos.writeObject(userList);
oos.flush();
oos.close();

反序列化java對象

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Z:\\userinfo"));
// 反序列化出來的對象需要進行強制類型轉換一下
List<User> userList = (List<User>)ois.readObject();
ois.close();

 

 

10. io和Properties聯合使用以讀取配置文件
如果一個文本文件中的每一行數據是像這樣“key=value”使用等號或冒號(冒號不建議)分隔成兩部分,就可以使用一個文件輸入流讀取出來,再使用Properties對象的load方法載入這個文件流輸入對象,然後在Properties的getProperty方法傳入等號左邊的字元串就可以取出等號右邊的字元串了。

user-conf.txt

 username=root
 password=123456

 

 FileReader reader = new FileReader("user-conf.txt");
 Properties pro = new Properties();
 pro.load(reader);
 String username = pro.getProperty("username");
 System.out.println(username);  // root

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 將一些零散的知識點進行整理, 以便加深理解,方便查閱,也希望能幫到大家。 一、負載均衡演算法 1. 隨機 完全隨機 通過系統隨機函數,根據後端伺服器列表的大小值來隨機選擇其中一臺進行訪問。由概率統計理論可以得知,隨著調用量的增大,其實際效果越來越接近於平均分配流量到每一臺後端伺服器,也就是輪詢的效果。 ...
  • /** * 1.模式定義: * 過濾器(Filter Pattern)又稱為標準模式(Criteria Pattern)是一種設計模式,這種模式允許開發人員使用不同的標準來過濾一組對象, * 通過預算邏輯以解耦的方式將他們聯繫起來。這種類型的設計模式屬於結構模型,說白了,就是按條件篩選一組對象出來。 ...
  • 背景 之前做過一個項目,資料庫存儲採用的是mysql。當時面臨著業務指數級的增長,存儲容量不足。當時採用的措施是 1>短期解決容量的問題 mysql從5.6升級5.7,因為數據核心且重要,資料庫主從同步採用的是全同步, 利用5.7並行複製新特性,減少了主從同步的延遲,提高了吞吐量。 當時業務量高峰是 ...
  • 1.動態SQL簡介 動態 SQL是MyBatis強大特性之一. 動態 SQL 元素和使用 JSTL 或其他類似基於 XML 的文本處理器相似. MyBatis 採用功能強大的基於 OGNL 的表達式來簡化操作. 2.if 1).實現DynamicSQL public interface Employ ...
  • 消費方項目為SpringMVC 服務提供方為Spring+MyBatis 使用版本號: dubbo 2.6.6 zookeeper 3.6.1 出現異常: 類型:com.alibaba.dubbo.rpc.RpcException 主要信息: Invoke remote method timeout ...
  • C++中,指向常量的指針和常量型指針很容易搞混和記錯。為了記憶,今天特此記錄下來,以便以後查看。 1. 指向常量的指針(point to const): (1)定義:通常是指向常量的指針,也就是指針指向的內容是個常量。 (2)格式:定義格式例如:const double * ptr 或者double ...
  • 逗號運算符( , )是C++語言運算符中優先順序最低的一種運算符,結合順序是從左至右,用來順序求值(最後一個逗號後面表達式的值作為整個表達式的值)。 感覺這個東西還是挺冷門的,之前都不知道,平時也比較少用到,不過探究這個是個挺有趣的過程。 以下是幾個無聊的小實驗: 逗號+常數語句的返回值 int a ...
  • 什麼是監督學習?什麼是無監督學習? 監督學習:有目標y值,如線性回歸,分類演算法 無監督學習:無目標y值,如聚類 邏輯回歸是分類演算法,不要被名字誤導,得到的是離散值 引入邏輯回歸 邏輯回歸主要用於二分類 線上性回歸中:Y=W1X1+W2X2+W3X3 +...+b=WT*X 在邏輯回歸中,習慣用Z表示 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...