?? appa.htm
字號:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Thinking in Java | Chinese Version by Trans Bot</title>
<meta name="Microsoft Theme" content="inmotion 111, default"></head>
<body background="../_themes/inmotion/inmtextb.gif" tppabs="http://member.netease.com/%7etransbot/Thinking%20in%20Java/_themes/inmotion/inmtextb.gif" bgcolor="#FFFFCC" text="#000000" link="#800000" vlink="#996633" alink="#FF3399">
<p>附錄A 使用非JAVA代碼<br>
<br>
JAVA語言及其標準API(應用程序編程接口)應付應用程序的編寫已綽綽有余。但在某些情況下,還是必須使用非JAVA編碼。例如,我們有時要訪問操作系統的專用特性,與特殊的硬件設備打交道,重復使用現有的非Java接口,或者要使用“對時間敏感”的代碼段,等等。與非Java代碼的溝通要求獲得編譯器和“虛擬機”的專門支持,并需附加的工具將Java代碼映射成非Java代碼(也有一個簡單方法:在第15章的“一個Web應用”小節中,有個例子解釋了如何利用標準輸入輸出同非Java代碼連接)。目前,不同的開發商為我們提供了不同的方案:Java
1.1有“Java固有接口”(Java Native Interface,JNI),網景提出了自己的“Java運行期接口”(Java
Runtime Interface)計劃,而微軟提供了J/Direct、“本源接口”(Raw Native
Interface,RNI)以及Java/COM集成方案。<br>
各開發商在這個問題上所持的不同態度對程序員是非常不利的。若Java應用必須調用固有方法,則程序員或許要實現固有方法的不同版本——具體由應用程序運行的平臺決定。程序員也許實際需要不同版本的Java代碼,以及不同的Java虛擬機。<br>
另一個方案是CORBA(通用對象請求代理結構),這是由OMG(對象管理組,一家非贏利性的公司協會)開發的一種集成技術。CORBA并非任何語言的一部分,只是實現通用通信總線及服務的一種規范。利用它可在由不同語言實現的對象之間實現“相互操作”的能力。這種通信總線的名字叫作ORB(對象請求代理),是由其他開發商實現的一種產品,但并不屬于Java語言規范的一部分。<br>
本附錄將對JNI,J/DIRECT,RNI,JAVA/COM集成和CORBA進行概述。但不會作更深層次的探討,甚至有時還假定讀者已對相關的概念和技術有了一定程度的認識。但到最后,大家應該能夠自行比較不同的方法,并根據自己要解決的問題挑選出最恰當的一種。<br>
<br>
A.1 Java固有接口<br>
JNI是一種包容極廣的編程接口,允許我們從Java應用程序里調用固有方法。它是在Java
1.1里新增的,維持著與Java 1.0的相應特性——“固有方法接口”(NMI)——某種程度的兼容。NMI設計上一些特點使其未獲所有虛擬機的支持。考慮到這個原因,Java語言將來的版本可能不再提供對NMI的支持,這兒也不準備討論它。<br>
目前,JNI只能與用C或C++寫成的固有方法打交道。利用JNI,我們的固有方法可以:<br>
■創建、檢查及更新Java對象(包括數組和字串)<br>
■調用Java方法<br>
■俘獲和丟棄“異常”<br>
■裝載類并獲取類信息<br>
■進行運行期類型檢查<br>
所以,原來在Java中能對類及對象做的幾乎所有事情在固有方法中同樣可以做到。<br>
<br>
A.1.1 調用固有方法<br>
我們先從一個簡單的例子開始:一個Java程序調用固有方法,后者再調用Win32的API函數MessageBox(),顯示出一個圖形化的文本框。這個例子稍后也會與J/Direct一志使用。若您的平臺不是Win32,只需將包含了下述內容的C頭:<br>
#include <windows.h><br>
替換成:<br>
#include <stdio.h><br>
并將對MessageBox()的調用換成調用printf()即可。<br>
第一步是寫出對固有方法及它的自變量進行聲明的Java代碼:<br>
<br>
999頁程序<br>
<br>
在固有方法聲明的后面,跟隨有一個static代碼塊,它會調用System.loadLibrary()(可在任何時候調用它,但這樣做更恰當)System.loadLibrary()將一個DLL載入內存,并建立同它的鏈接。DLL必須位于您的系統路徑,或者在包含了Java類文件的目錄中。根據具體的平臺,JVM會自動添加適當的文件擴展名。<br>
<br>
1. C頭文件生成器:javah<br>
現在編譯您的Java源文件,并對編譯出來的.class文件運行javah。javah是在1.0版里提供的,但由于我們要使用Java
1.1 JNI,所以必須指定-jni參數:<br>
javah -jni ShowMsgBox<br>
javah會讀入類文件,并為每個固有方法聲明在C或C++頭文件里生成一個函數原型。下面是輸出結果——ShowMsgBox.h源文件(為符合本書的要求,稍微進行了一下修改):<br>
<br>
1000頁程序<br>
<br>
從“#ifdef_cplusplus”這個預處理引導命令可以看出,該文件既可由C編譯器編譯,亦可由C++編譯器編譯。第一個#include命令包括jni.h——一個頭文件,作用之一是定義在文件其余部分用到的類型;JNIEXPORT和JNICALL是一些宏,它們進行了適當的擴充,以便與那些不同平臺專用的引導命令配合;JNIEnv,jobject以及jstring則是JNI數據類型定義。<br>
<br>
2. 名稱管理和函數簽名<br>
JNI統一了固有方法的命名規則;這一點是非常重要的,因為它屬于虛擬機將Java調用與固有方法鏈接起來的機制的一部分。從根本上說,所有固有方法都要以一個“Java”起頭,后面跟隨Java方法的名字;下劃線字符則作為分隔符使用。若Java固有方法“過載”(即命名重復),那么也把函數簽名追加到名字后面。在原型前面的注釋里,大家可看到固有的簽名。欲了解命名規則和固有方法簽名更詳細的情況,請參考相應的JNI文檔。<br>
<br>
3. 實現自己的DLL<br>
此時,我們要做的全部事情就是寫一個C或C++源文件,在其中包含由javah生成的頭文件;并實現固有方法;然后編譯它,生成一個動態鏈接庫。這一部分的工作是與平臺有關的,所以我假定讀者已經知道如何創建一個DLL。通過調用一個Win32
API,下面的代碼實現了固有方法。隨后,它會編譯和鏈接到一個名為MsgImpl.dll的文件里:<br>
<br>
1001頁程序<br>
<br>
若對Win32沒有興趣,只需跳過MessageBox()調用;最有趣的部分是它周圍的代碼。傳遞到固有方法內部的自變量是返回Java的大門。第一個自變量是類型JNIEnv的,其中包含了回調JVM需要的所有掛鉤(下一節再詳細講述)。由于方法的類型不同,第二個自變量也有自己不同的含義。對于象上例那樣的非static方法(也叫作實例方法),第二個自變量等價于C++的“this”指針,并類似于Java的“this”:都引用了調用固有方法的那個對象。對于static方法,它是對特定Class對象的一個引用,方法就是在那個Class對象里實現的。<br>
剩余的自變量代表傳遞到固有方法調用里的Java對象。主類型也是以這種形式傳遞的,但它們進行的“按值”傳遞。<br>
在后面的小節里,我們準備講述如何從一個固有方法的內部訪問和控制JVM,同時對上述代碼進行更詳盡的解釋。<br>
<br>
A.1.2 訪問JNI函數:JNIEnv自變量<br>
利用JNI函數,程序員可從一個固有方法的內部與JVM打交道。正如大家在前面的例子中看到的那樣,每個JNI固有方法都會接收一個特殊的自變量作為自己的第一個參數:JNIEnv自變量——它是指向類型為JNIEnv_的一個特殊JNI數據結構的指針。JNI數據結構的一個元素是指向由JVM生成的一個數組的指針;該數組的每個元素都是指向一個JNI函數的指針。可從固有方法的內部發出對JNI函數的調用,做法是撤消對這些指針的引用(具體的操作實際很簡單)。每種JVM都以自己的方式實現了JNI函數,但它們的地址肯定位于預先定義好的偏移處。<br>
利用JNIEnv自變量,程序員可訪問一系列函數。這些函數可劃分為下述類別:<br>
■獲取版本信息<br>
■進行類和對象操作<br>
■控制對Java對象的全局和局部引用<br>
■訪問實例字段和靜態字段<br>
■調用實例方法和靜態方法<br>
■執行字串和數組操作<br>
■產生和控制Java異常<br>
JNI函數的數量相當多,這里不再詳述。相反,我會向大家揭示使用這些函數時背后的一些基本原理。欲了解更詳細的情況,請參閱自己所用編譯器的JNI文檔。<br>
若觀察一下jni.h頭文件,就會發現在#ifdef _cplusplus預處理器條件的內部,當由C++編譯器編譯時,JNIEnv_結構被定義成一個類。這個類包含了大量內嵌函數。通過一種簡單而且熟悉的語法,這些函數讓我們可以從容訪問JNI函數。例如,前例包含了下面這行代碼:<br>
(*jEnv)->ReleaseStringUTFChars(jEnv, jMsg,msg);<br>
它在C++里可改寫成下面這個樣子:<br>
jEnv->ReleaseStringUTFChars(jMsg,msg);<br>
大家可注意到自己不再需要同時撤消對jEnv的兩個引用,相同的指針不再作為第一個參數傳遞給JNI函數調用。在這些例子剩下的地方,我會使用C++風格的代碼。<br>
<br>
1. 訪問Java字串<br>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -