Java 優化:讀取組態檔 "萬能方式" 跨平臺,動態獲取檔案的絕對路徑

2023-02-22 12:00:23

Java 優化:讀取組態檔 "萬能方式" 跨平臺,動態獲取檔案的絕對路徑

每博一文案

往事不會像煙霧似的飄散,將永遠像鉛一般沉重地澆鑄在心靈的深處。
不過,日常生活的紛繁不會讓人專注地沉湎於自己的痛苦
不幸,即使人的心靈傷痕累累,也還得要去為現實中的生存和發展而掙扎。
                              —————— 《平凡世界》
每個人的生活同樣也是一個世界,即使最平凡的人,也得要為他那個世界的存在而戰鬥。從這個意義
上說,在這些平凡的世界裡,也沒有一天是平靜的。
                              —————— 《平凡世界》

@


我們知道在 Java 中讀取一些組態檔資訊,是在開發中十分常用的要求。

例如:這裡我們使用 JDBC 範例:連線MySQL 資料庫,讀取連線資料庫的 使用者名稱,密碼

如下是一個名為 jdbc.properties 的組態檔資訊,以及存在目錄


package blogs.blogs8;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class IORead {
    public static void main(String[] args) {
        FileInputStream f = null;
        try {
            // 建立位元組輸入流物件
            // 在IDEA 中的預設相對路徑是在 src 同級目錄下的
            f = new FileInputStream("src/blogs/blogs8/jdbc.properties");

            // 建立Map集合中的 Properties 物件
            Properties properties = new Properties();
            properties.load(f);

            // 通過 key 讀取對應的鍵值對
            String user = properties.getProperty("user");
            System.out.println(user);
            String password = properties.getProperty("password");
            System.out.println(password);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關閉IO資源
            if(f == null) {
                try {
                    f.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}



說明:

上述的讀取檔案的方式,我們可以看到是 「完全沒有問題的」 可以讀取到對應的設定資訊,但是存在一個缺點:就是移除性差。src 中是在 IDEA 這個編譯器中體現的,如果是在其它的編譯器中執行的時候,很大的可能會報錯,原因是:這裡我們使用的相對路徑是,在 IDEA中的,IDEA 中的預設相對路徑是 在 project 下的也就是 src 的同級目錄。但是其它的系統,或者編譯器就可能不是這個和 IDEA 中預設相對路徑了。執行程式時,就有可能會報錯:如下:找不到指定的檔案。

上述這種方式:如果我們不寫相對路徑,而是寫絕對路徑的話,也是存在一個問題的。那就是因為該絕對路徑是寫死了的,不是動態獲取的,該路徑在 Windows 作業系統中是存在碟符的,所以寫絕對路徑的時候是需要帶上碟符(E槽,D槽的),但是如果該程式是執行在其他作業系統中的話,比如 Linux 作業系統中是沒有碟符的說法的。所以就會出問題。無法跨平臺。

1. 優化方式一:返回一個檔案的絕對路徑

接下來說一種比較通用的一種路徑:即使程式碼換位置了,這樣的程式碼編寫的方式仍然是通用的。因為該檔案的路徑是動態獲取的。

在Windows中的話,就以該系統的檔案規則,動態獲取到的絕對路徑是帶碟符的,而 Linux系統中就以該系統的檔案規則,獲取到的絕對路徑是不帶碟符的。 這就可以跨平臺了。

注意: 使用該方式的前提是:所讀取的檔案必須是在 類路徑 下才行。如果不是在類路徑下,執行程式時是會報錯:系統找不到指定的路徑

什麼是類路徑 ?

類路徑也是一種特殊的相對路徑,只不過它相對的是class檔案。在 IDEA 中的類路徑是在 src 目錄下的。重點記住它

該方式的核心程式碼:

        String path = Thread.currentThread().getContextClassLoader().getResource("db.properties").getPath();
/*
解釋: 
 Thread.currentThread()  當前執行緒物件
 getContextClassLoader() 是執行緒物件的方法,可以獲取到當前執行緒的類載入物件
 getResource() 獲取資源:這是類載入器物件的方法,當前執行緒的類載入器預設從類的根路徑下載入資源。
 getPath() 獲取當檔案的絕對路徑
*/

1.1 情況一

所讀取的檔案是直接存放在 src 的目錄下的,該檔案的並沒有其它的的包。如下圖所示:可以直接寫檔名 + 檔名的字尾即可。

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class IORead {
    public static void main(String[] args) {
        String path = Thread.currentThread().getContextClassLoader().getResource("db.properties").getPath();
        System.out.println(path);  // 返回該檔案的絕對路徑:
    }

}

通過該方式獲取到指定檔案的絕對路徑,再將該絕對路徑,作為引數,建立FileInputStream位元組輸入流物件


import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class IORead {
    public static void main(String[] args) {
        FileInputStream f = null;
        try {
            // 獲取到該組態檔的的絕對路徑
            String path = Thread.currentThread().getContextClassLoader().getResource("db.properties").getPath();
            //  通過該獲取的檔案的絕對路徑建立 位元組輸入流物件
            f = new FileInputStream(path);

            // 建立Map集合中的 Properties 物件
            Properties properties = new Properties();
            properties.load(f);

            // 通過 key 讀取對應的鍵值對
            String user = properties.getProperty("user");
            System.out.println(user);
            String password = properties.getProperty("password");
            System.out.println(password);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關閉IO資源
            if (f != null) {
                try {
                    f.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

也是可以讀取到檔案中是在 src 目錄下。

1.2 情況二

當所讀取的檔案,是在 src 目錄下,但是該 src 目錄下還有其他的包(目錄),則不可以直接寫 「檔名+ 檔案字尾名」了,而是需要寫明該 src 包(目錄)下的 相對路徑:如下圖所示的檔案:該路徑名應該是:blogs/blogs8/jdbc.properties

舉例:


import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class IORead {
    public static void main(String[] args) {
        FileInputStream f = null;
        try {
            // 獲取到該組態檔的的絕對路徑,如下src目錄下還有目錄(包),需要指定 src目錄下/包下的哪個檔案。
            String path = Thread.currentThread().getContextClassLoader().getResource("blogs/blogs8/jdbc.properties").getPath();
            //  通過該獲取的檔案的絕對路徑建立 位元組輸入流物件
            f = new FileInputStream(path);

            // 建立Map集合中的 Properties 物件
            Properties properties = new Properties();
            properties.load(f);

            // 通過 key 讀取對應的鍵值對
            String user = properties.getProperty("user");
            System.out.println(user);
            String password = properties.getProperty("password");
            System.out.println(password);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關閉IO資源
            if (f != null) {
                try {
                    f.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

2. 優化方式二:返回一個 InputStream 位元組輸入流

上述方式一:我們需要通過 :new 一個 FileInputStream 位元組輸入流物件的方式,這裡我們直接通過指定的檔名的,直接返回一個 InputStream 位元組輸入流 ,不需要 new 。

同樣的:該讀取的檔案必須是在類路徑下才行,這裡的IDEA的類路徑是 src 目錄下

核心程式碼如下:

// 直接以流的形式返回。
        InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().
                getResourceAsStream("db.properties");

舉例:


import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class IORead {


    public static void main(String[] args) {
        // 直接在 src目錄下沒有包含任何子目錄,可以直接寫檔名+ 字尾,而如果有子目錄,需要指明子目錄下的檔名+字尾名
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties");

        // 建立 Properties 集合物件,通過流獲取指定組態檔中的鍵值對資訊
        Properties properties = new Properties();
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String user = properties.getProperty("user");
        System.out.println(user);
        String password = properties.getProperty("password");
        System.out.println(password);

        // 關閉IO資源
        if (inputStream != null) {

            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

3. 優化方式三:java.util 包下提供了一個資源繫結器

上述兩個方式可以獲取到任意檔案的資訊。

但是以下這個方式三:就只能獲取到 類路徑下的以 .properties 字尾的組態檔資訊了。

java.util 包下提供了一個資源繫結器,便於獲取屬性.properties 組態檔中的內容。

該資源繫結器:只能繫結 xxx.properties 組態檔 ,並且這個檔案必須在 類路徑下,這裡的 IDEA 是 src 目錄下。

並且在寫路徑的時候,路徑後面的擴充套件名不能寫,寫了會報錯: ``。因為既然只能讀取 properteis 字尾的檔案,那就不用再多餘的寫檔案字尾名了。

如果在 src 目錄下的子目錄中的檔案,需要指明是 src 下的哪個子目錄下的檔案,同樣不要寫檔案字尾名,不然報錯。

舉例:

import java.util.ResourceBundle;

public class IORead {


    public static void main(String[] args) {
        ResourceBundle resourceBundle = ResourceBundle.getBundle("db");
        String user = resourceBundle.getString("user");
        System.out.println(user);
        String password = resourceBundle.getString("password");
        System.out.println(password);
    }
}

4. 總結:

  1. 原始的方式:寫相對路徑的話,無法跨編譯器;因為不同的編譯器預設相對的路徑是不同的。寫絕對路徑的話,無法跨平臺,因為不同作業系統的檔案規則是不一樣的,比如 Windows系統中的絕對路徑是帶碟符(D槽,C槽),Linux 系統中的檔案規則是不帶碟符的。當在J Windows 作業系統中編寫的絕對路徑的Java程式,移植到到 Linux 作業系統中就會報錯。
  2. 靜態獲取的絕對路徑 和 動態獲取絕對路徑。
  3. 上述的三種優化方式,都是動態獲取絕對路徑的,但是都是基於 類路徑下的檔案才行的,不同所讀取的檔案不在 類路徑下 是無法動態獲取到對應絕對路徑的。
  4. 上述 :優化方式1,優化方式2 可以動態獲取到 類路徑下的任意檔案資訊。但是 優化方式三:只能獲取到 類路徑下的以 .properties 字尾的組態檔資訊了。
  5. 注意:優化方式三:不可以寫檔案字尾名,直接寫檔名就可以了。因為資源繫結器,就只能繫結 xxx.properties 組態檔 ,並且這個檔案必須在 類路徑下。
  6. 如果類路徑下,比如:IDEA 中的 src 目錄就是類路徑,檔案是直接在 src 類路徑下沒有包含子目錄的話,可以直接寫 檔名+檔案字尾名,如果檔案是在 src 目錄下含有的子目錄下,則需要指明 類路徑 src 下的哪個子目錄的檔案。

5. 最後:

限於自身水平,其中存在的錯誤,希望大家給予指教,韓信點兵——多多益善 。謝謝大家,後會有期 ,江湖再見 !!!