环境:
Java: jdk17.0.11 maven3.8.1
Clang: gcc.exe (Rev1, Built by MSYS2 project) 14.2.0
Java代码样例:
java">package com.hmwl.godlike.demo.printf2;/*** @description:* @author: * @email: * @date: 2024/11/8 15:23*/
public class Printf2 {public static native String sprintf(String format, double x);static {System.loadLibrary("Printf2");}
}
java">package com.hmwl.godlike.demo.printf2;/*** @description:* @author:* @email: * @date: 2024/11/8 15:22*/
public class Printf2Test {public static void main(String[] args) {double prince = 44.95;double tax = 7.75;double amountDue = prince * (1 + tax / 100);String s = Printf2.sprintf("Amount due: %8.2f", amountDue);System.out.println(s);}
}
在Printf2.java所在目录执行:
javac -h . Printf2.java
得到含路径的.h头文件。参考如下:
/** Class: com_hmwl_godlike_demo_printf2_Printf2* Method: sprintf* Signature: (Ljava/lang/String;D)Ljava/lang/String;*/
JNIEXPORT jstring JNICALL Java_com_hmwl_godlike_demo_printf2_Printf2_sprintf(JNIEnv *, jclass, jstring, jdouble);#ifdef __cplusplus
}
#endif
#endif
编写本地方法如下:
#include "com_hmwl_godlike_demo_printf2_Printf2.h"
#include <string.h>
#include <stdlib.h>
#include <float.h>char* find_format(const char format[])
{char* p;char* q;p = strchr(format, '%');while (p != NULL && *(p + 1) == '%') /* skip %% */p = strchr(p + 2, '%');if (p == NULL) return NULL;/* now check that % is unique */p++;q = strchr(p, '%');while (q != NULL && *(q + 1) == '%') /* skip %% */q = strchr(q + 2, '%');if (q != NULL) return NULL; /* % not unique */q = p + strspn(p, " -0+#"); /* skip past flags */q += strspn(q, "0123456789"); /* skip past field width */if (*q == '.') { q++; q += strspn(q, "0123456789"); }/* skip past precision */if (strchr("eEfFgG", *q) == NULL) return NULL;/* not a floating-point format */return p;
}JNIEXPORT jstring JNICALL Java_com_hmwl_godlike_demo_printf2_Printf2_sprintf(JNIEnv* env, jclass cl,jstring format, jdouble x)
{const char* cformat;char* fmt;jstring ret;cformat = (*env)->GetStringUTFChars(env, format, NULL);fmt = find_format(cformat);if (fmt == NULL)ret = format;else{char* cret;int width = atoi(fmt);if (width == 0) width = DBL_DIG + 10;cret = (char*) malloc(strlen(cformat) + width);sprintf(cret, cformat, x);ret = (*env)->NewStringUTF(env, cret);free(cret);}(*env)->ReleaseStringUTFChars(env, format, cformat);return ret;
}void main() {
}
使用 msys2 命令行,切换到c代码所在文件夹,执行:
gcc -shared -o Printf2.dll Printf2.c -I"C:\Program Files\Java\jdk-17.0.11\include" -I"C:\Program Files\Java\jdk-17.0.11\include\win32"
-I 这两个目录的目的是为了让gcc 能够链接到 jni.h
编译无误,可以执行 Printf2Test 得到最终结果:
注意点:
- c代码中所引用的.h 方法名称要与实际生成的一致。
- gcc的平台要和jdk一致,否则即使编译通过,执行也会报错如下。
鸣谢文献: 《Java核心卷卷2 12th》作者: Cay Horstmann