反编译工具地址:

https://github.com/java-decompiler/jd-gui/releases

你想知道的JPype全在这里∞

 
先总结自己趟的坑
1. python进程是64位进程,jvm.dll是32位dll,此时startJVM会直接崩溃,解决方法就是把python换成32位的python
2. 神坑:安装好以后,在命令行python里面输入from jpype import * 没问题,但是我把这句话 写入 jpype.py ,然后执行 python jpype.py就不行了。为什么呢?牛人已经看明白了,是我的测试脚本名字叫jpype.py,这个问题各种搜都没搜到,后来突然意识到名字的问题。改成testjpype.py或者其他名字就行了。
 
另外,执行jar包程序什么的也找了半天,下面这个文章很全,为防止原文丢失,搬过来。
 
 
 
 
来源:https://liuliqiang.info/post/allthing-about-jpype/

你想知道的JPype全在这里

概述

可能很多人都听说过 Jython,或者也叫 JPython,如果没听过的话,我大概介绍一下,Jyhton 就是在 Java 虚拟机(JVM) 上运行 python 代码的一种语言,代码是用 python 编写的,然后编译成 JVM 可以解析的字节码,运行在 JVM 上。

很明显,Jython 是给 Java程序员 运行 Python 程序用的,如果是 Python程序员 想运行 Java代码 怎么办?方式还是很多的,有 JPype/JEP/JPE 等等,这些项目都可以在 Python 运行环境执行 Java代码,那么,本文就以 JPype 为主题,介绍一下如何在 Python代码中运行 Java 代码,包括但不限于直接编写 Java代码加载jar包 和 执行jar包

环境介绍

在开始之前,先介绍一下本文使用的 Python 和 Java 运行环境

  1. Java 版本

    在本文中,我运行的 Java 版本是 1.8,是 Oracle 的 Hotspot 版,不是 OpenJDK,但是对于本文的内容,OpenJDK 也适用,亲测。

    1
    2
    3
    4
    > java -version
    java version "1.8.0_45"
    Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
    Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
  2. Python 版本

    在本文中,我使用的 Python 版本为 3.5.2,这里有个很尴尬的问题,Python 不得不吐槽的一点就是版本太乱,2.6, 2.7, 3.4/3.5 这三个版本应该是比较多人用的,但是不兼容!!,而且大部分系统默认依赖的是 2.7,所以当你在项目使用时,一定要注意版本的选择,我目前推荐 3.5。

    但是,不用担心,对于本文来说,JPype 是支持 2.7 和 3.5 的,所以,你使用本文的代码是完全可以在 2.7 上跑通的,而且更舒适的是在 2.7 上安装 JPype比 3.5 上方便多了。

安装依赖

在 Python 3.5 中,要安装 JPype 你不能直接使用 pip3 安装,因为 JPype 默认是为 Python2 编写的(其实是开发那时Python3还未进入主流,并且早在 2009 年就断更了),但是,不要紧张,有专业人士为 Python3 编写了另外一个兼容的版本,所以我们需要从源码安装了,具体步骤为:

1
2
3
> git clone https://github.com/tcalmant/jpype-py3.git
> cd jpype-py3
> python3 setup.py install

然后就会编译安装了,正常的话是可以顺利安装成功地,这样安装也就完事了。

  1. 如果编译出错的话,请检查一下你的系统是否缺乏 python3-dev 库或者根本没有安装 C++ 编译工具,如果在 Linux 的话,你可以直接敲这样的命令:

    1
    > sudo apt-get install g++ python3-dev
    
  2. 如果还失败的话,那么可能你需要制定一下 JAVA_HOME,在尝试安装:

     > JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-amd64 python3 setup.py install

    这里的 JAVA_HOME 需要根据你的 JDK 情况进行修改,千万不要照抄!!!

  3. 如果还有问题,那我就没办法了,来项目的 GITHUB 提 ISSUE 吧。

安装完了之后,我们就要开始写代码了,下面我将会介绍一些常用的套路,依照这些套路,你应该可以顺利得完成大部分的工作需求了。

JPype 使用示例

最简单的使用示例

一开始肯定要来个最简单的 ”Hello World“ 了,毕竟都是国际惯例了,所以啥也不多说了,先来一小段代码让大家看看,如果简单快速得使用 JPype:

1
2
3
4
5
6
7
import jpype
from jpype import * if __name__ == "__main__":
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("Hello World")
shutdownJVM()

可以看到,这里的代码很简单,寥寥三句而已,开头的 import 是必须的套路了,然后关键代码都在 __main__ 里面,可以看到的是 __main__里头只有三句代码,其中第一句和第三句都是和 JVM 密切相关的,那就是 startJVM 和 shutdownJVM,其实就是开启和关闭 JVM 的执行环境;然后中间这一句才是真正执行的 Java代码,可以看到,除了和在 Java 中直接调用 System.out.println 多了前面的包名之外,其他都是一样的,所以这让 Python 使用 Java代码 变得非常简单。

入门第一坑

可能有时我们在代码中只会偶尔跑一下 Java代码,并不会一直运行着,这时候,为了性能考虑,可能回想在运行完 Java代码 之后会关掉 JVM,然后在下次运行Java代码的时候再启动一次JVM。说实话,这个想法是挺好的,所以我们来实践一遍:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import jpype
from jpype import * if __name__ == "__main__":
print(jpype.getDefaultJVMPath())
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("First time, i am running")
shutdownJVM() # Here we do a lot of work startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("Sencond time, i am coming back")
shutdownJVM()

然后我们执行一下,well,悲剧发生了,我们来看看输出:

 1
2
3
4
5
6
7
8
9
10
First time, i am running
JVM activity report :
classes loaded : 31
JVM has been shutdown
Traceback (most recent call last):
File "/Users/yetship/Code/python/JpypeExample/RestartJvm.py", line 12, in <module>
startJVM(jpype.getDefaultJVMPath())
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/JPype1_py3-0.5.5.2-py3.5-macosx-10.6-intel.egg/jpype/_core.py", line 58, in startJVM
_jpype.startup(jvm, tuple(args), True)
RuntimeError: Unable to start JVM at native/common/jp_env.cpp:60

没错,在第二次启动 JVM 的时候出错了,JVM居然启动不起来了!!!是的,不要惊讶,这是 BUG,而且是很久的 BUG,作者也不准备改的BUG,ISSUE 84 说了这个问题。

判断 JVM 是否启动

刚才第一坑说了,不能重启,那我们只能保持 JVM 一直运行了,同时,为了放置误操作重启了 JVM,所以 JPype 提供了另外一个函数判断 JVM 是否已启动,这里也给出一个示例看看:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
import jpype
from jpype import * if __name__ == "__main__":
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("First time, i am running") # Here we do a lot of work if not isJVMStarted():
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("Sencond time, i am coming back")
shutdownJVM()

这里的第 9 行判断了一下 JVM 是否已经启动,然后再决定要不要启动 JVM,最后再运行 Java代码

引用 jar 包

在使用 Java 的时候,你说不引用第三方 jar 包是不可能的,因为 Java 之所以流行,和他拥有大量的第三方库是分不开的,所以,这里必须要介绍一下这个功能,如何引用第三方 jar 包,这里就以 alibaba 出品的 fastjson为例,介绍一下如何使用第三方 jar 包

  1. 下载 jar 包

    下载链接: https://search.maven.org/remote_content?g=com.alibaba&a=fastjson&v=LATEST

  2. 引入 jar 包

    在 JPype 中引入 jar 包和我们平时执行 Java 命令时相似,回想一下我们平时指定 jar 包时,使用的是 -cp 参数,然后后面接 jar包的位置,这里也一样

    1
    jvm_args = "-Djava.class.path=ext_classpath"
    
  3. 代码示例

    ```python import json from jpype import *

if __name__ == "__main__":
jars = ["/Users/yetship/tools/libs/fastjson-1.2.21.jar"]
jvm_path = getDefaultJVMPath()
jvm_cp = "-Djava.class.path={}".format(":".join(jars)) startJVM(jvm_path, jvm_cp) JSONObject = JClass("com.alibaba.fastjson.JSONObject")
json_str = json.dumps({"name": "yetship", "site": "https://liuliqiang.info"}) jsonObj = JSONObject.parse(json_str)
print(jsonObj.getString("name"))
print(jsonObj.getString("site")) shutdownJVM()
```

可以看到,这里获取了 JSONObject 的对象,然后解析了一个 json 字符串,然后从解析出来的 JSON 对象中获取值,这就是一个使用第三方 jar 包的应用,从这开始,你就开启了 Python 调用 Java 代码广泛的天空。

执行 jar 包

可能引用第三方 jar 包我们还不满意,我们还希望能够直接执行 jar 包,其实执行 jar 包未必需要使用 JPype来做,我们直接使用 subprocess 来做也行,但是这样管理就变得稍微有点复杂了,为了方便管理,统一代码,所以这里介绍一下如何使用 JPype 执行 jar 包。

首先,我们必须知道的是,所以得运行 jar 包,其实就是我们在打 jar 包的时候都会在 META-INF/MANIFEST.MF 指定了默认调用哪个类的 main 方法而已,这里不是讲 Java 知识的文章,所以就到此了,我们需要知道的是所谓的执行 jar 包其实就是调用了一个 main 方法而已,所以这里可以这么来调用一个 jar 包:

  1. 制作 jar 包:

    java 代码为:

     1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package info.liuliqiang;
    
     public class Main
    { public static void main(String[] args)
    {
    System.out.println("I am called");
    }
    }
  2. 执行 jar 包

    ``` from jpype import *

if __name__ == "__main__":
jars = ["/Users/yetship/tools/libs/SimpleJar.jar"]
jvm_path = getDefaultJVMPath()
jvm_cp = "-Djava.class.path={}".format(":".join(jars)) startJVM(jvm_path, jvm_cp) # 获取 Main 类
Main = JClass("info.liuliqiang.Main")
# 执行 main 函数,注意参数是 String[] args
Main.main([]) shutdownJVM()
```

更加深入

类型对应

不同的语言支持的数据类型是不一样的,例如 Java 中有 int,long,double,float 等类型,而 python 中却是没有 double 的,只有 float,所以如何转换这些类型是个问题,这里就列举出转换的关系:

  • 第一行表示 Java 中的数据类型
  • 第一列表示 Python 中的数据类型
  • 表格中没有数据表示不能转换
  • E 表示需要明确自己转换
  • I/I(x) 表示可以默认转换
  • X/X(x) 表示可以精确匹配

内部类

虽然在 JPype 中,Java 的特性受到了一些限制,但是内部类却还是可以使用的,但是,在使用的过程中,有一些内容你还是需要注意的:

  1. 内部类在 Java 中是用 $ 符号与所在的主类隔开的,所以在查找类的时候需要带上 $ 符号,例如Boo$Foo
  2. 因为是内部类,所以你不能像其他标准的的类一样直接通过全路径包名访问内部类,你需要使用 JPackage 的 __getclass__ 函数来加载
  3. 对于非静态的内部类在 Python 中将无法实例化,其他内部类实例将和普通类一样使用。

性能

对于跨语言调用,我们离不开的一个话题就是性能了,对于 JPype 来说,因为它低层使用的是 JNI;JNI 在 Java 中被用来和低层的 C/C++ 代码进行交互,因此可以发现 JPype 其实不是简单得做了一层转换,实际上是做了两层的转换。

但事实上,在 Python 中使用 Java 代码有点类似于在 Python 中部分用 C 代码编写一般,不仅不会降低 python 代码的性能,反而会提高 python 代码的性能。

既然是跨语言,那么当频繁交互的时候必然带来开销,如果你经常往 Java 代码中传递相同的对象(例如 String/Object/Array)的话,那么你可以使用包装器来预转换,这样多少能提升一些代码的执行速度。

多线程

好了,到了 Python 的软项了,熟悉 Python 的同学都知道 Python 没有真正的多线程,所以只有伪多线程,这就导致了调用 Java 代码也没有多线程,同时Java代码中的多线程是不会生效的

JProxy

JProxy 是一个能让你在 Python 代码中实现任意数量的 Java 接口的方式。使用 JProxy 可以很简单得实现接口,JProxy 支持两种方式实现接口:

  1. 方式一:指定实例

    指定实例就是指 JProxy 接收两个参数:

    • 第一个参数是要实现的接口,或者接口列表
    • 第二个参数就是一个 Python 类,这个类已经实现了这些接口的所有方法

      举个例子,Java 代码中的接口是这样的:

      1
      2
      3
      4
      5
      public interface ITestInterface2
      {
      int testMethod();
      String testMethod2();
      }

      然后再 Python 中可以这么实现:

       1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      class C :
      def testMethod(self) :
      return 42 def testMethod2(self) :
      return "Bar" c = C()
      # 第一个参数指定实现的接口
      # 第二个参数指定实现的实例
      proxy = JProxy("ITestInterface2", inst=c)
  2. 方式二:指定方法

    指定方法的话就是 JProxy 也接收两个参数:

    • 第一个参数是要实现的接口或者接口列表
    • 第二个参数是一个字典,key 是要实现的方法,value 是一个可调用对象

      还是举刚才的例子,Java 接口还是那样,然后 Python 实现的话就应该这么写:

       1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      def _testMethod() :
      return 32 def _testMethod2() :
      return "Fooo!" d = {
      'testMethod' : _testMethod,
      'testMethod2' : _testMethod2,
      }
      proxy = JProxy("ITestInterface2", dict=d)

对于继承父类这样的操作在 Python 中是不支持的,同时,因为 JPype 提供了大量的 Java API,所以继承也就变得意义没那么大了,如果有兴趣的话可以参考 AWT 和 SWING 的实现。

Java 异常处理

如果希望在 Python 中捕获 Java 异常的话,那么 JPype 提供有 JavaException 这个类用于捕获所有的 Java 异常,通过捕获 JavaException 这个类,你可以使用 message(), stackTrace()和 javaClass() 这些方法来获得更多的异常信息。这里举个简单的例子:

1
2
3
4
5
6
try :
# Code that throws a java.lang.RuntimeException
except JavaException, ex :
if JavaException.javaClass() is java.lang.RuntimeException :
print "Caught the runtime exception : ", JavaException.message()
print JavaException.stackTrace()

如果你真的想直接捕获真正的 Java 异常的话,那么你可以使用 JException 这个装饰器,例如:

try :
# Code that throws a java.lang.RuntimeException
except jpype.JException(java.lang.RuntimeException), ex :
print "Caught the runtime exception : ", JavaException.message()
print JavaException.stackTrace()

JPype 局限

通过前面那么多的介绍,我们知道 JPype 是很强大的,实现了很多 Python 和 Java 交互的问题,但是 JPype 还是有一些局限性的,例如:

  1. 重启 JVM

    相信之前那个第一坑让你有点无奈,居然不能重启 JVM,但是这个不合理的设计背后是有原因的。我们现在已经知道了 JPype 低层依赖的实现是 JNI,然后 JNI 的 API 中实现了一个 destroyJVM() 的函数,然而这个函数并不工作,所以当你再次调用 startupJVM() 的时候,你得到的是一个异常,

  2. 依赖于“当前类”的方法

    在 Java 的库中,有很多方法依赖于调用的类来查找信息,所以,当我们在 Python 中调用这些方法时,那就会发生错误了,因为没有java 调用类提供给它,而且 JNI api 也没有办法来提供仿真的方法。

    目前遇到过会出现这种问题的函数有:

    1
    2
    java.lang.Class.forName(String classname);
    java.sql.DriverManager.getConnection(...);

    第一句可以使用这种方式类替换:

    1
    Class.forName(classname, True, ClassLoader.getSystemClassLoader())
    

    第二句的话没有太好的方法,如果真的需要解决的话,我们只有自己先实例化好 driver 对象,然后再传递给 connect 方法了。

总结

好的,终于到最后了,本文从安装到入门到深入,直至最后介绍了 JPype 的局限性,带大家全局认识了一下 JPype,希望能够帮助到大家在工作中,平时日常玩耍中解决一些问题。

Reference

· EOF ·

 

Powered by   MDPress Copyright  yetship 2012-2017

python调用java&反编译地址的更多相关文章

  1. Java 反编译工具 —— JAD 的下载地址(Windows版/Linux版/Mac OS 版)

    Java 反编译工具 —— JAD 的下载地址. 各种版本哦! Windows版,Linux版,Mac OS 版,等等 下载地址: http://varaneckas.com/jad/

  2. Java反编译工具CFR,Procyon简介

    Java反编译工具有很多,个人觉得使用最方便的是jd-gui,当然jad也不错,jd-gui主要提供了图形界面,操作起来很方便,但是jd-gui很久没有更新了,java 7出来很久了,jd-gui在反 ...

  3. Java 反编译工具哪家强?对比分析瞧一瞧

    前言 Java 反编译,一听可能觉得高深莫测,其实反编译并不是什么特别高级的操作,Java 对于 Class 字节码文件的生成有着严格的要求,如果你非常熟悉 Java 虚拟机规范,了解 Class 字 ...

  4. [Java基础]代码块及java反编译

    块的作用域:   块(即复合语句)是指一对花括号括起来的若干条简单的java语句.块确定了变量的作用域.一个块可以嵌套在另一个块中.但是,在嵌套的两个块中声明同名的变量时应注意,块中变量申明在块外变量 ...

  5. Java反编译插件JadClipse

    Java反编译是很容易的,现在就介绍一个反编译插件,以后我们通过Ctrl+鼠标左键查看源码就容易得多了,不用再担心源码找不到了,配置过程很简单的. 准备: 1.下载JadClipse(jar文件,ec ...

  6. 【转】Eclipse Class Decompiler——Java反编译插件

    闲暇之余,写了一个Eclipse下的Java反编译插件:Eclipse Class Decompiler,整合了目前最好的2个Java反编译工具Jad和JD-Core,并且和Eclipse Class ...

  7. myeclipse和eclipse安装Java反编译插件

    为myeclipse和eclipse安装Java反编译插件    插件所需包 1.解压jad1.5.8g.zip.将jad.exe放到jre的bin目录下,下载地址: http://ishare.ia ...

  8. Java 反编译工具下载

    反编译,通俗来讲,就是将.java 文件经过编译生成的 .class 文件还原.注意这里的还原不等于 .java 文件.因为Java编译器在编译.java 文件的时候,会对代码进行一些处理. 那么接下 ...

  9. Java反编译工具(Java Decompiler)

    Java Decompiler是一种非常实用的JAVA反编译工具,可以对整个jar包进行反编译,也可以将其集成到eclipse上,非常方便的根据class文件的源码.,官网地址http://jd.be ...

  10. Java反编译工具JD-GUI以及Eclipse的反编译插件

    什么是反编译 高级语言源程序经过编译变成可执行文件,反编译就是逆过程.但是通常不能把可执行文件变成高级语言源代码,只能转换成汇编程序. 反编译是一个复杂的过程,所以越是高级语言,就越难于反编译,但目前 ...

随机推荐

  1. npm i不成功devDependencies解决方法

    npm config ls -l 查看npm配置发现production为true,所以i不成功 npm config set production false 将production设置为false ...

  2. call bind的实现以及数组常用方法

    1.call 实现(apply 类似) Function.prototype.call= function(context){    context = context || window;     ...

  3. hdrp gpu instance MPB不生效问题

    Thanks for posting these tips. I was devastated when my project dropped to 3 FPS because material pr ...

  4. C#textbox更改字体颜色只读后不起作用的解决办法

    textbox的属性ReadOnly设置为true只读后,只更改字体颜色并不起作用. 解决办法是,连同背景色一起设置即可. textBox1.BackColor =textBox1.BackColor ...

  5. SpringCloud-Hoxton.SR1-config整合

    1.前一篇讲到了整合eureka和gateway,实现了服务的发现与注册以及网关的统一入口,这一篇在此基础上整合分布式配置中心config,首先新建一个子项目config-services作为服务端, ...

  6. 洛谷P4571 [JSOI2009] 瓶子和燃料

    题目 https://www.luogu.com.cn/problem/P4571 思路 首先观察并且简单模拟一下火星人取燃料的过程,发现最终燃料的量一定是他选的k个瓶子容量的线性组合(观察操作3就知 ...

  7. 2020ICPC上海I - Sky Garden

    思维 [I-Sky Garden_第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(上海)(重现赛)@hzy0227 (nowcoder.com)](https://codeforces.co ...

  8. Word10 个人简历office真题

    1.新键Microsoft Word 文档,命名为Word,再打开Word文档,选择[布局],打开[页面设置]右下角的箭头,弹出[页面设置]的窗口,根据题目要求调整[页边距].   2.根据案例题目二 ...

  9. nginx 配置react项目 并且开启gzip压缩

    #user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #erro ...

  10. docker容器监控系统

    Cadvisor+InfluxDB+Grafana Cadvisor Cadvisor是检测单节点资源信息的工具,提供了一个http接口的查询界面,可以和其他工具整合使用,Cadvisor既可以采集宿 ...