我将从简单到复杂,介绍几种主流的方法,并详细说明如何传递参数。

通过命令行执行 (最简单、最常用)
这是最直接的方法,Java像在终端里一样调用Python解释器,并传递参数,参数通过命令行的形式传递给Python脚本。
核心思路
- Java端: 使用
Runtime.getRuntime().exec()或ProcessBuilder来启动一个新进程。 - 参数传递: 将参数作为字符串数组的一部分传递给Python命令。
- Python端: 使用
sys.argv来接收从命令行传递过来的参数。
Java 代码
ProcessBuilder 是比 Runtime.exec() 更现代、更灵活的方式,推荐使用。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class JavaCallPythonWithArgs {
public static void main(String[] args) {
// 1. 定义要传递给Python脚本的参数
// 注意:参数必须是字符串数组
String[] pythonArgs = {
"python", // Python解释器命令
"path/to/your_script.py", // Python脚本的绝对路径
"arg1", // 第一个参数
"100", // 第二个参数
"3.14" // 第三个参数
};
try {
// 2. 使用ProcessBuilder启动进程
ProcessBuilder pb = new ProcessBuilder(pythonArgs);
pb.redirectErrorStream(true); // 将错误流合并到输出流,方便统一处理
Process process = pb.start();
// 3. 读取Python脚本的输出
// 这非常重要,否则缓冲区满了,Java可能会卡住
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
System.out.println("Python脚本输出:");
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 4. 等待Python脚本执行完毕
int exitCode = process.waitFor();
System.out.println("\nPython脚本执行完毕,退出码: " + exitCode);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt(); // 恢复中断状态
}
}
}
Python 代码 (your_script.py)
import sys
import json
def main():
# sys.argv 是一个列表,包含所有命令行参数
# sys.argv[0] 是脚本名称本身
# sys.argv[1] 是第一个实际参数,以此类推
if len(sys.argv) < 4:
print("Usage: python your_script.py <arg1> <arg2> <arg3>")
sys.exit(1) # 非正常退出
arg1 = sys.argv[1]
arg2 = sys.argv[2]
arg3 = sys.argv[3]
print(f"Python脚本接收到的参数:")
print(f"参数1 (字符串): {arg1}")
print(f"参数2 (整数): {arg2}")
print(f"参数3 (浮点数): {arg3}")
# Python处理数据...
# 计算 arg2 的平方
result = int(arg2) * int(arg2)
print(f"计算结果 ({arg2}^2): {result}")
# 如果Java需要获取处理结果,可以打印到标准输出
# Java端会捕获这个输出
print("--- 处理完成,准备返回数据 ---")
# 可以返回JSON格式的数据,方便Java解析
output_data = {
"status": "success",
"original_arg": arg3,
"processed_value": float(arg3) * 2
}
print(json.dumps(output_data))
if __name__ == "__main__":
main()
优点
- 简单直观:无需额外依赖,是所有Java环境都支持的基础功能。
- 灵活性高:可以调用任何Python脚本,无论其内部结构如何。
缺点
- 性能开销:每次调用都需要创建一个新的进程,启动成本较高,不适合高频调用。
- 数据类型限制:所有参数在传递时都是字符串,Python端需要自己进行类型转换。
- 交互复杂:与Python脚本的交互主要通过标准输入/输出流,对于复杂的数据结构(如对象、列表)序列化和反序列化比较麻烦。
使用 IPC (进程间通信) - 管道
当数据量较大或交互频繁时,仅仅通过打印到标准输出是不够的,可以使用命名管道或套接字进行更高效的进程间通信。
核心思路
- Java端: 启动Python进程,并获取其输入流和输出流。
- 数据传递: Java通过Python进程的输入流 (
getOutputStream()) 发送数据。 - Python端: 从标准输入 (
sys.stdin) 读取数据,处理完毕后,将结果写入标准输出 (sys.stdout)。 - Java端: 从Python进程的输出流 (
getInputStream()) 读取结果。
Java 代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class JavaCallPythonWithIPC {
public static void main(String[] args) {
String[] pythonArgs = {"python", "path/to/your_ipc_script.py"};
try {
Process process = Runtime.getRuntime().exec(pythonArgs);
// 获取Python进程的输入流,用于向Python发送数据
PrintWriter writer = new PrintWriter(new OutputStreamWriter(process.getOutputStream()));
// 获取Python进程的输出流,用于读取Python的返回结果
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
// 1. Java向Python发送数据
System.out.println("Java向Python发送数据...");
String dataToSend = "This is a complex string from Java with 123 and 45.6";
writer.println(dataToSend); // 发送一行数据
writer.flush(); // 确保数据被立即发送
// 2. Java从Python读取返回数据
System.out.println("等待Python处理并返回结果...");
String resultFromPython = reader.readLine(); // 读取Python返回的第一行
System.out.println("Python返回的结果: " + resultFromPython);
// 关闭流
writer.close();
reader.close();
// 等待进程结束
int exitCode = process.waitFor();
System.out.println("Python脚本执行完毕,退出码: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Python 代码 (your_ipc_script.py)
import sys
import json
def main():
# 从标准输入读取Java发送的数据
input_data = sys.stdin.readline().strip()
print(f"Python从标准输入读取到: {input_data}")
# 模拟数据处理
processed_data = {
"original_input": input_data,
"length": len(input_data),
"uppercase": input_data.upper()
}
# 将处理后的结果以JSON格式写入标准输出
# Java端会读取这个输出
print(json.dumps(processed_data))
if __name__ == "__main__":
main()
优点
- 双向通信:可以实现Java和Python之间的双向数据流。
- 适合大数据:可以分块读写数据,不像
System.out.println那样容易缓冲区溢出。
缺点
- 实现更复杂:需要手动管理输入输出流。
- 数据序列化:仍然需要约定好数据格式(如JSON、XML)来传递复杂数据。
使用专门的Java-Python桥接库 (最强大、最专业)
对于需要深度集成、高性能调用的情况,可以使用专门的库来创建一个“桥梁”。

核心思路
这些库通常在JVM中嵌入一个Python解释器,或者通过高性能的协议(如gRPC、ZeroMQ)进行通信,这使得Java可以像调用本地Java方法一样调用Python函数,并传递和接收Java对象。
推荐库
-
JPP (Java Python Process)
- 一个相对较新的库,旨在简化Java和Python之间的调用,支持同步和异步调用,并能处理复杂数据类型的转换。
- 官网: [https://github.com/b Bradleybossard/JPP](https://github.com/b Bradleybossard/JPP)
-
PyJnius
- 一个非常强大的库,它使用JNI (Java Native Interface) 来在Android或桌面Java应用中嵌入Python。
- 你可以动态导入Python模块,调用Python函数,并能自动在Java和Python对象之间进行转换(如
List<->list,Map<->dict)。 - 官网: https://github.com/kivy/pyjnius
-
JPype
(图片来源网络,侵删)- 一个成熟的库,允许在Python虚拟机中访问Java类,反之亦然,它提供了一个进程级的桥梁。
- 官网: http://jpype.readthedocs.io/
示例 (概念性,以PyJnius为例)
Python 代码 (my_module.py)
def process_data(data_list):
print(f"Python函数接收到Java列表: {data_list}")
# 对列表进行处理
squared = [x * x for x in data_list]
return squared
Java 代码
// 需要先引入PyJnius的jar包
// import org.jnius.*;
public class PyJniusExample {
public static void main(String[] args) {
// 1. 初始化Python环境 (PyJnius会处理)
// Python.start(new String[]{"python.path"});
try {
// 2. 导入Python模块
PyObject myModule = Py.importModule("my_module");
// 3. 准备Java参数
List<Integer> javaList = Arrays.asList(1, 2, 3, 4, 5);
// 4. 调用Python函数,并传递Java对象
// PyJnius会自动进行类型转换
PyObject result = myModule.callAttr("process_data", javaList);
// 5. 获取返回的Python对象,并转换回Java对象
@SuppressWarnings("unchecked")
List<Integer> resultList = (List<Integer>) result.toJava(List.class);
System.out.println("Java接收到Python返回的列表: " + resultList);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6. 关闭Python环境
// Python.stop();
}
}
}
优点
- 高性能:避免了进程创建的开销,数据传递直接在内存中进行。
- 无缝集成:可以传递和接收复杂的数据结构(列表、字典、自定义对象),无需手动序列化。
- 类型安全:库通常能处理类型转换,使代码更健壮。
缺点
- 依赖复杂:需要引入额外的库,并可能需要配置Python环境。
- 平台限制:某些库(如PyJnius)对平台(如Android)有更好的支持。
- 学习成本:需要学习特定库的API。
总结与选择建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 命令行执行 | 简单、无额外依赖、通用性强 | 性能差、交互复杂、数据类型受限 | 快速原型、一次性任务、简单的脚本调用 |
| IPC (管道) | 双向通信、适合大数据流 | 实现复杂、需手动序列化 | 需要Java和Python多次交互的场景 |
| 桥接库 | 高性能、无缝集成、类型安全 | 依赖库、配置复杂、学习成本高 | 生产环境、高频调用、复杂应用集成 |
如何选择?
- 如果你只是想快速运行一个Python脚本,传递几个简单的参数:使用方法一(命令行执行),它是最快、最直接的解决方案。
- 如果你的Java和Python需要频繁交换大量数据:考虑使用方法二(IPC),并采用JSON等标准格式进行序列化。
- 如果你正在构建一个复杂的企业级应用,Java和Python需要深度耦合,并且性能是关键:投入时间学习并使用方法三(桥接库,如PyJnius或JPype),这是最专业、最强大的方案。
