java高級用法之JNA中的Structure
簡介
前面我們講到了JNA中JAVA代碼和native代碼的映射,雖然可以通過TypeMapper來將JAVA中的類型和native中的類型進(jìn)行映射,但是native中的數(shù)據(jù)類型都是基礎(chǔ)類型,如果native中的數(shù)據(jù)類型是復(fù)雜的struct類型該如何進(jìn)行映射呢?
不用怕,JNA提供了Structure類,來幫助我們進(jìn)行這些映射處理。
native中的struct
什么時(shí)候會用到struct呢?一般情況下,當(dāng)我們需要自定義一個(gè)數(shù)據(jù)類的時(shí)候,一般情況下,在JAVA中需要定義一個(gè)class(在JDK17中,可以使用更加簡單的record來進(jìn)行替換),但是為一個(gè)數(shù)據(jù)結(jié)構(gòu)定義class顯然有些臃腫,所以在native語言中,有一些更簡單的數(shù)據(jù)結(jié)構(gòu)叫做struct。
我們先看一個(gè)struct的定義:
typedef struct _Point { int x, y; } Point;
上面的代碼中,我們定義了一個(gè)Pointer的struct數(shù)據(jù)類下,在其中定義了int的x和y值表示Point的橫縱坐標(biāo)。
struct的使用有兩種情況,一種是值傳遞,一種是引用傳遞。先來看下這兩種情況在native方法中是怎么使用的:
引用傳遞:
Point* translate(Point* pt, int dx, int dy);
值傳遞:
Point translate(Point pt, int dx, int dy);
Structure
那么對于native方法中的struct數(shù)據(jù)類型的使用方式,應(yīng)該如何進(jìn)行映射呢? JNA為我們提供了Structure類。
默認(rèn)情況下如果Structure是作為參數(shù)或者返回值,那么映射的是struct*,如果表示的是Structure中的一個(gè)字段,那么映射的是struct。
當(dāng)然你也可以強(qiáng)制使用Structure.ByReference 或者 Structure.ByValue 來表示是傳遞引用還是傳值。
我們看下上面的native的例子中,如果使用JNA的Structure來進(jìn)行映射應(yīng)該怎么實(shí)現(xiàn):
指針映射:
class Point extends Structure { public int x, y; } Point translate(Point pt, int x, int y); ... Point pt = new Point(); Point result = translate(pt, 100, 100);
傳值映射:
class Point extends Structure { public static class ByValue extends Point implements Structure.ByValue { } public int x, y; } Point.ByValue translate(Point.ByValue pt, int x, int y); ... Point.ByValue pt = new Point.ByValue(); Point result = translate(pt, 100, 100);
Structure內(nèi)部提供了兩個(gè)interface,分別是ByValue和ByReference:
public abstract class Structure { public interface ByValue { } public interface ByReference { }
要使用的話,需要繼承對應(yīng)的interface。
特殊類型的Structure
除了上面我們提到的傳值或者傳引用的struct,還有其他更加復(fù)雜的struct用法。
結(jié)構(gòu)體數(shù)組作為參數(shù)
首先來看一下結(jié)構(gòu)體數(shù)組作為參數(shù)的情況:
void get_devices(struct Device[], int size);
對應(yīng)結(jié)構(gòu)體數(shù)組,可以直接使用JNA中對應(yīng)的Structure數(shù)組來進(jìn)行映射:
int size = ... Device[] devices = new Device[size]; lib.get_devices(devices, devices.length);
結(jié)構(gòu)體數(shù)組作為返回值
如果native方法返回的是一個(gè)指向結(jié)構(gòu)體的指針,其本質(zhì)上是一個(gè)結(jié)構(gòu)體數(shù)組,我們應(yīng)該怎么處理呢?
先看一下native方法的定義:
struct Display* get_displays(int* pcount); void free_displays(struct Display* displays);
get_displays方法返回的是一個(gè)指向結(jié)構(gòu)體數(shù)組的指針,pcount是結(jié)構(gòu)體的個(gè)數(shù)。
對應(yīng)的JAVA代碼如下:
Display get_displays(IntByReference pcount); void free_displays(Display[] displays);
對于第一個(gè)方法來說,我們只返回了一個(gè)Display,但是可以通過Structure.toArray(int) 方法將其轉(zhuǎn)換成為結(jié)構(gòu)體數(shù)組。傳入到第二個(gè)方法中,具體的調(diào)用方式如下:
IntByReference pcount = new IntByReference(); Display d = lib.get_displays(pcount); Display[] displays = (Display[])d.toArray(pcount.getValue()); ... lib.free_displays(displays);
結(jié)構(gòu)體中的結(jié)構(gòu)體
結(jié)構(gòu)體中也可以嵌入結(jié)構(gòu)體,先看下native方法的定義:
typedef struct _Point { int x, y; } Point; typedef struct _Line { Point start; Point end; } Line;
對應(yīng)的JAVA代碼如下:
class Point extends Structure { public int x, y; } class Line extends Structure { public Point start; public Point end; }
如果是下面的結(jié)構(gòu)體中的指向結(jié)構(gòu)體的指針:
typedef struct _Line2 { Point* p1; Point* p2; } Line2;
那么對應(yīng)的代碼如下:
class Point extends Structure { public static class ByReference extends Point implements Structure.ByReference { } public int x, y; } class Line2 extends Structure { public Point.ByReference p1; public Point.ByReference p2; }
或者直接使用Pointer作為Structure的屬性值:
class Line2 extends Structure { public Pointer p1; public Pointer p2; } Line2 line2; Point p1, p2; ... line2.p1 = p1.getPointer(); line2.p2 = p2.getPointer();
結(jié)構(gòu)體中的數(shù)組
如果結(jié)構(gòu)體中帶有固定大小的數(shù)組:
typedef struct _Buffer { char buf1[32]; char buf2[1024]; } Buffer;
那么我們在JAVA中需要指定數(shù)據(jù)的大?。?/strong>
class Buffer extends Structure { public byte[] buf1 = new byte[32]; public byte[] buf2 = new byte[1024]; }
如果結(jié)構(gòu)體中是動(dòng)態(tài)大小的數(shù)組:
typedef struct _Header { int flags; int buf_length; char buffer[1]; } Header;
那么我們需要在JAVA的結(jié)構(gòu)體中定義一個(gè)構(gòu)造函數(shù),傳入bufferSize的大小,并分配對應(yīng)的內(nèi)存空間:
結(jié)構(gòu)體中的可變字段
默認(rèn)情況下結(jié)構(gòu)體中的內(nèi)容和native memory的內(nèi)容是一致的。JNA會在函數(shù)調(diào)用之前將Structure的內(nèi)容寫入到native memory中,并且在函數(shù)調(diào)用之后,將 native memory中的內(nèi)容回寫到Structure中。
默認(rèn)情況下是將結(jié)構(gòu)體中的所有字段都進(jìn)行寫入和寫出。但是在某些情況下,我們希望某些字段不進(jìn)行自動(dòng)更新。這個(gè)時(shí)候就可以使用volatile關(guān)鍵字,如下所示:
class Data extends com.sun.jna.Structure { public volatile int refCount; public int value; } ... Data data = new Data();
當(dāng)然,你也可以強(qiáng)制使用Structure.writeField(String)來將字段信息寫入內(nèi)存中,或者使用Structure.read() 來更新整個(gè)結(jié)構(gòu)體的信息或者使用data.readField(“refCount”)來更新具體字段信息。
結(jié)構(gòu)體中的只讀字段
如果不想從JAVA代碼中對Structure的內(nèi)容進(jìn)行修改,則可以將對應(yīng)的字段標(biāo)記為final。在這種情況下,雖然JAVA代碼不能直接對其進(jìn)行修改,但是仍然可以調(diào)用read方法從native memory中讀取對應(yīng)的內(nèi)容并覆蓋Structure中對應(yīng)的值。
來看下JAVA中如何使用final字段:
class ReadOnly extends com.sun.jna.Structure { public final int refCount; { // 初始化 refCount = -1; // 從內(nèi)存中讀取數(shù)據(jù) read(); } }
注意所有的字段的初始化都應(yīng)該在構(gòu)造函數(shù)或者靜態(tài)方法塊中進(jìn)行。
總結(jié)
結(jié)構(gòu)體是native方法中經(jīng)常會使用到的一種數(shù)據(jù)類型,JNA中對其進(jìn)行映射的方法是我們要掌握的。
到此這篇關(guān)于java高級用法之JNA中的Structure的文章就介紹到這了,更多相關(guān)java 中的Structure內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用hutool實(shí)現(xiàn)文件大小的友好輸出
這篇文章主要為大家詳細(xì)介紹了Java如何使用hutool實(shí)現(xiàn)文件大小的友好輸出,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11解決@RequestMapping和@FeignClient放在同一個(gè)接口上遇到的坑
這篇文章主要介紹了解決@RequestMapping和@FeignClient放在同一個(gè)接口上遇到的坑,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07詳解Java synchronized關(guān)鍵字的用法
在多線程編程中常常使用鎖機(jī)制來確保同一時(shí)刻只有一個(gè)線程能夠修改共享內(nèi)存,在Java中一般是使用synchronized作為鎖機(jī)制,下面就讓我們來學(xué)習(xí)一下如何使用synchronized實(shí)現(xiàn)線程安全吧2023-08-08Struts2之Validator驗(yàn)證框架的詳細(xì)介紹
Struts2中提供了數(shù)據(jù)校驗(yàn)驗(yàn)證數(shù)據(jù)例如驗(yàn)證郵件、數(shù)字等,本篇文章介紹了Struts2之Validator的詳細(xì)介紹,有興趣的可以了解一下。2017-03-03