杰瑞科技汇

Java如何调用Python代码?

核心思想

无论哪种方法,其核心思想都是 “进程间通信”(Inter-Process Communication, IPC),Java 程序会启动一个独立的 Python 进程,然后将数据传递给这个进程,Python 执行完代码后,再将结果返回给 Java 程序,它们之间需要一个“桥梁”来传递数据,这个桥梁通常是:

Java如何调用Python代码?-图1
(图片来源网络,侵删)
  • 标准输入/输出流:Java 向 Python 进程的标准输入写入数据,Python 从标准输入读取数据,执行后将结果写入标准输出,Java 再从标准输出读取。
  • 网络套接字:Java 启动一个 Python HTTP 服务器,通过网络 API(如 RESTful API)进行调用。
  • 共享内存:更高级的技术,性能更高,但实现复杂。

使用 ProcessBuilder (最直接、最常用)

这是 Java 内置的功能,无需任何第三方库,通过启动一个子进程来执行 Python 脚本,它通过标准输入输出来进行数据交换。

适用场景

  • 简单的脚本调用。
  • 不想引入额外依赖的 Java 项目。
  • 对性能要求不高。

优点

  • 纯 Java 实现:无需额外库。
  • 灵活性高:可以传递命令行参数,并自由控制进程。

缺点

  • 代码相对繁琐:需要手动处理输入输出流。
  • 性能开销大:每次调用都需要创建一个新进程。
  • 错误处理复杂:需要捕获 IOException,并正确关闭资源,否则可能导致进程挂起或资源泄露。
  • 数据格式受限:通常需要将 Java 对象序列化为字符串(如 JSON)进行传递,Python 端再反序列化。

详细步骤和代码示例

准备 Python 脚本

创建一个名为 script.py 的文件,它可以接收字符串参数,处理并返回结果。

# script.py
import sys
import json
# 从标准输入读取一行数据
input_data = sys.stdin.readline()
try:
    # 将 JSON 字符串解析为 Python 字典
    data = json.loads(input_data)
    name = data.get('name', 'Guest')
    age = data.get('age', 0)
    # 处理数据
    result_message = f"Hello, {name}! You are {age} years old. In 5 years, you will be {age + 5}."
    # 将结果打包成 JSON 字符串,写入标准输出
    # 注意:print 默认会换行,json.dumps 可以确保格式正确
    print(json.dumps({"status": "success", "message": result_message}))
except json.JSONDecodeError:
    print(json.dumps({"status": "error", "message": "Invalid JSON format"}))
except Exception as e:
    print(json.dumps({"status": "error", "message": str(e)}))

编写 Java 代码

Java如何调用Python代码?-图2
(图片来源网络,侵删)

在 Java 中,我们使用 ProcessBuilder 来启动 python 命令,并与之交互。

import java.io.*;
public class PythonCaller {
    public static void main(String[] args) {
        // 准备传递给 Python 脚本的数据
        String jsonData = "{\"name\": \"Alice\", \"age\": 30}";
        try {
            // 1. 创建 ProcessBuilder
            // 注意:如果你的 Python 不在系统 PATH 中,需要提供完整路径,"C:\\Python39\\python.exe"
            ProcessBuilder pb = new ProcessBuilder("python", "script.py");
            // 2. 启动进程
            Process process = pb.start();
            // 3. 获取进程的输入流和输出流
            // Java 的 OutputStream 写入到 Python 的 InputStream
            OutputStream os = process.getOutputStream();
            // Python 的 OutputStream 写入到 Java 的 InputStream
            InputStream is = process.getInputStream();
            // 获取 Python 的错误流,方便调试
            InputStream errorStream = process.getErrorStream();
            // 4. 向 Python 脚本发送数据
            os.write(jsonData.getBytes());
            os.flush(); // 确保数据被发送
            os.close(); // 关闭流,告诉 Python 没有更多数据了
            // 5. 读取 Python 脚本的返回结果
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            String line;
            StringBuilder response = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            // 6. 等待 Python 进程执行完毕
            int exitCode = process.waitFor();
            // 7. 处理结果
            if (exitCode == 0) {
                System.out.println("Python script executed successfully.");
                System.out.println("Response from Python: " + response.toString());
            } else {
                // 读取错误流
                BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorStream));
                String errorLine;
                StringBuilder errorResponse = new StringBuilder();
                while ((errorLine = errorReader.readLine()) != null) {
                    errorResponse.append(errorLine);
                }
                System.err.println("Python script failed with exit code: " + exitCode);
                System.err.println("Error from Python: " + errorResponse.toString());
            }
            // 8. 关闭所有资源
            reader.close();
            is.close();
            errorStream.close();
        } catch (IOException e) {
            System.err.println("Error starting or communicating with Python process: " + e.getMessage());
        } catch (InterruptedException e) {
            System.err.println("The process was interrupted: " + e.getMessage());
            Thread.currentThread().interrupt(); // 恢复中断状态
        }
    }
}

如何运行:

  1. script.pyPythonCaller.java 放在同一个目录下。
  2. 确保 Python 已安装并配置好环境变量。
  3. 编译并运行 Java 程序:
    javac PythonCaller.java
    java PythonCaller

预期输出:

Python script executed successfully.
Response from Python: {"status": "success", "message": "Hello, Alice! You are 30 years old. In 5 years, you will be 35."}

使用第三方库 (更简洁、更强大)

手动管理 ProcessBuilder 很繁琐,一些优秀的第三方库封装了这些细节,提供了更简洁、更强大的 API。

Java如何调用Python代码?-图3
(图片来源网络,侵删)

推荐库:Junrar (不,这是个解压库,抱歉) -> Py4JJava-Python Bridge

这里重点介绍 Py4J,它是一个非常流行和强大的库。

Py4J 的工作原理:

  1. Java 端启动一个网关,这个网关监听一个端口。
  2. Python 端通过 Py4J 库连接到这个 Java 网关。
  3. Python 端可以调用 Java 对象的方法,甚至可以创建新的 Java 对象。
  4. 反之,Java 端也可以通过 Py4J 的 GatewayServer 调用 Python 端注册的函数或对象。

它比 ProcessBuilder 更高效,因为它可以保持长连接,并且数据交换是类型安全的。

使用 Py4J 的步骤

准备 Python 脚本 (py4j_example.py)

# py4j_example.py
from py4j.java_gateway import JavaGateway, GatewayServer
# 1. 定义一个 Python 类,它的方法可以被 Java 调用
class PythonService:
    def greet(self, name):
        print(f"Python received name: {name}")
        return f"Hello from Python, {name}!"
    def calculate_average(self, numbers_list):
        if not numbers_list:
            return 0
        return sum(numbers_list) / len(numbers_list)
# 2. 创建 PythonService 实例
python_service = PythonService()
# 3. 启动 GatewayServer,将 Python 服务暴露给 Java
# 注意:默认端口是 25333
gateway = GatewayServer(python_service)
gateway.start()
print("Gateway Server Started")

编写 Java 代码 (需要添加 Py4J 依赖)

将 Py4J 的 JAR 包添加到你的项目中,如果你使用 Maven,在 pom.xml 中添加:

<dependency>
    <groupId>net.sf.py4j</groupId>
    <artifactId>py4j</artifactId>
    <version>0.10.9.7</version> <!-- 请使用最新版本 -->
</dependency>

编写 Java 代码:

import net.sf.py4j.GatewayServer;
import java.util.Arrays;
import java.util.List;
public class Py4JEntryPoint {
    // 这个方法必须是 public static 的
    public static PythonService getPythonService() {
        // 这里返回一个代理对象,Py4J 会用它来连接 Python
        // 实际的 PythonService 对
分享:
扫描分享到社交APP
上一篇
下一篇