MethodV(JNIEnv *, jobject, jclass, jmethodID, va_list)
Estas tres funciones son similares a las anteriores con la diferencia de invocar al método de la clase, por este motivo tenemos que introducir un parámetro más que la clase de la que se quiere ejecutar el método, de esta forma podremos llamar a los métodos de la clase padre como se puede ver en el Listado 4. Para llamar a métodos estáticos tendremos que recoger el ID del método con GetStaticMethodID y llamar a las funciones:
CallStaticMethod(JNIEnv *, jclass, jmethod, ...)
CallStaticMethodA(JNIEnv *, jclass, jmethod, jvalue *)
CallStaticMethod(JNIEnv *, jclass, jmethod, va_list)
El segundo parámetro como es lógico cambia ya que el método estático pertenece a la clase y no al objeto, es por este motivo por lo que le pasaremos un tipo jclass. Después utilizaremos la función que corresponda con el tipo devuelto por nuestro método y la que mejor se adapte a nuestra forma de pasar argumentos.
Operaciones con clases y objetos
A continuación pasamos a explicar las funciones que nos permitirán realizar operaciones con objetos y clases, algunas de estas funciones ya han sido vistas anteriormente.
jobject AllocObject(JNIEnv *, jclass)
Esta función nos permite crear una instancia de un objeto sin que se llame al constructor de este. Los parámetros son en primer lugar el entorno JNI y el segundo es un tipo jclass que será la clase que deseamos crear instancia, este nos retorna un tipo jobject que será la instancia de la clase o un NULL en caso de que no se haya podido crear. Las excepciones que pueden producirse con esta función serán:
- InstantiationException: Si la clase es un interfaces o una clase abstracta, para evitarla deberemos crear una clase heredada de esta que defina los métodos abstractos de esta.
- OutOfMemoryError: Si el sistema se ha quedado sin memoria.
jobject NewObject(JNIEnv *, jclass, jmethodID, ...)
jobject NewObjectA(JNIEnv *, jclass, jmethodID, jvalue *)
jobject NewObjectV(JNIEnv *, jclass, jmethodID, va_list)
Estas funciones nos crearan un objeto de la clase indicada como podemos ver en el Listado 4. Los parámetros que pasamos son:
- Primero el entorno JNI.
- Segundo la clase que queremos crear.
- Tercero el ID del constructor que se habrá obtenido con GetMethodID donde el nombre del método tendrá que ser "", y el tipo de retorno "V" (void).
- Son los parámetros del constructor. La forma de pasar los parámetros, al igual que nos pasaba al invocar los métodos, dependerá de como nos convenga enviarlos utilizando para ellos la función que más se adapte a nuestras necesidades.
Si el objeto no se ha podido crear nos devolverá NULL y las excepciones que pueden saltar son las mismas que en el caso de la función AllocObject.
jclass GetObjectClass(JNIEnv *, jobject):
Nos permite obtener la clase del objeto pasado como segundo parámetro.
jboolean IsInstanceOf(JNIEnv *, jobject, jclass)
Con esta función podemos saber si un objeto que pasamos en el segundo parámetro es instancia de la clase pasada como tercer parámetro. El valor que devuelve esta función será JN_TRUE si el objeto es instancia de la clase y JNI_FALSE en caso contrario.
jboolean IsSameObject(JNIEnv *, jobject, jobject)
Nos permite saber si los dos objetos que pasamos como parámetro son iguales, devolviendo JNI_TRUE en el caso de que sean iguales y JNI_FALSE si son distintos.
jobject NewGlobalRef(JNIEnv *, jobject)
void DeleteGlobalRef(JNIEnv *, jobject)
Crea una referencia global del objeto que se pasa como segundo parámetro, si no se puede crear nos devolverá NULL. Las referencias globales han de ser borradas con la función DeleteGlobalRef se pasara como segundo parámetro un objeto que será la referencia global a borrar.
En la versión 1.2 aparece un tipo especial de referencia global "Weak Global Reference". Esta tipo de referencia se puede utilizar igual que las locales o globales, pero será recogida automáticamente por el recolector si esta solo referenciada a WEAK. Las funciones nuevas que aparecen para poder manipular estas referencias son: NewWeakGlobalRef y DeleteWeakGlobalRef.
void DeleteLocalRef(JNIEnv *, jobject)
Las referencias locales son sólo validas mientras estemos en el método que tiene estas referencias locales, una vez que salgamos de el serán borradas por el recolector de basura. Esto tiene un coste para el sistema que algunas veces nos puede interesar liberar memoria de referencias locales que ya no se van a utilizar. Para poder liberar una referencia local tenemos la función DeleteLocalRef que permite al programador borrar manualmente una referencia local.
En la versión 1.2 aparecen una serie de funciones que nos permite un manejo más flexible de la variables locales. Una de las funciones que tenemos nuevas es "jint EnsureLocalCapacity(JNIEnv *, jint) " que nos permitirá asegurar el mínimo numero de referencias locales que se indica en el segundo parámetro, si puede asegurar nos esta capacidad nos devolverá un cero, la maquina virtual nos asegura automáticamente 16 referencias locales. Podemos crear un nuevo espacio de referencias locales "jint PushLocalFrame(JNIEnv *, jint)" o liberar este espacio de referencias mediante "jobject PopLocalFrame(JNIEnv *, jobject)".
Al igual que pasaba con las referencias globales en la versión 1.2 nos proporcionan una nueva función para crear referencia locales que es "jobject NewLocalRef(JNIEnv *, jobject)".
jclass FindClass(JNIEnv *, const char *)
Esta función carga una definición de clase como se puede ver en el Listado 4 la clase la buscar en los caminos de la variable de entorno CLASSPATH incluidos los ficheros ZIP.
En el primer parámetro tendremos el entorno JNI y como segundo el nombre de la clase a cargar, si esta en un paquete tendremos que indicar el nombre del paquete separando los subpaquetes mediante "/". Si no hemos podido localizar la clase devolverá NULL y saltara la excepción NoClassDefFoundError. Otras excepciones que pueden producirse son:
- ClassFormatError: Los datos de la clase no tienen un formato correcto.
- OutOfMemoryError: No tenemos memoria en el sistema, cuando nos llega esta excepción puede ser un buen momento para liberar espacio de referencias globales y locales que ya no nos sirve.
En la versión 1.2 FindClass podremos utilizar un cargador de clase asociado al método nativo que nos permita no solo las clases que se localizan en la variable CLASSPATH.
Operaciones con String
Las cadenas de caracteres que nos llegan desde Java no pueden ser utilizadas directamente en C y deberán ser pasadas al formato de este lenguaje, a la inversa nos encontramos con el mismo problema y el formato de las cadenas de caracteres de C no puede ser utilizado directamente por Java, así que tendremos que transfórmarlo con las funciones que nos ofrece el interfaces JNI y que pasamos a explicar basándonos en ejemplo de del Listado 5.
#include "Ejemplo3.h"
#include
/*
* Class: Ejemplo3
* Method: metodo_C
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_Ejemplo3_metodo_1C(JNIEnv *env, jobject object, jstring envio)
{
jstring enviamos;
const unsigned char *cadenaUTF;
unsigned char código;
int cont,longitud;
// Preparamos el String a devolver.
enviamos = (*env)->NewStringUTF(env, "Se creo en C");
// Recogemos o que nos llega.
cadenaUTF = (*env)->GetStringUTFChars(env, envio, 0);
printf("Nos llego a Java: %s\n",cadenaUTF);
(*env)->ReleaseStringUTFChars(env, envio, cadenaUTF);
longitud = (*env)->GetStringUTFLength(env, envio);
for (cont=0; cont < longitud; cont++)
{
if (cadenaUTF[cont] < 0x80 )
{
printf("%c",cadenaUTF[cont]);
}
else
{
printf("(%c)",((cadenaUTF[cont++] & 0x1F) << 6) + (cadenaUTF[cont] &0x3F));
}
}
printf("%X, ",cadenaUTF[longitud]);
return enviamos;
}
Una cadena de caracteres de Java se puede convertir a formato C con una representación Unicode o UTF-8. El formato Unicode solo lo deberemos utilizar si nuestro sistema operativo utiliza este tipo de caracteres.
Un carácter UTF-8 puede ocupar 1, 2, o 3 bytes dependiendo del código de carácter a representa, el rango de caracteres va desde 1 hasta 65535, los caracteres que van desde 1 al 128 (80H) se representaran con un byte como se puede ver en (a) de la Figura B.

Los caracteres entre 128 (80H) y 2047 (07FFH) se representarán con dos bytes con el formato que vemos en (b) de la Figura B. En el Listado 5 vemos un ejemplo de descodificación de caracteres entre los rangos 1 y 2047.
Los caracteres comprendidos entre los rangos 2048 (0800H) y 65536 (FFFFH) se representan con tres bytes como se ve en ( c) de la Figura B.
jstring NewString(JNIEnv *, const char *)
jstring NewStringUTF(JNIEnv *, const char *)
Estas funciones construyen una cadena de caracteres con formato de Java y se les pasa como parámetros una cadena de caracteres Unicode o UTF-8 dependiendo de la función que utilicemos. Si no se ha podido construir la cadena de caracteres devolverán NULL y la excepción que puede saltar es OutOfMemoryError que nos estará indicando que el sistema no tiene memoria para crear esta cadena.
jsize GetStringLength(JNIEnv *, jstring)
jsizeStringUTFLength(JNIEnv *, jstring)
Con estas funciones podemos ver los que nos ocupa una cadena de caracteres de Java en formato Unicode o UTF-8 respectivamente.
const char * GetStringChars(JNIEnv *, jstring, jboolean *)
const char * GetStringUTFChars(JNIEnv *, jstring, jboolean *)
Con estas funciones creamos un puntero a una cadena de caracteres Unicode o UTF-8 respectivamente con el contenido de la cadena Java que se le pasa como segundo parámetro.
El tercer parámetro es un puntero a jboolean que nos permite ver si esta copia se ha realizado con éxito o no, si la copia se realizo con éxito se pondrá a JNI_TRUE en caso contrario será puesto a JNI_FALSE.
Una vez que no necesitemos esta cadena de caracteres deberemos liberarla de memoria con las funciones ReleaseStringChars y ReleaseStringUTFChars respectivamente, los parámetros que tendremos que pasarles son:
- Primero el puntero del entorno JNI.
- Segundo la cadena de caracteres Java que sirvió de copia en las funciones GetStringChars y GetStringUTFChars.
- Tercero el puntero a carácter que se tiene que liberar y que es el que nos fue devuelto por una de las funciones GetStringChars o GetStringUTFChars.
La versión 1.2 nos proporciona dos nuevas funciones para convertir subconjunto de cadenas de caracteres Java a Unicode o UTF, estas nuevas funciones son:
void GetStringRegion(JNIEnv *, jstring, jsize, jsize, char *)
void GetStringUTFRegion(JNIEnv *, jstring, jsize, jsize, char *)
En el tercer y cuarto parámetro deberemos poner el principio y la longitud de la cadena y el resultado lo tendremos en el quinto parámetro. Si los valores de inicio y longitud no son correctos con respecto a la longitud de la cadena de caracteres Java que se pasa en el segundo parámetro se producirá la excepción StringIndexOutOfBoundException.
Operaciones con Arrays
En el Listado 6 podemos ver como accedemos a un array de Java y como podemos desde C crear un array en la clase Java. Desde C también podemos obtener la longitud de los arrays de Java, esto lo realizamos con la siguiente función "jsize GetArrayLength(JNIEnv *, jarray)".
#include "Ejemplo4.h"
/*
* Class: Ejemplo4
* Method: metodo_C
* Signature: ([I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_Ejemplo4_metodo_1C(JNIEnv *env, jobject object, jintArray origen)
{
int longitud;
int cont;
jint *datos;
jfloatArray resultado;
jfloat *nuevosdatos;
jfieldID ID_Campo;
jclass LaClase;
longitud = (*env)->GetArrayLength(env, origen);
/* Los datos que han llegado son */
datos = (*env)->GetIntArrayElements(env, origen, 0);
for (cont = 0; cont < longitud ; cont++)
{
printf("%d, ", datos[cont]);
}
printf("\n");
// Gabamos resultados en array de la clase.
resultado = (*env)->NewFloatArray(env, longitud*2);
nuevosdatos = (jfloat *) calloc(sizeof(jfloat), longitud*2);
for (cont=0; cont < longitud; cont ++)
{
nuevosdatos[cont] = datos[cont] / 2.0;
nuevosdatos[longitud + cont] = datos[cont] * 2;
}
(*env)->SetFloatArrayRegion(env, resultado, 0, longitud*2,nuevosdatos);
LaClase = (*env)->GetObjectClass(env, object);
ID_Campo = (*env)->GetFieldID(env, LaClase, "destino", "[F");
(*env)->SetObjectField(env, object, ID_Campo, resultado);
(*env)->ReleaseIntArrayElements(env, origen, datos, 0);
}
Otras funciones que nos permiten manejar arrays son las siguientes:
jarray NewObjectArray(JNIEnv *, jsize, jclass, jobject)
Nos permite crear un array de objetos, para crear un array multidimensional tenemos que pensar que es una array de arrays, según veremos no tenemos una función que nos permita crear un arrays de arrays pero si tomamos al array como un objeto. Esta función nos podrá servir para realizar arrays multidimensionales.
Esta función nos retornara NULL si no puede crear el array y si la causa es por carecer de memoria se producirá la excepción OutOfMemoryError.
Los parámetros que deberemos pasar son:
- El primer parámetro se pasa el entorno JNI.
- El segundo parámetro indica las longitud del array.
- El tercer parámetro es la clase del elemento del array.
- El cuarto será el elemento con el que inicializaremos los elementos del array.
jobject GetObjectArrayElemnt(JNIEnv *, jobjectArray, jsize)
Nos permite recoger el objeto que se encuentra en la posición que le indicamos como tercer parámetro. Si el parámetro tercero indica un índice no valido del array se producirá la excepción ArrayIndexOutOfBoundsException.
void SetObjectArrayElement(JNIEnv *, jobjectArray, jsize, jobject)
Nos permite cambiar el valor de un elemento del array, los parámetros son:
- Primero el entorno JNI.
- Segundo el array que deseamos modificar.
- Tercero el del elemento del array anterior que deseamos actualizar su valor.
- Cuarto el valor que pasamos.
Si el elemento indicado no es un elemento valido se producirá la excepción ArrayIndexOutOfBoundsException, si la clase que queremos introducir como nuevo valor no pertenece al tipo del array se producirá la excepción ArrayStoreException.
NewArray(JNIEnv *, jsize)
Nos permite crear un array de un tipo determinado cuya tamaño se le indica en el segundo parámetro. Tendremos una función para cada tipo básico. Si el array no puede ser construido nos devolverá NULL. Podemos ver un ejemplo de su uso en el Listado 6.
Los tipos de array básicos se pueden ver en la siguiente tabla.
Tipos de array
Tipo Nativo TipoArray
jboolean jbooleanArray
jbyte jbyteArray
jchar jcharArray
jshort jshortArray
jint jintArray
jlong jlongArray
jfloat jfloatArray
jdouble jdoubleArray
*GetArrayElements(JNIENV *, , jboolean *)
Desde C no podemos acceder directamente a los elementos de un array Java. Lo primero que tenemos que hacer es convertirlo a tipo C, esta función es la encargada de realizar esta conversión. El segundo parámetro es el array Java a convertir a formato C (un puntero al tipo del array) y el tercero nos indicara si se ha podido realizar la conversión o no, en caso de que la copia se haya realizado retornara JNI_TRUE en caso contrario JNI_FALSE.
Después de utilizar el array C deberemos borrarlos para liberar memoria y esto lo realizaremos con la función:
void ReleaseArrayElements(JNIEnv *, , *, jint)
Esta función libera el puntero al array en formato nativo, el segundo parámetro es el array Java y el tercero el array en formato nativo (puntero al tipo del array). El cuarto parámetro nos indicara la forma de borrado que podrá ser:
- 0 Se elimina la copia y se libera el puntero, este es el método que más utilizaremos.
- JNI_COMMIT la copia será borrada pero no liberamos el puntero por lo tanto no se libera la memoria ocupada por el mismo.
- JNI_ABORT liberamos el puntero pero no eliminamos la copia de los elementos.
Otro conjunto de funciones que nos permitirá manipular el array en regiones será:
void GetArrayRegion(JNIEnv *, , jsize, jsize, *)
Nos permite coger una porción de array Java pasado como segundo parámetro y especificada por un inicia (parámetro tercero) y de longitud (parámetro cuarto), a un array de tipo nativo que se encuentra en el quinto parámetro. Si la región indicada no es valida producirá una excepción ArrayIndexOutOfBoundsExcepction.
Void SetArrayRegion(JNIEnv *, , jsize, jsize, *)
Realiza una copia de un array nativo a un array Java, tendremos que especificar que porción de array queremos copiar con los parámetros tercero y cuarto. Esta función no produce ninguna excepción ya que C no es capaz de saber la longitud de sus arrays.
Para pasar estos datos a una array de la clase Java tendremos que obtener la variable Java obteniendo el ID y pasarlo como si de un objeto se tratara, un ejemplo lo podemos ver en el Listado 6.
Excepciones
Muchas de las funciones de JNI pueden producir excepciones, estas excepciones se pueden capturar desde Java. Además de poder lanzar excepciones podremos capturarlas desde JNI incluso es psoible producir errores graves que no permitan una recuperación y por lo tanto un retorno a la maquina virtual.
Si alguna vez tenemos un error tan grave que no podemos ni siquiera volver a nuestra clase Java, podemos lanzar un error fatal con un texto explicativo de lo que ha pasado y salir del programa inmediatamente. Para esto tenemos la función "void FatalError(JNIEnv *, const char *)" en la que como segundo parámetro tenemos una descripción del error. Tenemos una función que nos permite la depuración informando de las excepciones por la salida estándar de error (stderr), esta función es "void exceptionDescribe(JNIEnv *)".
Si queremos saber desde nuestro método nativo si una excepción a saltado o no y de esta forma intentar solucionar el problema desde nuestro método nativo utilizaremos la función del JNI "jthrowable ExceptionOcurred(JNIEnv *)". Después de tratar la excepción se podrá limpiar para que no salte al retornar a Java mediante la función "void ExceptionClear(JNIEnv *)". Las dos funciones siguientes nos permitirán lanzar excepciones:
jint Throw(JNIEnv *, jthrowable)
jint ThrowNew(JNIEnv *, jclass, const char *)
Si nos devuelven 0 es que se habremos podido lanzar la excepción. La segunda función nos permite construir una excepción con un mensaje que nos explique el motivo del error.
Interfaces con la maquina virtual
Una de las mejoras que tenemos con el JNI es que podemos crear aplicaciones nativas y estas utilizar clases realizadas en Java. El JNI nos permite cargar, inicializar e invocar a la maquina virtual de Java, para de esta forma desde nuestro programa escrito en lenguaje nativo poder crear clases Java y ejecutar sus métodos.
Cuando creamos una maquina virtual de Java en nuestro programa nativo, obtenemos un puntero al interfaces de la maquina virtual que nos permitirá destruirla y a su vez tratar con el thread de la maquina. Si queremos obtener este entorno en un método nativo de un programa Java para así tener un mayor control sobre la maquina virtual podemos ejecutar la función "jint GetJavaVM(JNIEnv *, JavaVM **)" que nos devuelve un puntero al interfaces de la maquina virtual Java, correspondiente al thread de esta maquina virtual.
Cuando creemos una aplicación nativa que tiene que utilizar clases de Java, se tendrán que crear una maquina virtual en esta aplicación. Para poder realizar esto necesitaremos que nuestro programa se enlace con la librería de Java. Algunos ejemplo de línea de compilación son:
Para Windows
cl -I\include -I\include\win32 -MT \
-link \lib\javai.lib
Para Linux
cc -I/include -I/include/genunix \
-L/lib/ixxx/green_threads -ljava
Para Sun
cc -I/include -I/include/solaris \
-L/lib/sparc/green_threads -ljava
será el directorio donde se tenga instalado el entorno Java.
es el programa C que creara una maquina virtual Java.

A continuación explicaremos como ejecutar un método de una clase Java desde un programa C. En el Listado 7 se puede ver un ejemplo de esto.
/********************************************************************
* Ejemplo5.c
*
* Descripcion: Este Ejemplo nos permite crear Maquinas virtuales.
*
* Autor: Fco. Javier Rodriguez Navarro
*
*********************************************************************/
#include
int main()
{
JDK1_1InitArgs datos;
JNIEnv *env;
JavaVM *mvj;
jclass LaClase;
jobject El_Objeto;
jmethodID ID_Metodo;
jint Resultado;
datos.version = 0x00010001; /* Indicamos la version */
JNI_GetDefaultJavaVMInitArgs(&datos);
Resultado = JNI_CreateJavaVM(&mvj, &env, &datos);
if (Resultado < 0)
{
printf("Error al crear la maquina virtual");
}
else
{
/* Localizamos la clase y ejecutamos el método */
LaClase = (*env)->FindClass(env, "Ejemplo5");
ID_Metodo = (*env)->GetStaticMethodID(env, LaClase, "inicio", "()V");
(*env)->CallStaticVoidMethod(env, LaClase, ID_Metodo);
(*mvj)->DestroyJavaVM(mvj);
printf("Esto es otra vez C\n");
}
}
Lo primero que hacemos es coger los datos de inicialización de la maquina virtual, lo realizamos con la función:
jint JNI_GetDefaultJavaVMInitArgs(void *)
Esta función recoge la configuración por defecto de la maquina virtual, dependiendo de la maquina virtual podemos tener diferentes tipos de datos. Para poder tener una inicialización siempre estándar tenemos el primer campo de la estructura que será el de la versión.
El campo de versión se rellenara con la versión del JDK en el caso de la 1.1 el contenido deberá de ser 0x00010001 y la estructura de la versión 1.1 es JDK1_1InitArgs con los siguientes campos:
- jint versión: Nos indica la versión de la maquina virtual.
- char **properties: Es un array de String con el formato "propiedad=valor".
- jint checkSource: Indica si chequea el código de una clase.
- jint nativeStackSize: Tamaño de la pila para los thread de la maquina virtual.
- jint javaStackSize: Tamaño de la pila Java, la pila de Java es el equivalente a un Stack de cualquier otro lenguaje donde se guardarán las variables locales y resultados temporales.
- jint minHeapSize: Tamaño inicial de Heap, esta es la parte de memoria donde se asignan los objetos de nueva creación, el tamaño del Heap puede aumentar hasta el valor indicado por maxHeapSize.
- jint verifyMode: Esta variable nos indica cuando verifica los byte codes de Java, los valores que puede tener es "0" nunca se verifican, "1" Solo los códigos remotos y "2" siempre.
- const char *classpath: Los directorios donde podemos localizar las clases.
- jint (*) (FILE *, const char *, va_list): Es un puntero a la función que imprime los valores de la maquina virtual. Podremos cambiarlos para que se guarden en un fichero de log.
- void (*)(jint ): Puntero a la función de salida de la maquina virtual.
- void (*)(): Puntero a la función que aborta la maquina virtual.
- jint enableClassGC: Indica si esta habilitado la clase recolectora de basura.
- jint enableVerboseGC: Permitirá que los mensajes del recolector de basura se impriman.
Una vez que tenemos los datos de la maquina virtual podemos crear una maquina virtual con la función:
jint JNI_CreateJavaVM(JavaVM **, JNIEnv **, void *)
Esta función nos permite crear una maquina virtual de Java, una vez que tenemos esta maquina virtual podremos cargar las clases y métodos que queramos para trabajar desde nuestro programa C con clases de Java. Esta función nos devolverá 0 en caso de que no hayan existidos problemas o un número negativo en caso contrario. Los parámetros de esta función son los siguientes:
- Primero es un puntero a la maquina virtual y nos permitirá descargar la maquina virtual una vez que hayamos terminado con nuestras clases de Java. Utilizaremos la función "jint DestroyVM(JavaVM *)", si no ha podido descargar la maquina virtual nos devolverá un número negativo.
Tenemos una función que nos permitirá todas las maquinas virtuales que han sido creadas "jint JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *)" el tercer parámetro nos indicara la maquinas virtuales que han sido creadas, y el primero será un array con los punteros de la maquinas virtuales creadas. En la versión de JDK 1.1 no se permite crear más de una maquina virtual, por lo que esta función nos puede parecer fuera de lugar, de todas formas con ella no podremos saber si ya se creo una maquina virtual y cual es su puntero para así poder actuar con ella.
Otra función especifica de la maquina virtual es "jint AttachCurrentThread(JavaVM *, JNIEnv **, void *)" que nos permitirá recoger el puntero al JNI de la maquina virtual.
- Segundo parámetro es el puntero al JNI que nos permitirá crear y ejecutar métodos de clase, así como las otras funciones que se han visto durante todo este artículo.
- Tercero el entorno de la maquina virtual que se ha podido cambiar antes de llamar a la creación de la maquina.
Fco. Javier Rodriguez Navarro trabaja como responsable de desarrollos en Tms con el objetivo de crear sistemas abiertos y escalables, de ahí la afición por Java.
Cada vez que tiene un rato libre se lanza a viajar y pasear para conocer nuevas gentes, paisajes y costumbres.
Para cualquier duda o tirón de orejas, e-mail a: frodrigu_ARROBA_idecnet.com
|
|