转自:http://xiaojizhuomi.iteye.com/blog/1154108
一, Ubuntu下环境的配置
1,安装JDK
首先安装java的jdk,我是从新立得软件包中安装:
安装后的jdk的目录是:/usr/lib/jvm/java-6-sun-1.6.0.26/。安装好jdk后,最重要的就是配置环境变量。环境变量的配置有好几种方法,能设置环境变量的文件包括:/etc/profile、/etc/evironment 、~/.profile、/bashrc、~/.bashrc。这几个文件里定义的环境变量,用户使用的权限和执行的时间不一样。在/etc/environment文件里定义jdk环境变量。在该文件中添加:
(CLASSPATH以冒号分隔开。. 号表示当前目录)
然后在~/.bashrc文件中添加:
因为ubuntu默认安装了OPENJDK所以要选择所用的JDK执行下面两条命令:
- update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-6-sun-1.6.0.26/bin/java 300
- update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-6-sun-1.6.0.26/bin/javac 300
这样安装的java就可以加入java选单。再调用下面的命令就可以选择java的版本了。
在终端测试一下吧!到此jdk就安装完成了。
2.安装Jpype
从官网上下载JPype。在unbuntu下安装还需要做点小修改,修改setup.py,文件中setupLinux函数中的self.javaHome变量为自己的JDK目录:
运行python setup.py install.的话 可能会出现找不到Python.py。需要安装python-dev。如果再安装出错的话,就需要你自己排错了。当然你在安装JPype之前必须安装好Python。测试一下在终端中打开python shell,import Jpype。无错表示安装成功。
二 python调用java 示例
写个很简单的java程序并打包成jar包供python程序调用。
简单的java程序:
- package test;
- public class TestApi
- {
- public String getData(String key){
- return key;
- }
- public void printData(String key){
- System.out.println(key);
- }
- public static void main(String[] args)
- {
- System.out.println(123);
- }
- }
编译成.class文件:
将.class文件放入test文件夹中,在test文件夹外新建一个.mf文件,就叫TestApi.mf吧。并在该文件中加入:
Main-Class指的就是主程序的入口类,利用下列指令打包jar文件:
到此,java的包文件已经完成,下面就测试JPype吧:
- #!/usr/bin/env python
- #coding : utf-8
- from jpype import *
- jvmpath = getDefaultJVMPath()
- startJVM(jvmpath, "-ea", "-Djava.class.path=.")
- TA = JPackage('test').TestApi
- jd = TA()
- jd.printData('1234')
- s = jd.getData('a')
- print s
- shutdownJVM();
在Python中调用Java类库和Java代码的桥接解决方案,背后使用的机制是JNI。虽然看起来这个库近年来更新不够活跃,原作者也试图用一个叫JEmbed(还没有发布)来替代之,但就我个人的使用体会来讲,现有版本是稳定可用的。
感谢Limodou~正是当初搜到了他在ChinaUnix上的旧帖子,才知道了jpype~
1.? 基本安装
经试验,0.5.3版本不需要特殊处理,直接python setup.py install就可以了(安装过程中显示的warning可以不必理会)。用于测试是否安装成功的Hello World
from jpype import *
startJVM(getDefaultJVMPath())
java.lang.System.out.println("hello world")
shutdownJVM()
2.? 启动JVM
依靠startJVM这个函数来完成,一个使用的例子是这样的:
vmPath = jpype.getDefaultJVMPath()
jpype.startJVM(vmPath, "-Xms32m", "-Xmx256m", "-mx256m", "-Djava.class.path=/home/some-lib.jar:")
startJVM的第一个参数是JVM库所在的路径(和JAVA_HOME不是一回事儿),通常可以用jpype.getDefaultJVMPath()来自动获取系统默认JVM的路径。如果系统中安装了多个JDK,希望从中选择一个,则可以手动注明这个路径。比如Mac OSX下可以写成"/System/Library/Frameworks/JavaVM.framework/Libraries/libjvm_compat.dylib"。
剩下的都是发送给JVM的启动参数,每个逗号见是一个参数。因为这里是不支持带空格的参数写法的,所以例子里特意把classpath参数写成了-Djava.class.path=...的形式。注意这里需要手工保证参数的正确性,jpype是不会对错误的参数给出提示的,它的反应很简单,就是在后面用到这个JVM的时候报一些怎么也想不明白的错误……所以,使用jpype遇到任何问题,首先检查传给startJVM的各参数正确性。
3.? 如何调用一个Java函数
主要靠JPackage语句来实现,比如
Document = jpype.JPackage('org').w3c.dom.Document
可以把Java里面的org.w3c.dom.Document映射给Python里面的Document变量。
java和javax两个包不需要以这种方式来调用,直接类似jpype.java.lang.System.out.println()这样就可以了。
有时候我们会遇到类似"TypeError: Package org.w3c.dom.Document is not Callable"这样的错误。通常这时用到的Java指令在jar里面,而这个jar没有被正确导入,所以JVM找不到它。也就是说,遇到这种错误时,要去检查startJVM函数中的-Djava.class.path=参数的设置,通常都是因为这里的路径写错了造成的。
4.? 如果捕捉Java异常
jpype. startJVM ( jpype. getDefaultJVMPath ( ) )
try :
jpype. java . lang . Integer ( "x1" )
except jpype. JavaException , ex:
print ex. javaClass ( ) , ex. message ( )
print ex. stacktrace ( )
jpype. shutdownJVM ( )
jpype. startJVM ( jpype. getDefaultJVMPath ( ) )
try :
jpype. java . lang . Integer ( "x1" )
except jpype. JException ( jpype. java . lang . NumberFormatException ) , ex:
print ex. javaClass ( ) , ex. message ( )
print ex. stacktrace ( )
jpype. shutdownJVM ( )
5.? 如何处理Java的函数多态
Java里面是允许参数格式不同的多个同名函数的,Python里面则不允许。这样在通过jpype调用Java api里面的函数时,有时会因为参数的类型乱掉而报错。那么怎么能调用到Java里面的特定函数呢?没办法,做强制类型转换吧。
比如jpype.java.lang.System.out.println(1)实际会调用println(int),那么如果我们想调用println(byte)%,则可以写成jpype.java.lang.System.out.println(JByte(1))这样。。
6.? 如何重启JVM
jpype提供的shutdownJVM()方法实际调用的是JNI接口的unload实现,但是Sun对unload的实现有点问题,造成的结果就是jpype调用shutdownJVM()以后就没法再startJVM()了(会报错)。什么?您问关掉JVM干嘛还要重新开启它,这个折腾个什么劲?答案很简单:因为有时在未知的黑暗角落隐藏着邪恶的源头——内存泄露。。
既然jpype没法重启JVM,那么只好把jpype放到processing里面来用,需要重启时,就杀掉当前进程,重新启动一个新进程好了。。(processing安装很简单,Python 2.6官方发行版已经带了,之前的版本则可以easy_install processing)
下面给出一个processing下用jpype的例子:
import processing
def java_loop ( pipe, id ) :
jpype. startJVM ( jpype. getDefaultJVMPath ( ) )
while True :
jpype. java . lang . System . out . println ( pipe. recv ( ) + ' ' + id )
pipe. send ( None )
head1, head2 = processing. Pipe ( )
p = processing. Process ( target = java_loop, args = [ head2, '(JVM 1)' ] )
p. setDaemon ( True )
p. start ( )
head1. send ( "Hello message from" )
head1. recv ( )
p. terminate ( )
head1, head2 = processing. Pipe ( )
p = processing. Process ( target = java_loop, args = [ head2, '(JVM 2)' ] )
p. setDaemon ( True )
p. start ( )
head1. send ( "Hello message from" )
head1. recv ( )
因为在Jython 调用 Java 碰壁全纪录中已经有了“出色”碰壁经历,此处仿照其大致过程
命令行下无差异,Eclipse中略有差异(classpath的设置方式不同所致)
JavaClass 的定义
public class JavaClass {
private String str = "";
public JavaClass() {
this.str = "JavaClass Init";
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
一、命令行模式
1、进入 python 目录,查看当前目录下只有一个JavaClass.java文件
D:\>cd python
D:\python>dir
2012-03-13 09:59 <DIR> .
2012-03-13 09:59 <DIR> ..
2012-03-13 09:59 227 JavaClass.java
1 个文件 227 字节
2 个目录 37,943,169,024 可用字节
2、启动Python命令行模式(Python版本是 2.7)
D:\python>python
3、导入 JavaClass时,提示找不到JavaClass模块
>>> from jpype import *
>>> jvmPath = getDefaultJVMPath()
>>> startJVM(jvmpath)
>>> startJVM(jvmPath)
>>> JavaClass = JClass("JavaClass")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\site-packages\jpype\_jclass.py", line 54, in JClass
raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name)
jpype._jexception.ExceptionPyRaisable: java.lang.Exception: Class JavaClass not found
原因:Python中导入的是.class文件,而JavaClass.java尚未编译生成 .class文件
4、Ctrl-Z退出 Python, 编译 JavaClass.java
D:\python>javac JavaClass.java
D:\python>dir
2012-03-13 10:12 <DIR> .
2012-03-13 10:12 <DIR> ..
2012-03-13 10:12 443 JavaClass.class
2012-03-13 09:59 227 JavaClass.java
2 个文件 670 字节
2 个目录 37,943,169,024 可用字节
5、按第2步,再次进入Python模式后导入 JavaClass时未报异常,说明成功导入,简单调用情况如下
D:\python>python
>>> from jpype import *
>>> jvmPath = getDefaultJVMPath()
>>> startJVM(jvmpath)
>>> startJVM(jvmPath)
>>> JavaClass = JClass("JavaClass")
>>> jc = JavaClass()
>>> jc.str // 调用get方法
'JavaClass Init'
>>> jc.str = "JavaClass Set" // 调用set方法
>>> jc.str
'JavaClass Set'
二、eclipse IDE 环境
workplace 是d:\python,项目名称是mython,其结构如下
mython
......
bin
javaDemo
JavaClass.class
pythonDemo
Py2Ja.py
src
javaDemo
JavaClass.java
pythonDemo
Py2Ja.py
1、JavaClass.java同上(但是第一行多了一个package javaDemo的定义)
2、编辑Py2Ja.py如下:
from jpype import *
jvmPath = getDefaultJVMPath()
startJVM(jvmPath)
JavaClass = JClass("javaDemo.JavaClass")
jc = JavaClass()
print jc.str
jc.str = "JavaClass IDE"
print jc.str
shutdownJVM()
测试运行时报异常
Traceback (most recent call last):
File "D:\python\mython\src\pythonDemo\Py2Ja.py", line 4, in <module>
JavaClass = JClass("javaDemo.JavaClass")
File "C:\Python27\lib\site-packages\jpype\_jclass.py", line 54, in JClass
raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name)
jpype._jexception.ExceptionPyRaisable: java.lang.Exception: Class javaDemo.JavaClass not found
原因:找不到javaDemo是因为“当前路径”下找不到JavaDemo文件或者目录
解决方法:手工添加JVM的启动参数-Djava.class.path ='D:\python\Mython\src
3、再次修改后如下:
from jpype import *
jvmPath = getDefaultJVMPath()
classPath = "f:\workplace\Python\src"
jvmArg = "-Djava.class.path="+classPath
startJVM(jvmPath, jvmArg)
JavaClass = JClass("javaDemo.JavaClass")
jc = JavaClass()
print jc.str
jc.str = "JavaClass IDE"
print jc.str
shutdownJVM()
测试运行时仍然报异常
Traceback (most recent call last):
File "F:\workplace\Python\src\pythonDemo\Py2Ja.py", line 6, in <module>
JavaClass = JClass("javaDemo.JavaClass")
File "C:\Python27\lib\site-packages\jpype\_jclass.py", line 54, in JClass
raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name)
jpype._jexception.ExceptionPyRaisable: java.lang.Exception: Class javaDemo.JavaClass not found
与2中的异常一样:找不到JavaClass。
原因:我们已经设置了-Djava.class.path ='D:\python\Mython\src,仍然找不到JavaClass,只能说明是找不到JavaClass.class文件
在命令行中第3步中提到Python文件导入的应该是.class文件,在看前面mython项目的整个目录发现,class文件在mython\bin\javaDemo中。也就是说可能是因为编译器找到了JavaClass的定义即 JavaClass.java,但是找不到它的.class文件。
那java文件对应的class文件为什么不是在src包下的JavaDemo中,而是在bin包下的JavaDemo中?
右击项目mython->Properties->Java Build Path->source最下面有default output folder中默认的是mython\bin,且这个目录在 Package Explorer视图下是看不到的,在Navigator视图中可以看到。
4、将default output folder 修改为 mython\src,目录结构变化如下
mython
......
src
javaDemo
JavaClass.class
JavaClass.java
pythonDemo
Py2Ja.py
测试运行成功:
JavaClass Init
JavaClass IDE
JVM activity report :
classes loaded : 20
JVM has been shutdown
总结:对于新手不太习惯命令行式的Java程序调试,IDE虽然简单方便,但是都有自己默认的规则。这些规则新手可能注意不到,所以在IDE环境中照搬程序源码的时候,总会报一些奇奇怪怪的错误,使得本来简单的问题看上去很复杂。究其原因:1、对开发环境不熟悉、2、java基础不扎实。
特殊情况备注:
1、***.py文件运行时,默认路径即该文件的所在路径,导入***.class时的路径就变成了 ***.py所在的包 + from中的包,即默认.class文件是在 ***.py所在的包 的 子包中,所以在调用Java文件时,需要特别添加class文件所在路径。即使Java文件和Python文件在同一个包中,Java文件仍然在某个包中,测试运行时仍然要设置java文件所在的包最上级所在的目录,一般为 src。
2、将Java文件和Python文件都放在 src 下时,虽然目录结构中显示一个叫"(default package)",但是在Java文件的内部是没有包的设定的,这种情况下运行Python文件时,可不手工设置class文件所在路径,因为此时 Python文件的所在目录为src,而Java文件也在src中,且没有包名引起的问题。
刚刚接触 Python,因为基础不太扎实,期间诸多碰壁,仅此记录。
成长的道路上不怕跌倒,怕的是的跌倒了再也爬不起来。为自己加油!