实现步骤:
1) 完成Java代码,编写好Java调用类。
2) 编译你的Java类。
3) 用javah生成编译好的class文件对应的C/C++ 函数的头文件。
4) 实现头文件中的函数原型,编写native代码。
5) 将native代码编译打包成DLL库(win32)或共享库(Linux)。
6) 将你的Java代码跑起来
一、首先在创建一个类InvokerHelper。
1. 编写Java代码。
注意:
(1) 调用本地代码的java方法,要设置成native的。
(2) 要使用System的LoadLibrary方法去加载包含本地方法实现的库。
/src/helper/InvokerHelper.java
package helper;
public class InvokerHelper {
static {
System.loadLibrary("PolynomialCaculator");
}
// 多项式相加
public static native String AddPolyn(int n1, float[] coef1, int[] expn1, int n2, float[] coef2, int[] expn2);
// 多项式相加
public static native String SubtractPolyn(int n1, float[] coef1, int[] expn1, int n2, float[] coef2, int[] expn2);
// 多项式相加
public static native String MultiplyPolyn(int n1, float[] coef1, int[] expn1, int n2, float[] coef2, int[] expn2);
}
2、编译生成InvokerHelper.class
3、在cmd命令行中切换至eclipse的对应的workspace的bin目录下,执行命令javah -classpath . -jni helper.InvokerHelper。此处的helper为包 名,InvokerHelper为类名。此时生成一个helper_InvokerHelper.h文件。内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class helper_InvokerHelper */
#ifndef _Included_helper_InvokerHelper
#define _Included_helper_InvokerHelper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: helper_InvokerHelper
* Method: AddPolyn
* Signature: (I[F[II[F[I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_helper_InvokerHelper_AddPolyn
(JNIEnv *, jclass, jint, jfloatArray, jintArray, jint, jfloatArray, jintArray);
/*
* Class: helper_InvokerHelper
* Method: SubtractPolyn
* Signature: (I[F[II[F[I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_helper_InvokerHelper_SubtractPolyn
(JNIEnv *, jclass, jint, jfloatArray, jintArray, jint, jfloatArray, jintArray);
/*
* Class: helper_InvokerHelper
* Method: MultiplyPolyn
* Signature: (I[F[II[F[I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_helper_InvokerHelper_MultiplyPolyn
(JNIEnv *, jclass, jint, jfloatArray, jintArray, jint, jfloatArray, jintArray);
#ifdef __cplusplus
}
#endif
#endif
4) 实现头文件中的函数原型,编写native代码。
创建一个DLL项目,这里我用devc++创建。文件->新建->项目->选择DLL工程。
以下有几个特别值得注意的地方:
1、要将生成的helper_InvokerHelper.h文件放到DLL工程的根目录,并将文件开头的#include <jni.h>改成#include "jni.h"
2、找到jdk的安装目录的jni.h与jni_md.h文件,将这两个文件复制到DLL工程的根目录。默认安装的话,这两个文件的路径为C:\Program Files\Java\jdk1.8.0_40\include和C:\Program Files\Java\jdk1.8.0_40\include\win32
接下来实现创建新的.c或.cpp文件(分别为C文件和C++文件),实现helper_InvokerHelper.h头文件中的方法,文件名应该与之前java的helper_InvokerHelper类中的System.loadLibrary()的名称相同。此处为我用C++语言,所以为PolynomialCaculator.cpp。将helper_InvokerHelper.h头文件中的方法复制粘贴到PolynomialCaculator.cpp,将方法逐一实现。
代码如下:
#include<stdio.h>
#include "helper_InvokerHelper.h"
#include "jni.h"
#include<math.h>
#include<stdlib.h>
#include<string.h>
typedef struct Polynomial{
float coef; //系数
int expn; //指数
struct Polynomial *next;
}*Polyn,Polynomial;
void Insert(Polyn p,Polyn h){
if(p->coef==0) free(p); //系数为0的话释放结点
else
{
Polyn q1,q2;
q1=h;
q2=h->next;
while(q2&& p->expn < q2->expn)
{ //查找插入位置
q1=q2;
q2=q2->next;
}
if(q2&& p->expn == q2->expn)
{ //将指数相同相合并
q2->coef += p->coef;
free(p);
if(!q2->coef)
{ //系数为0的话释放结点
q1->next=q2->next;
free(q2);
}
}
else
{ //指数为新时将结点插入
p->next=q2;
q1->next=p;
}
}
}
Polyn CreatePolyn(int n, float coef[], int expn[]){ //建立一个头指针为head、项数为m的一元多项式
int i;
Polyn p,head;
p=head=(Polyn)malloc(sizeof(struct Polynomial));
head->next=NULL;
for(i=0;i<n;i++)
{
p=(Polyn)malloc(sizeof(struct Polynomial)); //建立新结点以接收数据
p->coef=coef[i];
p->expn=expn[i];
Insert(p,head); //调用Insert函数插入结点
}
return head;
}
int compare(Polyn a,Polyn b){
//比较多项式的指数的大小
if(a&&b)
{
if(!b||a->expn>b->expn) return 1;
else if(!a||a->expn<b->expn) return -1;
else return 0;
}
else if(!a&&b) return -1; //a多项式已空,但b多项式非空
else return 1; //b多项式已空,但a多项式非空
}
jstring PrintPolyn(Polyn P,JNIEnv * env){
char buf[50];
int a;
int i=0;//指向下一个插入地址
int k=0;
Polyn q=P->next;
int flag=1; //项数计数器
if(!q)
{ //若多项式为空,输出0
buf[i]=0;
i++;
buf[i]=230;
i++;
char* tmpstr=buf;
jstring rtstr = env->NewStringUTF(tmpstr);
return rtstr;
}
while(q)
{
//分离整数部分与小数部分
int xiaoshu=((int)fabs(q->coef*10))%10; //第一位小数
if(q->coef>0&& flag!=1)
{
buf[i]='+'; //系数大于0且不是第一项
i++;
printf("+");
}
if(q->coef!=1&&q->coef!=-1)
{ //系数非1或-1的普通情况
if(q->coef<0)
{
//添加负号
buf[i]='-';
i++;
printf("-");
}
//添加整数部分
buf[i]=(int)(fabs(q->coef));
i++;
printf("%d",(int)(fabs(q->coef)));
if(xiaoshu!=0)
{
//添加小数点
buf[i]='.';
i++;
printf(".");
//添加小数部分
buf[i]=xiaoshu;
i++;
printf("%d",xiaoshu);
}
if(q->expn==1)
{
buf[i]='X';
i++;
printf("X");
}
else if(q->expn)
{
buf[i]='X';
i++;
printf("X");
buf[i]='^';
i++;
printf("^");
//添加指数的各位数
buf[i]=q->expn;
i++;
printf("%d",q->expn);
}
}
else
{
if(q->coef==1)
{
if(!q->expn)
{
buf[i]=1;
i++;
printf("1");
}
else if(q->expn==1)
{
buf[i]='X';
i++;
printf("X");
}
else
{
buf[i]='X';
i++;
printf("X");
buf[i]='^';
i++;
printf("^");
//添加指数的各位数
buf[i]=q->expn;
i++;
printf("%d",q->expn);
}
}
else if(q->coef==-1)
{
if(!q->expn)
{
buf[i]='-';
i++;
printf("-");
buf[i]=1;
i++;
printf("1");
}
else if(q->expn==1)
{
buf[i]='-';
i++;
printf("-");
buf[i]='X';
i++;
printf("X");
}
else
{
buf[i]='-';
i++;
printf("-");
buf[i]='X';
i++;
printf("X");
buf[i]='^';
i++;
printf("^");
//添加指数的各位数
buf[i]=q->expn;
i++;
printf("%d",q->expn);
}
}
}
q=q->next;
flag++;
}
buf[i]='\0';
char shu[strlen(buf)];
strcpy(shu,buf);
char* tmpstr=shu;
jstring rtstr = env->NewStringUTF(tmpstr);
return rtstr;
}
Polyn AddPolyn(Polyn pa,Polyn pb){ //求解并建立多项式a+b,返回其头指针
Polyn qa=pa->next;
Polyn qb=pb->next;
Polyn headc,hc,qc;
hc=(Polyn)malloc(sizeof(struct Polynomial)); //建立头结点
hc->next=NULL;
headc=hc;
while(qa||qb)
{
qc=(Polyn)malloc(sizeof(struct Polynomial));
switch(compare(qa,qb)){
case 1:
{
qc->coef=qa->coef;
qc->expn=qa->expn;
qa=qa->next;
break;
}
case 0:
{
qc->coef=qa->coef+qb->coef;
qc->expn=qa->expn;
qa=qa->next;
qb=qb->next;
break;
}
case -1:
{
qc->coef=qb->coef;
qc->expn=qb->expn;
qb=qb->next;
break;
}
}
if(qc->coef!=0)
{
qc->next=hc->next;
hc->next=qc;
hc=qc;
}
else free(qc); //当相加系数为0时,释放该结点
}
return headc;
}
JNIEXPORT jstring JNICALL Java_helper_InvokerHelper_AddPolyn
(JNIEnv * env, jclass method, jint n1, jfloatArray coef1, jintArray expn1, jint n2, jfloatArray coef2, jintArray expn2){
jfloat* c1 = (env)->GetFloatArrayElements(coef1,0);
jint* e1 = (env)->GetIntArrayElements(expn1, 0 );
float c11[n1];
int e11[n1];
int i;
for(i=0;i<n1;i++)
{
c11[i]=c1[i];
e11[i]=e1[i];
}
(env)->ReleaseFloatArrayElements(coef1, c1, 0 );
(env)->ReleaseIntArrayElements(expn1, e1, 0 );
jfloat* c2 = (env)->GetFloatArrayElements(coef2, 0 );
jint* e2 = (env)->GetIntArrayElements(expn2, 0 );
float c22[n2];
int e22[n2];
for(i=0;i<n2;i++)
{
c22[i]=c2[i];
e22[i]=e2[i];
}
env->ReleaseFloatArrayElements(coef2, c2, 0 );
env->ReleaseIntArrayElements(expn2, e2, 0 );
Polyn pa=CreatePolyn(n1,c11,e11);
Polyn pb=CreatePolyn(n2,c22,e22);
Polyn qa=pa->next;
Polyn qb=pb->next;
Polyn headc,hc,qc;
hc=(Polyn)malloc(sizeof(struct Polynomial)); //建立头结点
hc->next=NULL;
headc=hc;
while(qa||qb)
{
qc=(Polyn)malloc(sizeof(struct Polynomial));
switch(compare(qa,qb)){
case 1:
{
qc->coef=qa->coef;
qc->expn=qa->expn;
qa=qa->next;
break;
}
case 0:
{
qc->coef=qa->coef+qb->coef;
qc->expn=qa->expn;
qa=qa->next;
qb=qb->next;
break;
}
case -1:
{
qc->coef=qb->coef;
qc->expn=qb->expn;
qb=qb->next;
break;
}
}
if(qc->coef!=0)
{
qc->next=hc->next;
hc->next=qc;
hc=qc;
}
else free(qc); //当相加系数为0时,释放该结点
}
//测试代码
return PrintPolyn(headc,env);
}
JNIEXPORT jstring JNICALL Java_helper_InvokerHelper_SubtractPolyn
(JNIEnv * env, jclass method, jint n1, jfloatArray coef1, jintArray expn1, jint n2, jfloatArray coef2, jintArray expn2)
{
jfloat* c1 = (env)->GetFloatArrayElements(coef1,0);
jint* e1 = (env)->GetIntArrayElements(expn1, 0 );
float c11[n1];
int e11[n1];
int i;
for(i=0;i<n1;i++)
{
c11[i]=c1[i];
e11[i]=e1[i];
}
(env)->ReleaseFloatArrayElements(coef1, c1, 0 );
(env)->ReleaseIntArrayElements(expn1, e1, 0 );
jfloat* c2 = (env)->GetFloatArrayElements(coef2, 0 );
jint* e2 = (env)->GetIntArrayElements(expn2, 0 );
float c22[n2];
int e22[n2];
for(i=0;i<n2;i++)
{
c22[i]=c2[i];
e22[i]=e2[i];
}
env->ReleaseFloatArrayElements(coef2, c2, 0 );
env->ReleaseIntArrayElements(expn2, e2, 0 );
Polyn pa=CreatePolyn(n1,c11,e11);
Polyn pb=CreatePolyn(n2,c22,e22);
Polyn h=pb;
Polyn p=pb->next;
Polyn pd;
while(p)
{ //将pb的系数取反
p->coef*=-1;
p=p->next;
}
pd=AddPolyn(pa,h);
for(p=h->next;p;p=p->next) //恢复pb的系数
p->coef*=-1;
return PrintPolyn(pd,env);
}
JNIEXPORT jstring JNICALL Java_helper_InvokerHelper_MultiplyPolyn
(JNIEnv * env, jclass method, jint n1, jfloatArray coef1, jintArray expn1, jint n2, jfloatArray coef2, jintArray expn2)
{
jfloat* c1 = (env)->GetFloatArrayElements(coef1,0);
jint* e1 = (env)->GetIntArrayElements(expn1, 0 );
float c11[n1];
int e11[n1];
int i;
for(i=0;i<n1;i++)
{
c11[i]=c1[i];
e11[i]=e1[i];
}
(env)->ReleaseFloatArrayElements(coef1, c1, 0 );
(env)->ReleaseIntArrayElements(expn1, e1, 0 );
jfloat* c2 = (env)->GetFloatArrayElements(coef2, 0 );
jint* e2 = (env)->GetIntArrayElements(expn2, 0 );
float c22[n2];
int e22[n2];
for(i=0;i<n2;i++)
{
c22[i]=c2[i];
e22[i]=e2[i];
}
env->ReleaseFloatArrayElements(coef2, c2, 0 );
env->ReleaseIntArrayElements(expn2, e2, 0 );
Polyn pa=CreatePolyn(n1,c11,e11);
Polyn pb=CreatePolyn(n2,c22,e22);
Polyn hf,pf;
Polyn qa=pa->next;
Polyn qb=pb->next;
hf=(Polyn)malloc(sizeof(struct Polynomial));//建立头结点
hf->next=NULL;
for(;qa;qa=qa->next)
{
for(qb=pb->next;qb;qb=qb->next)
{
pf=(Polyn)malloc(sizeof(struct Polynomial));
pf->coef=qa->coef*qb->coef;
pf->expn=qa->expn+qb->expn;
Insert(pf,hf); //调用Insert函数以合并指数相同的项
}
}
return PrintPolyn(hf,env);
}
编译PolynomialCaculator.cpp,将会获得PolynomialCaculator.dll文件,将该文件复制粘贴到java工程的根目录,这个目录非常重要,不要弄错。为了防止大家出错,贴出我的工程目录截图
6) 将你的Java代码跑起来
即调用InvokerHelper类里定义的方法,只要像调用普通的额Java类的方法一样就可以了
这里将我写的一元稀疏多项式的界面代码是功能实现也贴出来:
/src/view/CalculatorUI.java
package view;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import helper.InvokerHelper;
public class CalculatorUI extends JFrame implements ActionListener {
private JTextArea answerText;
private JPanel panel;
private String[] btString = { "^", "(", ")", "计算", "7", "8", "9", "-", "4", "5", "6", "+", "1", "2", "3", "0", "C",
"x", "*" };
// 存储两个多项式的系数与指数
private float[] coef1;
private int[] expn1;
private float[] coef2;
private int[] expn2;
private String operator;
public CalculatorUI() {
// (6x-3-x+4.4x2-1.2x9) (-6x-3+5.4x2-x2+7.8x15) sssssssssss
answerText = new JTextArea(1, 37);
GridLayout layout = new GridLayout(5, 4, 4, 4);
panel = new JPanel(layout);
panel.setPreferredSize(new Dimension(450, 200));
this.setLayout(new FlowLayout());
this.setTitle("一元稀疏多项式计算器");
answerText.setFont(new Font("TimesRoman", Font.BOLD, 15));
for (int i = 0; i < btString.length; i++) {
JButton bt = new JButton(btString[i]);
bt.addActionListener(this);
panel.add(bt);
}
this.add(answerText);
this.add(panel);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int width = screenSize.width;
int height = screenSize.height;
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(450, 270);
this.setResizable(false);
this.setLocation(width / 2 - 215, height / 2 - 165);
this.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent event) {
String c = event.getActionCommand();
switch (c) {
case "计算":
analyseFormular(answerText.getText());
break;
case "C":
answerText.setText("");
break;
default:
answerText.append(c);
}
}
private void analyseFormular(String s) {
// (2x^2+3x^3)+(3x^3+4x^4)
if (!verifyFormula(s)) {
JOptionPane.showMessageDialog(this, "式子不匹配,式子应符合(多项式1)+或-或*(多项式2)");
return;
}
getOperator(s);
getArrayData(s);
Calculate();
}
private void Calculate() {
String answer;
switch (operator) {
case "+":
// getAnswer(answer)
System.out.println("加法");
answer = InvokerHelper.AddPolyn(coef1.length, coef1, expn1, coef2.length, coef2, expn2);
answerText.setText(getAnswer(answer));
break;
case "-":
System.out.println("减法");
answer = InvokerHelper.SubtractPolyn(coef1.length, coef1, expn1, coef2.length, coef2, expn2);
answerText.setText(getAnswer(answer));
break;
case "*":
System.out.println("乘法");
answer = InvokerHelper.MultiplyPolyn(coef1.length, coef1, expn1, coef2.length, coef2, expn2);
answerText.setText(getAnswer(answer));
break;
}
}
private String getAnswer(String answer) {
StringBuilder result = new StringBuilder("");
int i = 0;
if (answer.length() > 0)
for (i = 0; i < answer.length() && ((int) answer.charAt(i)) != 254; i++) {
if (answer.charAt(i) == 'X' || answer.charAt(i) == '^' || answer.charAt(i) == '+'
|| answer.charAt(i) == '-' || answer.charAt(i) == '.') {
result.append(answer.charAt(i));
System.out.print("," + answer.charAt(i));
} else {
result.append((int) answer.charAt(i));
System.out.print("," + (int) answer.charAt(i));
}
}
else
result.append("0");
return result.toString();
}
private void getOperator(String s) {
if (s.contains(")+("))
operator = "+";
else if (s.contains(")-("))
operator = "-";
else if (s.contains(")*("))
operator = "*";
}
private void getArrayData(String s) {
// 获得两个多项式
String forArr[] = s.split("\\)([+-]|\\*)\\(");
String xiang = "((-?[0-9]+\\.?[0-9]*)|-)?x(\\^-?[0-9]+)?\\b|-?[0-9]+\\.?[0-9]*\\b";
// 多项式每一项的正则表达式
Pattern pattern = Pattern.compile(xiang);
Matcher matcher;
String[] arr1;
String[] arr2;
int num1 = 0;
int num2 = 0;
matcher = pattern.matcher(forArr[0]);
while (matcher.find()) {
num1++;
}
matcher = pattern.matcher(forArr[1]);
while (matcher.find()) {
num2++;
}
coef1 = new float[num1];
expn1 = new int[num1];
coef2 = new float[num2];
expn2 = new int[num2];
arr1 = new String[num1];
arr2 = new String[num2];
matcher = pattern.matcher(forArr[0]);
int i = 0;
while (matcher.find()) {
arr1[i] = matcher.group();
i++;
}
i = 0;
matcher = pattern.matcher(forArr[1]);
while (matcher.find()) {
arr2[i] = matcher.group();
i++;
}
String[] str;
for (i = 0; i < arr1.length; i++) {
System.out.println(arr1[i]);
if (arr1[i].equals("x")) {
coef1[i] = 1;
expn1[i] = 1;
} else if (arr1[i].equals("-x")) {
coef1[i] = -1;
expn1[i] = 1;
} else if (!arr1[i].contains("x")) {
// 整数
coef1[i] = Float.parseFloat(arr1[i]);
expn1[i] = 0;
} else if (arr1[i].endsWith("x") && !arr1[i].contains("^")) {
// 2x,3x这种形式
coef1[i] = Float.parseFloat(arr1[i].replace("x", ""));
expn1[i] = 1;
} else if (arr1[i].startsWith("x") && arr1[i].contains("^")) {
// x^3这种形式
coef1[i] = 1;
expn1[i] = Integer.parseInt(arr1[i].replace("x^", ""));
} else {
str = arr1[i].split("x\\^");
if (str[0].equals("-"))
coef1[i] = -1;
else
coef1[i] = Float.parseFloat(str[0]);
expn1[i] = Integer.parseInt(str[1]);
}
System.out.println("系数:指数=" + coef1[i] + "," + expn1[i]);
}
for (i = 0; i < arr2.length; i++) {
System.out.println(arr2[i]);
if (arr2[i].equals("x")) {
coef2[i] = 1;
expn2[i] = 1;
} else if (arr2[i].equals("-x")) {
coef2[i] = -1;
expn2[i] = 1;
} else if (!arr2[i].contains("x")) {
// 整数
coef2[i] = Float.parseFloat(arr2[i]);
expn2[i] = 0;
} else if (arr2[i].endsWith("x") && !arr2[i].startsWith("x")) {
// 2x,3x这种形式
coef2[i] = Float.parseFloat(arr2[i].replace("x", ""));
expn2[i] = 1;
} else if (arr2[i].startsWith("x") && arr2[i].contains("^")) {
// x^3这种形式
coef2[i] = 1;
expn2[i] = Integer.parseInt(arr2[i].replace("x^", ""));
} else {
str = arr2[i].split("x\\^");
if (str[0].equals("-"))
coef2[i] = -1;
else
coef2[i] = Float.parseFloat(str[0]);
expn2[i] = Integer.parseInt(str[1]);
}
System.out.println("系数:指数=" + coef2[i] + "," + expn2[i]);
}
System.out.println("第一个多项式:");
for (i = 0; i < coef1.length; i++)
System.out.println(coef1[i] + ":" + expn1[i]);
System.out.println("第二个多项式:");
for (i = 0; i < coef2.length; i++)
System.out.println(coef2[i] + ":" + expn2[i]);
}
// 验证表达式是否正确
private boolean verifyFormula(String s) {
boolean b = false;
String duoxiangshi = "\\((([+-]?[0-9]+\\.?[0-9]*)|(([+-]?[0-9]+\\.?[0-9]*)|-)*x(\\^-?[0-9]+)?)(([+-])(([0-9]+\\.?[0-9]*)|(([0-9]+\\.?[0-9]*)|-)*x(\\^-?[0-9]+)?))*\\)";
String regex = duoxiangshi + "([+-]|\\*)" + duoxiangshi;
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(s);
b = matcher.matches();
return b;
}
}
这时一个有图形界面的一元稀疏多项式就完成了。
我在写这个小项目时候遇到的其他问题还包括
1、用正则表达式解析字符串,关于正则表达式,如果大家没有接触过,推荐给大家一个网址,非常有用
http://deerchao.net/tutorials/regex/regex.htm#howtouse
2、dll中C的返回值类型和java的接收的类型,这个对于刚接触java的jni的新手来说也是一个很头疼的问题。
3、在学习jni的过程中,还了解到可以用java封装好的jna,jna比jni的编程过程更佳简易
感悟:
写这个带图形界面的一元稀疏多项式计算器,完全只是处于感兴趣。本来是一个难度系数最低的课设,但后来脑洞大开想加入界面。但由于没学过用C写界面,而自己又有一定的swing编程基础,之前也听过不同语言混合开发一个程序。于是便开始在网上搜索资料。由此便一发不可收拾了,时时刻刻都在弄这个,因为领悟能力有限,有四五天是熬夜熬到凌晨三四点,最终经过四五天才完成这个作业。写完这个小项目,满满的成就感。希望大家不要被遇到的困难击退,坚持下来,总会有收获的