在 Java 中使用外部库

外部库填补了 Java 核心库中的空白。
105 位读者喜欢此文章。
books in a library, stacks

ktchang16 通过 Flickr。CC BY 2.0

Java 附带了一组核心库,包括定义常用数据类型和相关行为的库,例如 StringDate;与主机操作系统交互的实用程序,例如 SystemFile;以及用于管理安全性、处理网络通信以及创建或解析 XML 的有用子系统。 鉴于这组核心库的丰富性,通常很容易找到必要的位和片段,以减少程序员为解决问题而必须编写的代码量。

即便如此,仍有许多有趣Java 库是由发现核心库中存在差距的人员创建的。 例如,Apache Commons“是一个专注于可重用 Java 组件各个方面的 Apache 项目”,并提供大约 43 个开源库的集合(截至撰写本文时),涵盖 Java 核心之外的一系列功能(例如 geometrystatistics)或增强或替换 Java 核心中的功能的功能(例如 mathnumbers)。

另一种常见的 Java 库是系统组件的接口,例如,到数据库系统的接口。 本文着眼于使用这样的接口来连接到 PostgreSQL 数据库并获取一些有趣的信息。 但首先,我将回顾一下库的重要组成部分。

什么是库?

当然,库必须包含一些有用的代码。 但要发挥作用,需要以 Java 程序员可以访问组件以解决手头问题的方式组织代码。

我将大胆地声称,库最重要的部分是其应用程序编程接口 (API) 文档。 许多人对这种文档很熟悉,它通常由 Javadoc 生成,Javadoc 读取代码中的结构化注释并生成 HTML 输出,该输出在页面的左上角面板中显示 API 的包;左下角显示其类;右侧显示库、包或类级别的详细文档(具体取决于主面板中的选择)。 例如,Apache Commons Math 的 API 文档的顶层看起来像

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 文件的构建过程的端点。

访问外部库提供的功能有两个主要步骤

  1. 确保 Java 编译步骤 - javac - 和执行步骤 - java - 可以通过类路径(命令行上的 -cp 参数或 CLASSPATH 环境变量)访问该库。
  2. 使用适当的 import 语句来访问程序源代码中的包和类。

剩下的就像使用 Java 核心类(例如 String)进行编码一样 - 使用库提供的类和接口定义编写代码。 简单,是吧? 好吧,可能没那么简单; 首先,您需要了解库组件的预期使用模式,然后才能编写代码。

示例:连接到 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 来定义数据库,该 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 实例中,它可以迭代该实例来分析返回的结果,最后通过关闭 ResultSetStatement 实例(上面的注释 [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 import 语句。 因此,无需在编译时指定类路径。 相反,它使用 Java 类加载器在运行时引入 PostgreSQL 代码。

下一步阅读什么

在 Java 中初始化数组

数组是一种有用的数据类型,用于管理最适合在连续内存位置中建模的集合元素。 这是有效使用它们的方法。

标签
Chris Hermansen portrait Temuco Chile
自从 1978 年从不列颠哥伦比亚大学毕业以来,我就几乎总是伴随着某种计算机,自 2005 年以来我一直是一名全职 Linux 用户,从 1986 年到 2005 年我一直是一名全职 Solaris 和 SunOS 用户,在此之前是 UNIX System V 用户。

评论已关闭。

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.