Java Native Interface (JNI)

Exemplinho básico.

Em Java:

Arquivo: OlaJNI.java
public class OlaJNI {

    // carga da biblioteca
    static {
        System.loadLibrary( "ola" ); // ola.dll
    }

    // método nativo
    private native void olaMundo();

    public static void main( String[] args ) {
        new OlaJNI().olaMundo();
    }

}

Compile assim:
javac -h . OlaJNI.java

Vai gerar o header no mesmo diretório (OlaJNI.h).

Em C:

Arquivo: OlaJNI.c
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>

#include "OlaJNI.h"

JNIEXPORT void JNICALL Java_OlaJNI_olaMundo( JNIEnv *env, jobject obj ) {
    printf( "ola mundo (do programa em C)!\n" );
}

No Windows

Compile a DLL assim (estou usando Windows e meu GCC é do MinGW), mudando para o caminho do JDK do seu ambiente:
gcc -shared -o ola.dll -I"C:\Program Files\Java\jdk-25\include" -I"C:\Program Files\Java\jdk-25\include\win32" OlaJNI.c

Vai gerar uma DLL com o nome ola (ola.dll) que vai ser carregada pela classe quando for executada (veja o código Java).

Agora você executa sua classe Java, que vai chamar o código nativo que está na DLL:
java --enable-native-access=ALL-UNNAMED OlaJNI -Djava.library.path=.

No Linux

No Linux o que vai mudar é a geração da lib, algo como
gcc -shared -fpic -o libola.so -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux OlaJNI.c

Onde JAVA_HOME é o caminho para a instalação do seu JDK.