在 Java 中使用外部庫
Java 自帶有一組核心庫,其中包含了定義常用數據類型和相關行為的庫(例如 String
和 Date
)、與主機操作系統交互的實用程序(例如 System
和 File
),以及一些用來管理安全性、處理網路通信、創建或解析 XML的有用的子系統。鑒於核心庫的豐富性,程序員通常很容易在其中找到有用的組件,以減少需要編寫的代碼量。
即便如此,核心庫仍有一些功能上的不足,因此發現這些不足的程序員們還額外創建了很多有趣的 Java 庫。例如,Apache Commons「是一個專註於可重用 Java 組件所有方面的 Apache 項目」,提供了大約 43 個開源庫的集合(截至撰寫本文時),涵蓋了 Java 核心庫之外的一系列功能 (例如 geometry 或 statistics),並增強或替換了 Java 核心庫中的原有功能(例如 math 或 numbers)。
另一種常見的 Java 庫類型是系統組件的介面(例如資料庫系統介面),本文會著眼於使用此類介面連接到 PostgreSQL 資料庫,並得到一些有趣的信息。首先,我們來回顧一下庫的重要部分。
什麼是庫?
庫 里自然包含的是一些有用的代碼。但為了發揮用處,代碼需要以特定方式進行組織,特定的方式使 Java 程序員可以訪問其中組件來解決手頭問題。
可以說,一個庫最重要的部分是它的應用程序編程介面(API)文檔。這種文檔很多人都熟悉,通常是由 Javadoc 生成的。Javadoc 讀取代碼中的結構化注釋並以 HTML 格式輸出文檔,通常 API 的 包 在頁面左上角的面板中顯示, 類 在左下角顯示,同時右側會有庫、包或類級別的詳細文檔(具體取決於在主面板中選擇的內容)。例如,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
包來查找剛剛創建的函數的零點。但如果你不知道,就可能需要更多學習向的文檔,也許甚至是一個實際例子,來讀懂參考文檔。
這種文檔結構還有助於闡明 包 (相關 Java 類和介面定義的集合)的含義,並顯示特定庫中捆綁了哪些包。
這種庫的代碼通常是在 .jar 文件 中,它基本上是由 Java 的 jar
命令創建的 .zip 文件,其中還包含一些其他有用的信息。.jar 文件通常被創建為構建過程的端點,該構建過程編譯了所定義包中的所有 .java 文件。
要訪問外部庫提供的功能,有兩個主要步驟:
- 確保通過類路徑(或者命令行中的
-cp
參數或者CLASSPATH
環境變數),庫可用於 Java 編譯步驟(javac)和執行步驟(java
)。 - 使用恰當的
import
語句訪問程序源代碼中的包和類。
其餘的步驟就與使用 String
等 Java核心類相同,使用庫提供的類和介面定義來編寫代碼。很簡單對吧?不過也沒那麼簡單。首先,你需要了解庫組件的預期使用模式,然後才能編寫代碼。
示例:連接 PostgreSQL 資料庫
在資料庫系統中訪問數據的典型使用步驟是:
- 訪問正在使用的特定資料庫軟體代碼。
- 連接到資料庫伺服器。
- 構建查詢字元串。
- 執行查詢字元串。
- 針對返回的結果,做需要的處理。
- 斷開與資料庫伺服器的連接。
所有這些面向程序員的部分由介麵包 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
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive