MOOC 个人学习笔记

# 1.RMI

  • RMI:Remote Method Invocation 远程方法调用
  • 两个位于不同 JVM 虚拟机的 Java 程序互相请求访问

# RMI 的参数和返回值

  • (自动化) 传递远程对象 (实现 Remote 接口)
    • 当一个对远程对象的引用从一个 JVM 传递到另一个 JVM,该远程对象的发送者和接收者将持有同一个实体对象的引用。这个引用并非是一个内存位置,而是由网络地址和该远程对象的唯一标识符构成的。
      ### 两个 JVM 拥有同一个对象 ###
  • (自动化) 传递可序列化对象 (实现 Serializable 接口)
    • JVM 中的一个对象经过序列化后的字节,通过网络,其副本传递到另一个 JVM 中,并重新还原为一个 Java 对象。
      ### 每个 JVM 拥有自己的对象 ###
//Client
Context namingContext = new InitialContext();
      
// 开始查找 RMI 注册表上有哪些绑定的服务
Enumeration<NameClassPair> e = namingContext.list("rmi://127.0.0.1:8001/");
while (e.hasMoreElements())
    System.out.println(e.nextElement().getName());
// 获取某一个地址上的服务类
String url = "rmi://127.0.0.1:8001/warehouse1";      
Warehouse centralWarehouse = (Warehouse) namingContext.lookup(url);
/*
// 接口类
public interface Warehouse extends Remote
{  
   double getPrice (String description) throws RemoteException;
}
*/
// 输入参数  取得结果
String descr = "面包机";
double price = centralWarehouse.getPrice(descr);
System.out.println(descr + ": " + price);
//Server
System.out.println("产生服务器对象");
WarehouseImpl centralWarehouse = new WarehouseImpl();
System.out.println("将服务器对象绑定在8001端口,对外提供服务");
LocateRegistry.createRegistry(8001);// 定义端口号
Naming.rebind("rmi://127.0.0.1:8001/warehouse1", centralWarehouse);
// 实现类
public class WarehouseImpl extends UnicastRemoteObject implements Warehouse
{
   private Map<String, Double> prices;
   public WarehouseImpl() throws RemoteException
   {
      // 物品列表
      prices = new HashMap<>();
      prices.put("面包机", 24.95);
      prices.put("微波炉", 49.95);
   }
   public double getPrice(String description) throws RemoteException
   {
      Double price = prices.get(description);
      return price == null ? 0 : price;
   }
}
// 接口类
public interface Warehouse extends Remote
{  
   double getPrice(String description) throws RemoteException;
}
  • RMI 优点
    • 跨平台分布式对象调用
    • 完全对象支持
    • 安全策略
  • RMI 缺点
    • 双方必须是 Java 语言实现
    • 不如消息传递协作方便

# 2.JNI

  • JNI,Java Native Interface
  • Java 和本地 C 代码进行互操作
    • Java 调用 C 程序完成一些需要快速计算的功能 (常见,重点)
    • C 调用 Java 程序 (基于反射的方法)

# 基本步骤

  • 在 Java 类中声明一个本地方法,使用 native 函数
class HelloNative {
    public static native void greeting();
}
  • 调用 javac.exe 编译,得到 HelloNative.class
  • 调用 javah.exe 得到包含该方法 (Java_HelloNative_greeting) 的头文件 HelloNative.h
  • 实现.c 文件 (对应 HelloNative.h)
#include "HelloNative.h"
JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv* env, jclass cl) {
    printf("Hello Native World\n");
}
  • 将.c 和.h 文件,整合为共享库 (DLL) 文件
  • 在 Java 类中,加载相应的共享库文件
HelloNative.greeting();
static {
    System.loadLibrary("HelloNative"); //dll 名字
}

# 3.Nashorn

  • 脚本引擎,ScriptEngine
    • Nashorn,JDK 8 自带的 JS 解释器 (JDK6/7 是 Rhino 解释器)
ScriptEngine engine = new ScriptEngineManager().getEngineByName(“nashorn”)
  • 主要方法
    • eval,执行一段 js 脚本. eval (String str), eval (Reader reader)
    • put,设置一个变量
    • get,获取一个变量
    • createBindings, 创建一个 Bindings
    • setBindings, 设置脚本变量使用的范围
//Test1
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 
engine.put("a", 100);
engine.put("b", 200);
engine.eval("var c = a+b");
String result = engine.get("c").toString();
System.out.println(result);
//Test2
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 
SimpleBindings simpleBindings = new SimpleBindings(); // 传递参数到 js
simpleBindings.put("a", 100); 
simpleBindings.put("b", 200); 
Object result = engine.eval("load('src/edu/ecnu/sum2.js')", simpleBindings);
/*
src/edu/ecnu/sum2.js
c = a + b;
*/
System.out.println(result);
//Test3
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 
FileReader scriptFile = new FileReader("src/edu/ecnu/sum3.js");
/*
src/edu/ecnu/sum3.js
function sum(a, b)
{
   return a + b;
}
*/
engine.eval(scriptFile);
Invocable in = (Invocable) engine;
String result = in.invokeFunction("sum",100,200).toString();
System.out.println(result);

# JDK 支持的脚本引擎工厂

引擎名字MIME 类型文件扩展
Nashornnashorn,jstext/javascriptjs
Groovygroovygroovy
RenjinRenjintext/x-RR,r,S,s
SISC Schemesiscscheme,sisc

# 4.Jython

  • Jython 是 Python 语言在 Java 平台的实现
  • Jython 是在 JVM 上实现的 Python,由 Java 编写
  • Jython 将 Python 源码编译成 JVM 字节码,由 JVM 执行对应的字节码,因此能很好的与 JVM 集成
  • Jython 并不是 Java 和 Python 的连接器

# 关键类

  • PythonInterpreter
    • exec 执行语句
    • set 设置变量值
    • get 获取变量值
    • execfile 执行一个 python 文件
  • PyObject
  • PyFunction
// 执行 Python 程序语句
PythonInterpreter pi = new PythonInterpreter();
pi.exec("import sys");
pi.set("a", new PyInteger(42));
pi.exec("print a");
pi.exec("x = 2+2");
PyObject x = pi.get("x");
System.out.println("x: " + x);
// 执行 Python 文件
PythonInterpreter pi = new PythonInterpreter();
pi.execfile("src/main/java/edu/ecnu/hello.py");
pi.cleanup();
pi.close();
// 执行 Python 文件并传参
try (PythonInterpreter pi = new PythonInterpreter()) {
    pi.set("cnt", 5);
    pi.execfile("src/main/java/edu/ecnu/randomSum.py");
    PyObject sum = pi.get("sum");
    System.out.println("Sum is: " + sum);
}
/*
src/main/java/edu/ecnu/randomSum.py
from java.util import Random
r = Random()
sum = 0
for i in xrange(cnt):
    randomNum = r.nextInt()
    print randomNum
    sum += randomNum
*/
// 调用 Python 程序中的函数
PythonInterpreter pi = new PythonInterpreter();        
pi.execfile("src/main/java/edu/ecnu/calculator1.py");
PyFunction pf = pi.get("power", PyFunction.class);
PyObject result = pf.__call__(Py.newInteger(2), Py.newInteger(3)); //2^3
System.out.println(result);
pi.cleanup();
pi.close();
/*
src/main/java/edu/ecnu/calculator1.py
import math
def power(x, y):
    return math.pow(x, y)
*/
// 调用 Python 程序中的类
PythonInterpreter pi = new PythonInterpreter();
pi.execfile("src/main/java/edu/ecnu/calculator2.py");
// 在 Java 中调用 Python 对象实例的方法
String pythonClassName = "Calculator";  //python 类名        
String pythonObjName = "cal"; //python 对象名    
pi.exec(pythonObjName + "=" + pythonClassName + "()"); // 实例化 python 对象        
PyObject pyObj = pi.get(pythonObjName); // 获取实例化的 python 对象
// 调用 python 对象方法,传递参数并接收返回值
PyObject result = pyObj.invoke("power", new PyObject[] {Py.newInteger(2), Py.newInteger(3)}); 
double power = Py.py2double(result);
System.out.println(power);
pi.cleanup();
pi.close();
/*
import math
class Calculator(object):
    def power(self, x, y):
        return math.pow(x,y)
*/

# 5.Web Service

  • 由万维网联盟 (W3C, World Wide Web Consortium) 提出
  • 消除语言差异、平台差异、协议差异和数据结构差异,成为不同构件模型和异构系统之间的集成技术
  • Web Service 是为实现跨网络操作而设计的软件系统,提供了相关的操作接口,其他应用可以使用 SOAP 消息,以预先指定的方式来与 Web Service 进行交互

# wsimport 工具

  • % JAVA_HOME%\bin 目录下
  • 根据 wsdl 文档,自动产生客户端中间代码
wsimport -keep -verbose http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL

# 基本步骤

  • 调用 wsimport 所产生客户端中间代码
  • 提供相应参数
  • 获取返回结果
/**
* use wsimport to parse wsdl
* wsimport -keep -verbose http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL
* copy the generated class to this project, and invoke them
* 
* 调用手机号查询 web service
* @param args
*/
MobileCodeWS mobileCodeWS = new MobileCodeWS();
MobileCodeWSSoap  mobileCodeWSSoap = mobileCodeWS.getMobileCodeWSSoap();
String tel=mobileCodeWSSoap.getMobileCodeInfo("13722222222",null); // 修改为有效号码
System.out.println(tel);
  • Java 调用 Web Service 其他办法
    • Axis/Axis2 (axis.apache.org)
    • 采用 URLConnection 访问 Web Service
    • 采用 HttpClient 访问 Web Service

# 6. 命令行

# Runtime

  • Java 提供 Runtime 类
    • exec 以一个独立进程执行命令 command, 并返回 Process 句柄
    • 当独立进程启动后,需要处理该进程的输出流 / 错误流
      • 调用 Process.getInputStream 可以获取进程的输出流
      • 调用 Process.getErrorStream 可以获取进程的错误输出流
    • 调用 Process.waitFor 等待目标进程的终止 (当前进程阻塞)
Process p;
String cmd = "ipconfig /all";  // 查看 ip 地址
try {
    // 执行命令
    p = Runtime.getRuntime().exec(cmd);
    // 取得命令结果的输出流
    InputStream fis = p.getInputStream();
    // 用一个读输出流类去读
    InputStreamReader isr = new InputStreamReader(fis);
    // 用缓冲器读行
    BufferedReader br = new BufferedReader(isr);
    String line = null;
    // 直到读完为止
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
    System.out.println("");
    int exitVal = p.waitFor(); // 获取进程最后返回状态
    System.out.println("Process exitValue: " + exitVal);
} catch (Exception e) {
    e.printStackTrace();
}