Linux中國

在 Java 中使用外部庫

Java 自帶有一組核心庫,其中包含了定義常用數據類型和相關行為的庫(例如 String 和 Date)、與主機操作系統交互的實用程序(例如 System 和 File),以及一些用來管理安全性、處理網路通信、創建或解析 XML的有用的子系統。鑒於核心庫的豐富性,程序員通常很容易在其中找到有用的組件,以減少需要編寫的代碼量。

即便如此,核心庫仍有一些功能上的不足,因此發現這些不足的程序員們還額外創建了很多有趣的 Java 庫。例如,Apache Commons「是一個專註於可重用 Java 組件所有方面的 Apache 項目」,提供了大約 43 個開源庫的集合(截至撰寫本文時),涵蓋了 Java 核心庫之外的一系列功能 (例如 geometry 或 statistics),並增強或替換了 Java 核心庫中的原有功能(例如 math 或 numbers)。

另一種常見的 Java 庫類型是系統組件的介面(例如資料庫系統介面),本文會著眼於使用此類介面連接到 PostgreSQL 資料庫,並得到一些有趣的信息。首先,我們來回顧一下庫的重要部分。

什麼是庫?

庫 library 里自然包含的是一些有用的代碼。但為了發揮用處,代碼需要以特定方式進行組織,特定的方式使 Java 程序員可以訪問其中組件來解決手頭問題。

可以說,一個庫最重要的部分是它的應用程序編程介面(API)文檔。這種文檔很多人都熟悉,通常是由 Javadoc 生成的。Javadoc 讀取代碼中的結構化注釋並以 HTML 格式輸出文檔,通常 API 的 包 package 在頁面左上角的面板中顯示, 類 class 在左下角顯示,同時右側會有庫、包或類級別的詳細文檔(具體取決於在主面板中選擇的內容)。例如,Apache Commons Math 的頂級 API 文檔 如下所示:

![API documentation for Apache Commons Math](/data/attachment/album/202109/10/075802g5nlrmmlxgwcb325.png "API documentation for Apache Commons Math")

單擊主面板中的包會顯示該包中定義的 Java 類和介面。例如,org.apache.commons.math4.analysis.solvers 顯示了諸如 BisectionSolver 這樣的類,該類用於使用二分演算法查找單變數實函數的零點。單擊 BisectionSolver 鏈接會列出 BisectionSolver 類的所有方法。

這類文檔可用作參考文檔,不適合作為學習如何使用庫的教程。比如,如果你知道什麼是單變數實函數並查看包 org.apache.commons.math4.analysis.function,就可以試著使用該包來組合函數定義,然後使用 org.apache.commons.math4.analysis.solvers 包來查找剛剛創建的函數的零點。但如果你不知道,就可能需要更多學習向的文檔,也許甚至是一個實際例子,來讀懂參考文檔。

這種文檔結構還有助於闡明 包 package (相關 Java 類和介面定義的集合)的含義,並顯示特定庫中捆綁了哪些包。

這種庫的代碼通常是在 .jar 文件 中,它基本上是由 Java 的 jar 命令創建的 .zip 文件,其中還包含一些其他有用的信息。.jar 文件通常被創建為構建過程的端點,該構建過程編譯了所定義包中的所有 .java 文件。

要訪問外部庫提供的功能,有兩個主要步驟:

  1. 確保通過類路徑(或者命令行中的 -cp 參數或者 CLASSPATH 環境變數),庫可用於 Java 編譯步驟(javac)和執行步驟(java)。
  2. 使用恰當的 import 語句訪問程序源代碼中的包和類。

其餘的步驟就與使用 String 等 Java核心類相同,使用庫提供的類和介面定義來編寫代碼。很簡單對吧?不過也沒那麼簡單。首先,你需要了解庫組件的預期使用模式,然後才能編寫代碼。

示例:連接 PostgreSQL 資料庫

在資料庫系統中訪問數據的典型使用步驟是:

  1. 訪問正在使用的特定資料庫軟體代碼。
  2. 連接到資料庫伺服器。
  3. 構建查詢字元串。
  4. 執行查詢字元串。
  5. 針對返回的結果,做需要的處理。
  6. 斷開與資料庫伺服器的連接。

所有這些面向程序員的部分由介麵包 java.sql 提供,它獨立於資料庫,定義了核心客戶端 Java 資料庫連接(JDBC)API。java.sql 包是 Java 核心庫的一部分,因此無需提供 .jar 文件即可編譯。但每個資料庫提供者都會創建自己的 java.sql 介面實現(例如 Connection 介面),並且必須在運行步驟中提供這些實現。

接下來我們使用 PostgreSQL,看看這一過程是如何進行的。

訪問特定資料庫的代碼

以下代碼使用 Java 類載入器(Class.forName() 調用)將 PostgreSQL 驅動程序代碼載入到正在執行的虛擬機中:

import java.sql.*;

public class Test1 {

    public static void main(String args[]) {

        // Load the driver (jar file must be on class path) [1]

        try {
            Class.forName("org.postgresql.Driver");
            System.out.println("driver loaded");
        } catch (Exception e1) {
            System.err.println("couldn't find driver");
            System.err.println(e1);
            System.exit(1);
        }

        // If we get here all is OK

        System.out.println("done.");
    }
}

因為類載入器可能失敗,失敗時會拋出異常,所以將對 Class.forName() 的調用放在 try-catch 代碼塊中。

如果你使用 javac 編譯上面的代碼,然後用 java 運行,會報異常:

me@mymachine:~/Test$ javac Test1.java
me@mymachine:~/Test$ java Test1
couldn't find driver
java.lang.ClassNotFoundException: org.postgresql.Driver
me@mymachine:~/Test$

類載入器要求類路徑中有包含 PostgreSQL JDBC 驅動程序實現的 .jar 文件:

me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test1
driver loaded
done.
me@mymachine:~/Test$

連接到資料庫伺服器

以下代碼實現了載入 JDBC 驅動程序和創建到 PostgreSQL 資料庫的連接:

import java.sql.*;

public class Test2 {

        public static void main(String args[]) {

                // Load the driver (jar file must be on class path) [1]

                try {
                        Class.forName("org.postgresql.Driver");
                        System.out.println("driver loaded");
                } catch (Exception e1) {
                        System.err.println("couldn't find driver");
                        System.err.println(e1);
                        System.exit(1);
                }

                // Set up connection properties [2]

                java.util.Properties props = new java.util.Properties();
                props.setProperty("user","me");
                props.setProperty("password","mypassword");
                String database = "jdbc:postgresql://myhost.org:5432/test";

                // Open the connection to the database [3]

                try (Connection conn = DriverManager.getConnection(database, props)) {
                        System.out.println("connection created");
                } catch (Exception e2) {
                        System.err.println("sql operations failed");
                        System.err.println(e2);
                        System.exit(2);
                }
                System.out.println("connection closed");

                // If we get here all is OK

                System.out.println("done.");
        }
}

編譯並運行上述代碼:

me@mymachine:~/Test$ javac Test2.java
me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test2
driver loaded
connection created
connection closed
done.
me@mymachine:~/Test$

關於上述的一些注意事項:

  • 注釋 [2] 後面的代碼使用系統屬性來設置連接參數(在本例中參數為 PostgreSQL 用戶名和密碼)。代碼也可以從 Java 命令行獲取這些參數並將所有參數作為參數包傳遞,同時還有一些其他 Driver.getConnection() 選項可用於單獨傳遞參數。
  • JDBC 需要一個用於定義資料庫的 URL,它在上述代碼中被聲明為 String database 並與連接參數一起傳遞給 Driver.getConnection() 方法。
  • 代碼使用 try-with-resources 語句,它會在 try-catch 塊中的代碼完成後自動關閉連接。Stack Overflow 上對這種方法進行了長期的討論。
  • try-with-resources 語句提供對 Connection 實例的訪問,並可以在其中執行 SQL 語句;所有錯誤都會被同一個 catch 語句捕獲。

用資料庫的連接處理一些有趣的事情

日常工作中,我經常需要知道為給定的資料庫伺服器實例定義了哪些用戶,這裡我使用這個 簡便的 SQL 來獲取所有用戶的列表:

import java.sql.*;

public class Test3 {

        public static void main(String args[]) {

                // Load the driver (jar file must be on class path) [1]

                try {
                        Class.forName("org.postgresql.Driver");
                        System.out.println("driver loaded");
                } catch (Exception e1) {
                        System.err.println("couldn't find driver");
                        System.err.println(e1);
                        System.exit(1);
                }

                // Set up connection properties [2]

                java.util.Properties props = new java.util.Properties();
                props.setProperty("user","me");
                props.setProperty("password","mypassword");
                String database = "jdbc:postgresql://myhost.org:5432/test";

                // Open the connection to the database [3]

                try (Connection conn = DriverManager.getConnection(database, props)) {
                        System.out.println("connection created");

                        // Create the SQL command string [4]

                        String qs = "SELECT " +
                                "       u.usename AS "User name", " +
                                "       u.usesysid AS "User ID", " +
                                "       CASE " +
                                "       WHEN u.usesuper AND u.usecreatedb THEN " +
                                "               CAST('superuser, create database' AS pg_catalog.text) " +
                        "       WHEN u.usesuper THEN " +
                                "               CAST('superuser' AS pg_catalog.text) " +
                                "       WHEN u.usecreatedb THEN " +
                                "               CAST('create database' AS pg_catalog.text) " +
                                "       ELSE " +
                                "               CAST('' AS pg_catalog.text) " +
                                "       END AS "Attributes" " +
                                "FROM pg_catalog.pg_user u " +
                                "ORDER BY 1";

                        // Use the connection to create a statement, execute it,
                        // analyze the results and close the result set [5]

                        Statement stat = conn.createStatement();
                        ResultSet rs = stat.executeQuery(qs);
                        System.out.println("User name;User ID;Attributes");
                        while (rs.next()) {
                                System.out.println(rs.getString("User name") + ";" +
                                                rs.getLong("User ID") + ";" +
                                                rs.getString("Attributes"));
                        }
                        rs.close();
                        stat.close();

                } catch (Exception e2) {
                        System.err.println("connecting failed");
                        System.err.println(e2);
                        System.exit(1);
                }
                System.out.println("connection closed");

                // If we get here all is OK

                System.out.println("done.");
        }
}

在上述代碼中,一旦有了 Connection 實例,它就會定義一個查詢字元串(上面的注釋 [4]),創建一個 Statement 實例並用其來執行查詢字元串,然後將其結果放入一個 ResultSet 實例。程序可以遍歷該 ResultSet 實例來分析返回的結果,並以關閉 ResultSet 和 Statement 實例結束(上面的注釋 [5])。

編譯和執行程序會產生以下輸出:

me@mymachine:~/Test$ javac Test3.java
me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test3
driver loaded
connection created
User name;User ID;Attributes
fwa;16395;superuser
vax;197772;
mbe;290995;
aca;169248;
connection closed
done.
me@mymachine:~/Test$

這是在一個簡單的 Java 應用程序中使用 PostgreSQL JDBC 庫的(非常簡單的)示例。要注意的是,由於 java.sql 庫的設計方式,它不需要在代碼中使用像 import org.postgresql.jdbc.*; 這樣的 Java 導入語句,而是使用 Java 類載入器在運行時引入 PostgreSQL 代碼的方式,也正因此無需在代碼編譯時指定類路徑。

via: https://opensource.com/article/20/2/external-libraries-java

作者:Chris Hermansen 選題:lujun9972 譯者:unigeorge 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    這個站點使用 Akismet 來減少垃圾評論。了解你的評論數據如何被處理。

    More in:Linux中國

    Linux中國

    捐贈 Let's Encrypt,共建安全的互聯網

    隨著 Mozilla、蘋果和谷歌對沃通和 StartCom 這兩家 CA 公司處罰落定,很多使用這兩家 CA 所簽發證書的網站紛紛尋求新的證書籤發商。有一個非盈利組織可以為大家提供了免費、可靠和安全的 SSL 證書服務,這就是 Let's Encrypt 項目。現在,它需要您的幫助
    Linux中國

    關於Linux防火牆iptables的面試問答

    Nishita Agarwal是Tecmint的用戶,她將分享關於她剛剛經歷的一家公司(印度的一家私人公司Pune)的面試經驗。在面試中她被問及許多不同的問題,但她是iptables方面的專家,因此她想分享這些關於iptables的問題和相應的答案給那些以後可能會進行相關面試的人。 所有的問題和相應的答案都基於Nishita Agarwal的記憶並經過了重寫。 嗨,朋友!我叫Nishita Agarwal。我已經取得了理學學士學位,我的專業集中在UNIX和它的變種(BSD,Linux)。它們一直深深的吸引著我。我在存儲方面有1年多的經驗。我正在尋求職業上的變化,並將供職於印度的P
    Linux中國

    Lets Encrypt 已被所有主流瀏覽器所信任

    旨在讓每個網站都能使用 HTTPS 加密的非贏利組織 Lets Encrypt 已經得了 IdenTrust的交叉簽名,這意味著其證書現在已經可以被所有主流的瀏覽器所信任。從這個裡程碑事件開始,訪問者訪問使用了Lets Encrypt 證書的網站不再需要特別配置就可以得到 HTTPS 安全保護了。 Lets Encrypt 的兩個中級證書 ...