ポートを開いたり、画面にグラフィックを描画したりするためのバイトコードはありません。これらのタスクを実行するクラスがあります。これらのクラスにはネイティブメソッドがあります。これらはC(またはネイティブライブラリにコンパイルできるその他の言語)で記述されたメソッドであり、ネイティブであるため、オペレーティングシステムのネットワークおよびgraphcisライブラリにアクセスしてこれらのタスクを実行できます。したがって、これらのクラスをインスタンス化し、それらのメソッドのいくつかを呼び出します。Javaバイトコードは、クラスをインスタンス化し、オブジェクトのメソッドを呼び出すためのバイトコードを提供します。JVMは、これがネイティブメソッドであることを認識すると、このメソッドに属するネイティブコードを呼び出し、このネイティブコードは、CまたはC++プログラムが実行できるほとんどすべてのことを実行できます。
したがって、たとえば、System.out.println
Javaで呼び出すと、おおよそ次のようになります。
System
静的変数を持つクラスですout
。この変数は、メソッドが実行されるjava.io.PrintStream
前にJVMによってすでに作成されているタイプのオブジェクトを指します。main
変数は次のout
ように初期化されます。
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
そして、メソッドsetOut0は次のように定義されます。
private static native void setOut0(PrintStream out);
つまり、setOut0は、Cで記述されたネイティブメソッド、またはコンパイルしてネイティブライブラリにリンクできるその他の言語ですが、少なくともJavaとこのライブラリ間のインターフェイスは通常Cで記述されています。
Javaは、ロードされたすべてのライブラリで、名前が付いたシンボル(この場合、シンボルは関数名を意味します)を検索します。Java_java_lang_System_setOut0
これJava_ClassName_MethodName
を呼び出します。このメソッドで見つけたサンプルCコードは次のようになりました。
JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
jfieldID fid =
(*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
if (fid == 0)
return;
(*env)->SetStaticObjectField(env,cla,fid,stream);
}
しかし、これは本当の魔法ではなく、本当の魔法は他の場所で起こります。PrintStream.write()
を呼び出しますBufferedWriter.write()
。このメソッドは再び呼び出しOutputStreamWriter.write()
(直接ではありませんが、遅かれ早かれそこで終了します)、このメソッドはを呼び出しますStreamEncoder.write()
。うわー、その呼び出しを追跡するのは難しくなっています。StreamEncoder
呼び出しBufferedOutputStream.write()
、これが呼び出し、これがFileOutputStream.write()
呼び出しFileOutputStream.writeBytes()
、そして最後に、私たちはそこにいます!ネイティブメソッドFileOutputStream.writeBytes()
です。次のようになります。
JNIEXPORT void JNICALL
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
jobject this, jbyteArray bytes, jint off, jint len) {
writeBytes(env, this, bytes, off, len, fos_fd);
}
したがって、writeBytesという名前の関数を呼び出し、この関数はオペレーティングシステムによって異なります。たとえば、これがWindows、Linux、OSXのいずれであるかによって異なります。UNIX/Linuxシステムでは、この関数は別の関数(など)を呼び出す場合がありますが、どこかにあります。 CFILE *
ストリームまたはファイル記述子( Cの場合のみ)に何かを書き込むための単純なC関数呼び出しint
。したがって、またはに書き込む呼び出し、またはにprintf()/fprintf()
書き込むputs()/fputs()
呼び出しである可能性があります。Windowsでは、通常はへの呼び出しですが、(C標準関数であり、すべてのプラットフォームでサポートされている必要があります)の場合もあります。stdout
write()
STDOUT_FILENO
WriteFile()
printf()/fprintf()