Java

解决 Java 异常 “ClassCastException: Ljava.lang.Object; cannot be cast to Ljava.lang.Integer”

1、概览 在 Java 中,数组是语言的基本组成部分,它提供了一种结构化的方式来存储相同类型的多个值。然而,在使用数组和类型转换时,我们有时会遇到意想不到的运行时异常。 当我们试图将 Object[] 数组转换为特定数组类型(如 Integer[])时,就会出现这样的问题。这会导致 ClassCastException 异常,这可能会让很多人感到困惑。 本文将带你了解出现这种异常的原因,从而了解 Java 数组的基本机制,并学习如何在代码中避免此类错误。 2、问题介绍 像往常一样,先通过一个例子来了解这个问题: Integer[] convertObjectArray() { Object[] objArray = new Object[3]; objArray[0] = 1; objArray[1] = 2; objArray[2] = 3; return (Integer[]) objArray; } 在上述方法中,我们在一个 Object[] 数组中插入了三个 int 值。由于 Object[] 数组只包含 Integer,我们尝试将其转换为 Integer[] 数组。 在测试中调用该方法,看看会发生什么: Exception ex = assertThrows(ClassCastException.class, () -> convertObjectArray()); LOG.error("The exception stacktrace:", ex); 可以看到,调用该方法会抛出 ClassCastException。在输出中,还可以看到异常的详细信息: java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Integer; ... at .

使用 Java ServerSocket 实现简单的 HTTP 服务器

1、概览 HTTP 服务器通常用于为发起请求的客户端提供资源。Java 中有一系列生产级 Web 服务器。 本文将带你了解如何使用 ServerSocket 类实现一个简单的 Web 服务器,从而了解 HTTP 服务器是如何工作的。注意,此服务器仅用于教学目的,不适合用于生产。 2、ServerSocket 基础 首先,服务器会监听客户端应用程序的连接。客户端应用程序可以是浏览器、其他程序、API 工具等。连接成功后,服务器会响应客户端连接,向客户端提供资源。 ServerSocket 类提供了在指定端口上创建服务器的方法。它使用 accept() 方法监听指定端口上的传入连接。 accept() 方法会阻塞,直到建立连接,并返回一个 Socket 实例。Socket 实例为服务器和客户端之间的通信提供了对输入和输出流的访问。 3、创建 ServerSocket 实例 首先,创建一个监听指定端口的 ServerSocket 对象: int port = 8080; ServerSocket serverSocket = new ServerSocket(port); 接着,使用 accept() 方法接受一个传入连接: while (true) { Socket clientSocket = serverSocket.accept(); // ... } 如上,使用 while 循环等待连接。然后,调用 ServerSocket 对象上的 accept() 方法来监听和接受连接。 连接建立后,该方法会返回一个 Socket 对象,允许服务器和客户端通过已建立的网络进行通信。 4、处理输入和输出 通常,服务器接收来自客户端的输入并发送适当的响应。我们可以使用 Socket 类的 getInputStream() 和 getOutputStream() 方法,通过提供流来读取和写入数据到客户端,从而进行通信。

在 Java 程序中运行 JAR

1、简介 在开发 Java 项目时,我们可能会遇到这样的情况:需要在 Java 程序中启动一个单独的进程运行外部 JAR(可执行 JAR)并查看输出,或者可能想要执行外部 JAR 中带有 main 方法的类文件。 2、运行可执行 JAR 可执行 JAR 是一种 JAR 文件类型,它包含一个设置了 Main-Class 属性的清单(manifest)文件。该属性指向应首先运行(使用 main 方法)的类文件。 我们可以使用 java -jar <example.jar> 命令从命令行运行该 JAR。也可以在 Java 程序中使用 ProcessBuilder 来实现类似的结果。 下面的代码演示了如何以编程式将可执行 JAR 作为单独进程运行,并在控制台中查看输出结果: @Test public void givenRunnableJar_whenExecuted_thenShouldRunSuccessfully() { Process process = null; try { String jarFile = new File(Objects.requireNonNull(getClass().getClassLoader() .getResource(RUNNABLE_JAR_PATH)) .toURI()).getAbsolutePath(); ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", jarFile); processBuilder.redirectErrorStream(true); process = processBuilder.start(); try (InputStream inputStream = process.getInputStream()) { byte[] output = inputStream.

用 Java 调用 GoLang 函数

1、简介 众所周知,Java 和 Go 是两种著名的编程语言,各自在不同的领域表现出色。Java 以其可移植性和广泛的生态系统而闻名,而 Go 则以其简洁性、性能和高效的并发处理而著称。在某些情况下,将两种语言的优势结合起来,可以开发出更强大、更高效的应用程序。 本文将带你了解如何利用 Java Native Access(JNA)库来弥合两种语言之间的差距,从而在不编写任何 C 代码的情况下从 Java 中调用 Go 函数。 2、用 JNA 架起 Java 和 Go 之间的桥梁 传统上,从 Java 调用本地代码需要用 C 语言编写 Java 本地接口(JNI)代码,这会增加复杂性和开销。然而,随着 Java Native Access(JNA)库的出现,可以直接从 Java 调用本地共享库,而无需深入研究 C 代码。这种方法简化了集成过程,允许开发人员在 Java 应用程序中无缝利用 Go 的功能。 要理解这种集成是如何工作的,首先,要了解 Java Native Access(JNA)库在连接 Java 和 Go 方面的作用。具体来说,JNA 提供了一种从 Java 代码调用本地共享库函数的直接方法。通过将 Go 代码编译到共享库中并导出必要的函数,Java 可以与 Go 函数进行交互,就像它们是自己生态系统的一部分一样。 本质上,这一过程包括编写 Go 函数,将其编译成共享库,然后使用 JNA 创建映射到这些函数的相应 Java 接口。 2、项目设置 首先,设置项目环境。这包括配置集成所需的构建工具和依赖项。在本例中,我们需要以下组件: Java Development Kit(JDK):用于编译和运行 Java 代码。 Go 语言环境:用于编写和编译 Go 代码。 Java Native Access(JNA)库:作为 Maven 项目的一个依赖项包含在内。 构建工具:Maven 适用于 Java,Go 编译器适用于 Go 代码。 添加 JNA 库到 maven 依赖:

在 Java 中将 JAR 添加到 Classpath 的几种方法

1、简介 在开发 Java 项目时,我们经常依赖外部库来开发应用。 本文将带你了解把这些第三方库作为 JAR 添加到 classpath 下的不同方法。 2、在命令行中使用 -cp 或 -classpath 选项 如果我们从命令行启动程序,那么可以在命令中指定 JAR 依赖: java -cp /path/to/jar/file.jar com.example.MyClass 如上,/path/to/jar/file.jar 是 JAR 文件的路径,com.example.MyClass 是要执行的类。 还可以添加多个 jar: java -cp "lib/jar1.jar:lib/jar2.jar" com.example.MyClass 3、在命令行中使用 CLASSPATH 在某些情况下,在同一台机器上运行的多个 Java 程序可能需要用同一个 JAR。 在这种情况下,我们可以在 macOS/Linux 中设置 CLASSPATH 环境变量,而不是在每条命令中指定 classpath: export CLASSPATH=/path/to/jar/file.jar Windows 系统的设置方法如下: set CLASSPATH=C:\path\to\jar\file.jar 设置 CLASSPATH 后,我们就可以直接运行 Java 程序,而无需指定 -classpath 选项。 注意,如果我们以这种方式设置 CLASSPATH,它只对该终端会话有效。一旦终端关闭,设置就会丢失。所以,我们可以将 classpath 添加到环境变量,使其永久有效。 4、在 MANIFEST.MF 文件中指定 Classpath 当我们创建一个独立可执行的应用时,最佳实践是将所有依赖的 JAR 都打包到一个 JAR 中。

解决 Java 异常:“class file has wrong version”

1、概览 在处理 Java 项目时,建立新项目或更新或添加依赖时常见的错误是 “class file has wrong version(类文件版本错误)”。 本文将带你了解出现这个错误的原因以及解决办法。 2、原因 要了解根本原因,我们需要更多地了解类文件。 每当我们编译 Java 代码时,都会为编译文件中包含的每个类创建一个 .class 文件。这些 .class 文件的部分元数据是一个版本号,该版本号与用于编译代码的 Java 当前主版本号相对应。 对于最常见的 Java 版本,这些类的版本号分别为:Java 21 = 65.0、Java 17 = 61.0、Java 11 = 55.0 和 Java 8 = 51.0。如果我们使用的是不同版本的 Java,可以根据需要加减一个版本号,从而计算出相应的类文件版本号。 在运行时,Java 会查看每个 .class 文件,并要求运行环境的版本号大于或等于编译 .class 文件时使用的版本号。这种版本号要求是出于 Java 的向后兼容性机制。也就是说,如果我们运行的是 Java 17 代码,我们就可以使用任何用 Java 17 或以前版本的 Java 编译的类文件。但是,如果我们遇到一个用 Java 21 编译的 .class 文件,就会遇到 “class file has wrong version 65.0, should be 61.0”(class 文件版本号错误,应为 61.

Java 中的 Objects.requireNonNull() 指南

1、简介 NullPointerException(空指针异常)是 Java 最常见的异常之一。它发生在访问或与指向空(null)的引用变量交互时。验证对象非空(尤其是当它们是方法或构造函数中的参数时)对于确保代码的健壮性和可靠性非常重要。我们可以通过编写自己的 null 检查代码、使用第三方库或选择更方便的方法来实现这一点。 本文将带你了解 Java 中内置的一种灵活的解决方案 - Objects.requireNonNull()。 2、Null 值处理 简单回顾一下,有很多方法可以避免手动空值检查。我们可以从各种库中进行选择,而不是用 if 语句来封装我们的代码,这可能会导致错误和耗时。Spring、Lombok(@NonNull) 和 Uber 的 NullAway 就是其中的几种。 相反,如果我们想保持一致性,避免在代码库中引入额外的库,或者使用普通 Java,我们可以选择 Optional 类或 Objects 方法。虽然 Optional 可简化空值处理,但它往往会增加编码开销,并使简单用例的代码变得杂乱无章。由于它是一个独立的对象,因此也会消耗额外的内存。此外,Optional 只能将 NullPointerException 转换为 NoSuchElementException,即不能解决缺陷。 而,Objects 类提供了静态工具方法,可以更高效地处理对象。该类在 Java 7 中引入,并在 Java 8 和 Java 9 中多次更新,它还提供了在执行操作前验证条件的方法。 3、Objects.requireNonNull() 的优点 Objects 类通过提供一组 requireNonNull() 静态方法,简化了对 null 值的检查和处理。这些方法提供了一种简单明了的方法,可在使用前检查对象是否为空。 requireNonNull() 方法的主要目的是检查对象引用是否为空,如果为空,则抛出 NullPointerException 异常。通过明确抛出异常,我们传达了检查的意图,使代码更易于理解和维护。这也会告知开发人员和维护人员,该错误是故意的,这有助于防止随着时间的推移对行为进行无意的更改。 Objects 类是 java.util 包的一部分,因此无需外部库或依赖关系即可轻松访问。它的方法都有详细的文档说明,为开发人员提供了正确使用的明确指导。 4、使用案例 现在,来看看 requireNonNull() 方法的各种用例(包括其各种重载方法)。 这些方法允许在不同情况下处理空值,从简单的空值检查到带有自定义信息的更复杂的验证。 此外,还有两个方法支持默认值 - requireNonNullElse() 和 requireNonNullElseGet()。

把 Future 转换为 Completablefuture

1、简介 本文将带你了解如何将 Future 转换为 CompletableFuture。通过这种转换,我们可以利用 CompletableFuture 的高级功能,如非阻塞操作、链式任务和更好的错误处理,同时仍可使用返回 Future 的 API 或库。 2、为什么要把 Future 转换为 CompletableFuture? Java 中的 Future 接口表示异步计算的结果。它提供了检查计算是否完成、等待计算完成和检索结果的方法。 不过,Future 也有其局限性,比如阻塞调用需要使用 get() 来获取结果。此外,它还不支持链式调用多个异步任务或处理回调。 而,Java 8 中引入的 CompletableFuture 解决了这些缺陷。它通过用于任务链和回调的 thenApply() 和 thenAccept() 等方法支持非阻塞操作,并使用 exceptionally() 进行错误处理。 通过将 Future 转换为 CompletableFuture,我们可以在使用返回 Future 的 API 或库时利用这些功能。 3、逐步转换 来看看如何将 Future 转换为 CompletableFuture。 3.1、使用 ExecutorService 模拟 Future 要了解 Future 如何工作,我们首先要使用 ExecutorService 模拟异步计算。ExecutorService 是一个用于管理和调度独立线程中任务的框架。这将有助于我们理解 Future 的阻塞特性: @Test void givenFuture_whenGet_thenBlockingCall() throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.

使用 Java 在 PostgreSQL 中存储日期和时间

1、简介 在数据库中存储日期(Date)和时间(Time)信息是软件开发中的一项常见任务。由于有许多不同的格式、时区和存储格式,处理日期和时间可能是一项复杂的任务,如果处理不慎,可能会导致许多问题。 本文将带你了解 Java Date / Time API 提供的日期和时间类,以及 PostgreSQL 如何持久化这些类。 2、设置 本文使用 Spring Boot 和 Spring Data JPA 在 PostgreSQL 数据库中持久化日期和时间值。 首先,创建一个实体,其中包含 Java Date / Time API 中不同日期和时间类的字段: @Entity public class DateTimeValues { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; private Date date; private LocalDate localDate; private LocalDateTime localDateTime; private Instant instant; private ZonedDateTime zonedDateTime; private LocalTime localTime; private OffsetDateTime offsetDateTime; private java.sql.Date sqlDate; // Getter / Setter 省略 } 此外,还要添加一个默认构造函数,以固定时间初始化所有日期/时间字段:

解决 Java 异常 “java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking”

1、简介 Spring Webflux 是一个非阻塞的 Web 框架,从底层开始构建,旨在利用多核、下一代处理器的优势,处理大量并发连接(既然是非阻塞框架,线程就不应该被阻塞)。 本文将带你了解在使用 Spring Webflux 时常犯的一个错误。 2、Spring Webflux 线程模型 为了更好地理解这个问题,我们需要了解 Spring Webflux 的线程模型。 在 Spring Webflux 中,一个小型工作线程池负责处理传入请求。这与 Servlet 模型不同,在 Servlet 模型中,每个请求都有一个专用线程。因此,框架会保护(隔离)这些接受(处理)请求的线程。 理解了这一点后,继续往下看。 3、通过线程阻塞了解 IllegalStateException 让我们通过一个示例来了解 Spring Webflux 中何时以及为何会出现异常:“java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread”。 以文件搜索 API 为例。该应用从文件系统读取文件,并在文件中搜索用户提供的文本。 3.1、FileService 先定义一个 FileService 类,它能以字符串形式读取文件内容: @Service public class FileService { @Value("${files.base.dir:/tmp/bael-7724}") private String filesBaseDir; public Mono<String> getFileContentAsString(String fileName) { return DataBufferUtils.read(Paths.get(filesBaseDir + "/" + fileName), DefaultDataBufferFactory.sharedInstance, DefaultDataBufferFactory.DEFAULT_INITIAL_CAPACITY) .