JNI String類型

2021-03-02 極客Furzoom

在上一篇中介紹了JNI的基本數據類型。本文是JNI系列的第三篇,介紹JNI中的除了基本類型之外的引用類型—String類型。

系列文章的大綱如下:

JNI引用類型

在JNI定義了一系列的引用類型,與Java中的類型對應,有如下的繼承關係:

// JNI 類型 Java類型
jobject (all Java objects)
|-- jclass (java.lang.Class objects)
|-- jstring (java.lang.String objects
|-- jarray (arrays)
| |-- jobjectArray (object arrays)
| |-- jbooleanArray (boolean arrays)
| |-- jbyteArray (byte arrays)
| |-- jcharArray (char arrays)
| |-- jshortArray (short arrays)
| |-- jintArray (int arrays)
| |-- jlongArray (long arrays)
| |-- jfloatArray (float arrays)
| |-- jdoubleArray (double arrays)
|-- jthrowable (java.lang.Throwable objects)

在C的實現中,所以的JNI引用類型都是jobject,如:

struct _jobject;

typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;

而在C++的實現中,有類似如下的定義:

class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};

typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;

可以看到JNI中所以的引用類型都是指針。

還有jvalue的定義,用於數組元素類型的定義:

typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;

String的常用操作

對String類型,思考下面兩個問題:

Java中String使用Unicode編碼,C/C++中的char *是UTF-8編碼(Linux/Android/MacOS),如何轉換編碼?

所有引用類型都面對一個問題,內存管理方式不同,Java中是有GC的,C/C++中需要手動管理內存,JNI中怎麼處理?

對於兩個問題,JNI提供相應的函數供我們使用,我們使用時需要特別的小心。

JNI的字符操作方法Unicode編碼字符串

jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);

jsize GetStringLength(JNIEnv *env, jstring string);

const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);

void ReleaseStringChars(JNIEnv *env, jstring string,
const jchar *chars);

所有的函數的第一個參數都是JNIEnv *,稱為JNI接口指針,在生成的native方法的中第一個參數都是它。

NewString(),通過Unicode字符數組構造一個jstring,也就是java.lang.String對象。JVM會進行內存管理。len是unicodeChars的長度。

GetStringLength(),返回string中Unicode字符的數量。

GetStringChars(),返回string的表示的Unicode編碼的字符數組。在調用ReleaseStringChars()前都可用。這裡就需要自行進行內存管理。如果isCopy不是空,如果返回的數組是一個拷貝,其值會置為JNI_TRUE,否則置為JNI_FALSE。

ReleaseStringChars(),通知JVM,native代碼不會再使用chars,chars是使用GetStringChars()得到的返回值。

UTF-8編碼字符串

UTF-8編碼的字符串與Unicode編碼字符串接口類似。平時使用最多的也是下面的幾個接口。

jstring NewStringUTF(JNIEnv *env, const char *bytes);

jsize GetStringUTFLength(JNIEnv *env, jstring string);

const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);

void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);

通過下面的實例來加深一下理解。

實例

我們來看一個實例。

還是Hello.java

package myjni;

public class Hello {
static {
System.loadLibrary("hello");
}

// 返回字符串:"Hey, name"
public native String hello(String name);

public static void main(String[] args) {
Hello hello = new Hello();

String name = "furzoom";
System.out.println("Say hello to " + name + ": " + hello.hello(name));

name = "楓竹夢";
System.out.println("Say hello to " + name + ": " + hello.hello(name));
}
}

如果還不清楚如果生成頭文件請參考JNI簡介。

生成頭文件myjni_Hello.h的籤名為:

/*
* Class: myjni_Hello
* Method: hello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_myjni_Hello_hello
(JNIEnv *, jobject, jstring);

實現函數Java_myjni_Hello_hello:

JNIEXPORT jstring JNICALL Java_myjni_Hello_hello(JNIEnv *env, jobject obj, jstring jname) {
// 得到傳入的UTF-8編碼的字符串
const char* name = env->GetStringUTFChars(jname, nullptr);
if (name == nullptr) {
return nullptr;
}

// 構造需要返回的結果字符串
std::string result("Hey, ");
result += name;
// 使用完從GetStringUTFChars的字符串後,需要通知JVM
env->ReleaseStringUTFChars(jname, name);

// 從JNI的UTF-8編碼字符串構造jstring
return env->NewStringUTF(result.c_str());
}

編譯生成動態庫,並運行Java程序得到輸出:

Say hello to furzoom: Hey, furzoom
Say hello to 楓竹夢: Hey, 楓竹夢

本文完。

相關焦點

  • go語言中 int、float、string 類型轉換
    前言 Go 開發中經常涉及到類型轉換,本文就重點記錄下 整形、浮點型和字符串類型互相轉換的方法。整形轉字符串 fmt.Sprintf將整數轉為字符串,字符串可以是2進位、8進位、10進位或者16進位的表示。
  • 深入解析 String.intern
    如有好文章投稿,請點擊 → 這裡了解詳情引言在 JAVA 語言中有8中基本類型和一種比較特殊的類型String。這些類型為了使他們在運行過程中速度更快,更節省內存,都提供了一種常量池的概念。常量池就類似一個JAVA系統級別提供的緩存。8種基本類型的常量池都是系統協調的,String類型的常量池比較特殊。它的主要使用方法有兩種:接下來我們主要來談一下String.intern方法。
  • String 數據類型 轉 int類型
    String類型轉int類型通常需要int的包裝類Integer,該類有三個方法可以實現這種轉換,分別為decode(String s)、parseInt
  • 深入解析String中的intern
    原文地址:深入解析String#intern引言在 JAVA 語言中有8中基本類型和一種比較特殊的類型String。
  • [數據類型] golang 筆記 string/int/array/map/slice/point/json
    有點兒excited寫了幾個Demo做筆記,換一種類型的積木玩兒吧,所有Demo環境信息如下:基本數據結構 string/int// filename go-base.go// 儀式感要有,Hello World不能少package mainimport "fmt"func main() {
  • C++STL(一)----string和vector
    STLvector容器1.容器和容器初始化及其遍歷:功能:vector與普通數組區別:動態擴展:#include<iostream>#include<algorithm>#include<vector>using namespace std;int main(){ int
  • TypeScript 高級類型及用法
    語法:T | U其返回類型為連接的多個類型中的任意一個用法:假設聲明一個數據,既可以是 string 類型,也可以是 number 類型let stringOrNumber:string | number = 0
  • Java字符串String那些事
    引言眾所周知在java裡面除了8種基本數據類型的話,還有一種特殊的類型String,這個類型是我們每天搬磚都基本上要使用它。❝String 類型可能是 Java 中應用最頻繁的引用類型,但它的性能問題卻常常被忽略。高效的使用字符串,可以提升系統的整體性能。當然,要做到高效使用字符串,需要深入了解其特性。
  • 卻支持String呢?
    # 結論switch 底層是使用 int 型 來進行判斷的,即使是枚舉、String類型,最終也是轉變成 int 型。由於 long 型表示範圍大於 int 型,因此不支持 long 類型。下面詳細介紹下各個類型是如何被轉變成 int 類型的,使用的編譯命令為 javac,反編譯網站為:http://javare.cn# 枚舉類型是咋變成 int 類型的?
  • 「Golang庫」 Cast - 簡單、高效且類型安全的數據類型轉換小工具
    Cast —— Golang 簡單、高效且類型安全的數據類型轉換小工具一個簡單、高效且類型安全的類型轉換工具(庫),致力於防止程序在類型轉換過程中發生崩潰(Panic)簡介spf13/cast - 源碼(https://github.com/spf13/cast)開發者需要頻繁將動態類型的數據進行類型轉換
  • 完整的 string.Format對C#字符串格式化
    常用的格式化數值結果表 字符說明示例輸出C貨幣string.Format("{0:C3}", 2)$2.000D十進位string.Format("{0:D3}", 2)002E科學計數法1.20E+0011.20E+001G常規string.Format("{0:G}", 2)2N用分號隔開的數字string.Format
  • Java 中的 Switch 是如何支持 String 的?為什麼不支持 long?
    我們知道 Java Switch 支持byte、short、int 類型,在 JDK 1.5 時,支持了枚舉類型,在 JDK 1.7 時,又支持了 String類型。由於 long 型表示範圍大於 int 型,因此不支持 long 類型。
  • string、const char *、 char *轉換 || cout不明確問題解決
    char * const 指針常量:實質是常量,其內部存儲的是指針,指針的指向不能改變char * 指向字符類型的指針#include"stringAndChar.h"#include <string.h>#include <iostream>using namespace std;void test() { char cArray[] = "ABCD"; char* cArrayPtr = cArray; cout<< cArray <<
  • Java中的 Switch 是如何支持 String 的?為什麼不支持 long?
    我們知道 Java Switch 支持byte、short、int 類型,在 JDK 1.5 時,支持了枚舉類型,在 JDK 1.7 時,又支持了 String類型。那麼它為什麼就不能支持 long 類型呢,明明它跟 byte、short、int 一樣都是數值型,它又是咋支持 String 類型的呢?
  • Java 中的 Switch 都支持 String 了,為什麼不支持 long?
    我們知道 Java Switch 支持byte、short、int 類型,在 JDK 1.5 時,支持了枚舉類型,在 JDK 1.7 時,又支持了 String類型。那麼它為什麼就不能支持 long 類型呢,明明它跟 byte、short、int 一樣都是數值型,它又是咋支持 String 類型的呢?
  • 詳解 PHP 7.4 的類型屬性
    string $b = 'foo'; private Foo $prop; protected static string $static = 'default';}如果你不確定類型的額外好處,我建議您首先閱讀這篇文章。未初始化在進入正題之前,首先要探討一個與類型屬性有關的重要方面。
  • 程式語言中的鴨子類型 Duck Typing
    (duck typing)這是程序設計中的一種類型推斷風格,這種風格適用於動態語言(比如PHP、Python、Ruby、Typescript、Perl、Objective-C、Lua、Julia、JavaScript、Java、Groovy、C#等)和某些靜態語言(比如Golang,一般來說,靜態類型語言在編譯時便已確定了變量的類型,但是Golang的實現是:在編譯時推斷變量的類型
  • 關於Java字符串(String)10個最常見問題
    為什麼在安全敏感信息場合應該用char[]而不是string?String具有不可變的特性,當字符串一旦被創建,那麼知道垃圾收集器處理之前他們都是不可變的。如果使用數組,那麼你可以明確地改變其內部單元數據。因此,安全敏感的信息例如密碼不應該在系統中任何時候都存在。3. 我們可以在switch語句中使用string嗎?是的,在Java 7中可以!