Javaに対しては、優れた技術だと評価する人々がいる一方で、批判も少なくない。Javaは、ソフトウェアに関する複雑さを管理する問題に対して、革新的な方法を提供するという目標のもとで、開発された。多くの人々は、Java技術は、この期待に対して満足できる答えを提供したと評価している。しかしJavaにも欠点が無いわけではないし、どのようなプログラミング作法にも適応しているわけではない。また、どのような環境や要件にも普遍的に適応しているわけではない。
目次 |
Javaプログラムを実行する際、すべてのサードパーティのライブラリはクラスパスに存在する必要がある。これは移植性の障壁となる。なぜならばクラスパスの文法はプラットフォームに依存するからである(Windowsベースのシステムはサブディレクトリを区切るためにバックスラッシュ "\" を使用し、パス区切りにセミコロン ";" を使用している。[1] だが一方、他の多くのプラットフォームではサブディレクトリ区切りにスラッシュ "/" を、パス区切りにコロン ":" が使われる)。[2]
各々の.jarや.zipアーカイブは、クラスパスにおいて明示的に名前がつけられる必要がある。この抜け道として、アスタリスク(*)で終わるクラスパスを指定することで、そのディレクトリにある.jarや.JARで終わるすべてのファイル名にマッチさせることができる。しかしながら、.zipや.classファイルのようなものはマッチしない。[1][2]
これらのクラスパス問題は環境変数CLASSPATHを使用せず、サン・マイクロシステムズが推奨する-classpathオプションを使用することで解決する。開発時は、-classpathオプションは、バッチファイルやmakeや Apache Ant や統合開発環境を使うことで利便な方法で指定することができる。Javaアプリケーションを利用したい実行エンドユーザに対しては、開発者が実行時には、マニフェストファイルにクラスパスを記述するか、FatJarやOneJar[3]という、複数のJarファイルを一つにまとめるツールを使うことで解決できる。
SunのJavaの財産的価値はフリーソフトウェアコミュニティで議論を呼んだ。なぜならばサン・マイクロシステムズ (Sun) のJavaの実装はフリーソフトウェアではなかったため、フリーソフトウェアや主にDebianや$100ラップトップ、Fedora Core のようなGPL互換ライセンスが要求されるプロジェクトにはSunのJavaを含めることはできなかった。 [4][5]
サン・マイクロシステムズ (Sun) は JavaOne 2006 で、Javaはオープンソースソフトウェアになるだろうと公表した。この声明はSun Softwareの上級副社長 Rich Green によって公表された。「オープンソースソフトウェアにするか否かという問題はない。どのような方法でオープンソースにするかが問題である。つまり我々はこれを実行する。」 [6]
2006年7月には、Sunの最高技術責任者 Robert Brewin はJavaは2007年6月に部分的にオープンソースになるが、全体のプラットフォームが完全なオープンソースになるには時間がかかるだろうとコメントしている。[7]
2006年11月13日はSunは標準版Java実行環境は2007年3月にGPLの下でリリースされる予定であることを公表した。[8] Java実行環境のソースコードはGPLの下で利用可能になる予定である。リチャード・ストールマンによると、これはJavaの罠の終焉を意味するとのことである。マーク・シャトルワースはJavaのオープンソース化についての最初のSunによる発表を「フリーソフトウェアコミュニティにとっての確かなマイルストーン」と評価した。[8][9]
Javaのライセンスの問題は、徐々に解決されつつある。時間が解決していると言える面もあるといえる。今後も、新たなライセンスの問題に直面しそうであれば、Java Community Process に提案するよう働きかけてみるという手段もある。フリーソフトウェア財団や Eclipse Foundation など他のオープンソースコミュニティの助けを借りることで問題を解決に向けて進めることができる可能性がある。
Javaはメモリを管理するが、他のリソース(JDBCデータベース接続など)は管理しない。これらはC++でメモリを解放する必要があったのと同様、プログラマによって解放されなければならない。
Javaはガベージコレクションによるメモリ管理機能を備える。これによって、完全ではないもののはプログラマがメモリリークを起こすようなプログラムを作ってしまう危険性を減らした。Javaでは、オブジェクトは常にヒープ領域に(JITコンパイラによって最適化されてスタックやレジスタに割り当てられない限り)確保される。ローカル変数はスタックやレジスタに確保される。C++ではオブジェクトをヒープ領域に割り当てるかスタック領域に割り当てるかをプログラマが選択することができたが、Javaではそれが不可能になっている。
オブジェクトはガベージコレクションによって管理されるが、Javaはプログラマに、ガベージコレクションがいつ起こるかを保証しない。たとえSystem.gc()を用いてもプログラマはガベージコレクションを阻止することも特定のオブジェクトを解放することもできない。これはプログラミングを簡易にしメモリリークの可能性を軽減するが、より効果的なメモリ処理を行うための柔軟性が犠牲になっている。Cやアセンブリのような低水準言語はこの柔軟性を提供する。
C++などで書かれた多くのプログラムはメモリリークの犠牲になりがちだが、問題はメモリリークだけではない。ファイルハンドルやデータベース、ネットワーク接続のような他のリソースのリークは特に例外が投げられたときに常に起こりうる。 C++ではRAIIと呼ばれるイディオムによっていずれの問題も克服することができるが、Javaプログラマは忘れずにfinally節でリソースを解放する必要があり、Javaが何を解放するかということとプログラマは何を解放しなければならないのかということをきちんと理解する必要がある。
この問題は、メモリを増設する、最大ヒープメモリサイズを拡大するなどで解決できるケースもある。ウィークリファレンス(SoftReference)を用いることで多少の解決策になることはある。だが、JREやJDKのバージョンが特定の古いものであることが要因となっていることもある。最新版のJREやJDKで実行、開発すれば問題発生率を下げることもできる。
finally節は、次世代のJava ( Java SE 7 ) で利用可能になる可能性があるクロージャで解決できることもある。これらは、場合によっては、ライブラリやフレームワークによって解決できることもある。ファイル入出力の場合、Jakarta Commons IO、データベース接続のときは Jakarta Commons DBUtils、Hibernateや Apache Cayenne などのオブジェクトリレーショナルマッピングフレームワークを用いることでfinally節のことをあまり多く気にしなくても良いようにする手段がある。
Javaの設計者らは、現在他の言語にあるいくつかの機能(多重継承、演算子多重定義、タプルなど)を実装しないことを決めた。
総称型(ジェネリクス)が Java 5.0 に導入されたとき、すでにJavaにはクラスの大規模な枠組みがあった(それらの多くはすでに非推奨となっていた)、そして後方互換性を保つため、総称型の実装方法として既存のクラスを維持することを可能にする(コンパイル時の)型消去(型削除、type erasure)が選ばれた。これは、他の言語と比べると、総称型の導入によって提供される機能を限定してしまう結果になった。[10][11]
Javaのプリミティブ型はオブジェクトではない。プリミティブ型は値への参照ではなく値そのものをスタック領域に保持している。これはパフォーマンスの理由により行われた。このためJavaは純粋なオブジェクト指向言語と見なされてない。またこのことによりリフレクションがより複雑になっている。しかし、Java 5.0 は、コンパイル中に要求された場合にプリミティブ型を対応するプリミティブラッパークラスのオブジェクトに自動変換する機能(オートボクシング)をサポートしている。オートボクシングではNullPointerExceptionが投げられる可能性がある。オートボクシングは暗黙のうちに起こる(キャストやメソッド呼び出しを伴わない)ため、このNullPointerExceptionという非チェック例外はJavaプログラムのコードに目を通しただけでは明確にはならない恐れがある。
Javaはメソッドを非virtualにする手段を提供しない(オーバーライドを禁止するためにfinal修飾子を使用することで"sealed"(シールド、封印、密閉)にすることはできる)。これは派生クラスに、同じ名前の関係の無いメソッドを定義させる方法がないことを意味する。これは基底クラスが別の人間によって設計されるときに問題となることがあり、また、新しいバージョンが、派生クラスで同じ名前のメソッドがすでに存在するときに、同じ名前とシグネチャのメソッドを導入することで問題となることがある。これは、どちらのクラスの設計者の意図にも反して、派生クラスのメソッドが基底クラスのメソッドを暗黙のうちにオーバーライドするであろうことを意味する。これらのバージョン問題にある程度適合するために Java 5.0 は アノテーションを導入しているが、後方互換性を保つには、それをデフォルトでは強制できない。@Override
Javaは主に単一パラダイム言語である。Java 5.0 に登場した static imports の追加はこれまでのJavaよりも手続きパラダイムによりよく順応する。
JavaではC++でオプションとされていた例外処理の仕様を取り込んだが、この際チェック対象の例外に対応するthrow文を必須とした。例外のチェックは小規模なシステムにとっては役立つが、大規模なシステムについても有益であるかどうかについては統一的な見解には至っていない。特に上位のコードでは下位のコードから発生するエラーを意識したくない(例:NamingException(名前解決例外))。名前クラスの作成者は名前解決例外をチェック対象の例外として上位コードに対応を強制するか、コンパイル時のチェックを使わずに下位のコードからの例外を連鎖的に通知するかを選択する必要がある。
匿名内部クラスは基本的なクロージャを提供するが、これは完全ではなく、クラスのメンバーとしてかFinal型で宣言する必要がある。これは、変数のスコープを抜けたときに削除できるような、様々な寿命に対応したスタックモデルをJVM実装者が選択出来るようにするためである。例えば"Runnable"をクロージャとして使用した場合、単純にコードを大括弧で括って書くことが出来ないため、コードを入れるために"run"メソッドを宣言する必要がある。
Javaの浮動小数点数演算は主にIEEE 754(二進化浮動小数点数演算標準)をベースとしているが、例えばIEEE 754に必須とされる例外フラグや方向指定の丸めなどの、 いくつかの機能については"strictfp"修飾子を指定した場合でもサポートされない。Javaの仕様として知られているものはJava自体の問題ではないが、浮動小数点数演算を行う上で避けて通れない問題である。 [12][13]
Swingプラットフォームを使って、Javaで書かれたアプリケーションのグラフィカルユーザインタフェース (GUI) のルックアンドフィール(Look and feel、直訳すると「外観と操作感」)は、大抵ネイティブのアプリケーションのものと異なる。 プログラマはネイティブのウィジェットを表示するAWT(ネイティブであるためOSのプラットフォームと同じ見た目を提供する)を使うことを選択することができる。しかしAWTツールキットは、高度なウィジェットのラッピングを必要としかつ様々なサポートされたプラットフォームで移植性を犠牲にしない高度なGUIプログラミングには、向いていない。そして、SwingとAWTにはとりわけ高水準ウィジェットにおいてAPIが大きく異なる。
Swingツールキットは全てJavaで実装されている。Swingツールキットはネイティブアプリケーションとは異なるルックアンドフィールを持つという問題がある。一方でSwingツールキットのウィジェットはネイティブなツールキットの機能に限定されないという利点がある。この利点は全てのプラットフォームで利用できることが保証される最も基本的な描画機構を使ってウィジェットを再実装していることによる。不幸にも、(2006年8月の時点で)Java実行環境 (JRE) のデフォルトのインストールでは、デフォルトの埋め込みMetal(メタル) ルックアンドフィールの代わりに、システムの「ネイティブ」なルックアンドフィールを使わなかった。プログラマがネイティブなルックアンドフィールを設定するようにしないならば、ユーザーは外観がネイティブアプリケーションのそれとは非常に異なるアプリケーションに遭遇するだろう。 Mac OS X の配布元に限って含まれる アップルコンピュータ自身のJava実行環境の最適化バージョンは、デフォルトで「デフォルト」をセットし、"Aqua" のルックアンドフィールを実装し、Macintosh上のSwingアプリケーションは、ネイティブなソフトウェアに似た外観を提供している。Mac OS X の環境でさえ、プログラマはそのアプリケーションがAquaのように見えることを確実とするために、若干の余分の仕事をまだしなければならない(例えば、それらはメニューバーが Microsoft Windows のようにアプリケーションウィンドウ内部ではなくOS Xメニューバー内部に表示させるために、システムプロパティをセットする必要がある)。
この問題は、開発者がJavaのバージョンを Java SE 6 以降にアップグレードすることで解決する。Java SE 6 になってからJavaのデスクトップまわり、グラフィカルユーザインタフェース環境は一新され、開発効率は高まっているため、以前のバージョンよりも開発の手間はかからなくなっている。
Javaプログラムのパフォーマンスに関して一般論を述べることは不可能である。なぜなら、実行時の性能は、言語自身が持つ固有の特性よりも、JavaコンパイラやJava仮想マシンの品質に非常に大きく影響されるからである。Javaバイトコードの実行方法は、仮想マシンによって実行時に翻訳して実行する方法と、ロード時もしくは実行時に機械語にコンパイルしてハードウェアによって直接的に実行する方法がある。前者の翻訳実行する方法は機械語の実行よりも遅く、後者のロード時もしくは実行時にコンパイルして実行する方法は、最初のコンパイル時にパフォーマンスが犠牲になる。
この問題は現在に於いては、さほど大きな障壁にはなっていない。なぜならば、コンピュータの性能はムーアの法則によって刻一刻と進化しているからである。そしてJavaもバージョンアップするにつれて最適化技術を進歩させてきた。これによって、Javaの初回起動時のオーバーヘッドは誤差の範囲内になりつつある。C++ではJavaよりも高速にできるよう手動で最適化ができるが、C++でJavaの実行時最適化技術を真似することは困難である。
Javaだけに限ったことではないが、パフォーマンス上の障壁となる言語仕様がいくつか存在する。それは例えば配列境界チェックや実行時型チェック、仮想関数などである(コンパイラ時の最適化で解消できるものもある)。また、言語仕様の欠落がパフォーマンスに影響する場合もある。例えば、Javaは構造体の配列や真の多次元配列を持たず、オブジェクト(や配列)への参照の配列が定義できるのみである。また、Javaでは関数から複数の値を返すためにはオブジェクトを使用する以外の方法がない。これらによってJavaのプログラムコードは、他の言語で書かれたコードよりも頻繁にヒープを使用しなければならなくなっている。
不要オブジェクトの自動回収を行うガベージコレクションは明示的なメモリ開放に比べそのオーバーヘッドは大きいが、ガベージコレクタの実装やアプリケーションでのオブジェクトの利用状況によってその影響はまちまちに変化する。多くのJava仮想マシンは世代別ガベージコレクションの採用によって動的メモリ管理を高速化しているため、多くのアプリケーションでは高い性能を示している。
ジャストインタイムコンパイル(JITコンパイル)とネイティブコンパイルの性能差はほとんど無いとされるが、よく議論の種にもなる。JITコンパイルには時間が掛かるため、短時間で終了するアプリケーションや巨大なプログラムでは問題となる。しかし、一旦ネイティブコードに変換すれば数値計算においてもネイティブコンパイルと同等の性能を示す。
Javaはメソッド呼び出しの明示的なインライン化をサポートしないものの、多くのJITコンパイラではバイトコード読み込み時にインライン展開を行い、また実行時のプロファイリングを利用してその効率をより高めている。HotSpot仮想マシンが採用している動的再コンパイルでは、実行時でしか取得できない情報を利用することで、多くのプログラミング言語が採用する静的コンパイル方式を超える性能を得る場合もある。
Javaはセキュリティと移植性を重視して設計されたので、Javaではマシンアーキテクチャとアドレス空間への直接アクセスをサポートしていない。スキャナ(デジタルカメラ、オーディオレコーダ、ビデオキャプチャないし実質的に直接メモリ空間の制御(一般的に、ドライバインストールを必要とするハードウェアやそれらの部品)を必要とする特定の一部のハードウェアを動かすことはJavaでは容易に実現できないことを意味する。この問題の実例はJavaのバージョン1.0で見られた。様々なプリンタドライバへのインタフェースコードがこの初期のJava実行環境に含まれず、プリンタへのアクセスが可能でなかった。
ハードウェアに直にアクセスするクライアントサイドまたはサーバーシステムは、ネイティブコードとJavaライブラリを橋渡しするJava Native Interface(JNI)を使って、C、C++およびアセンブリ言語とJavaを組み合わせる方法を取る。また、ハードウェアアクセスを担うネイティブコードとJavaとの通信をファイルやデータベース、共有メモリを介して行う方法もあるが、理想的なやり方ではない。
JNIを使うことで、プラットフォーム依存性、潜在的なデッドロック、メモリリーク、場合によっては性能の著しい劣化などの問題が発生しうる。また、2つの異なるコードベースをメンテナンスするために必要となるコードの複雑性は、言うまでもない。しかし、それは、例えば.NET Framework 共通言語ランタイムのように、他の仮想マシン言語と共通する事例である(Platform Invocation Servicesを参照)。
現在では今のところ、Javaはまだまだハードウェア開発、デバイスドライバ開発には適していない点が多い。この問題について、JavaでUSBドライバ開発ができる Java Communication API という技術がすでにある。他にも、Jini対応機器を端末に接続すると、サーバから自動的にJava製ドライバを端末にダウンロードしてその機器を使うことができる技術Jiniというものが考えられている(JiniはしばしばJNI (Java Native Interface) と誤読することがあるので要注意である)。
Javaは、Java仮想マシン (JVM) の上部で動くバイトコード言語である。異なるプラットフォームで実行できる能力と言語の互換性は、最終的にはJVM実装の安定性とJVMのバージョンに依存している。Javaは非常に様々なシステム上で動くとうたわれているが、最新のJVM (とJRE) はWindows、Linux、Solaris対応だけ活発に更新されている状況である。HP (Java for HP-UXなど)とIBM (for MVS、AIX、OS/400)は、それぞれのプラットフォームファミリー独自の実装を提供するが、必ずしも最新のサン・マイクロシステムズのリリースを反映していない。他のプラットフォームにおけるJVM実装も、たいてい今後も続くが、時々一般の実装例よりも何年か何ヶ月か遅れるので、互換性問題が生じる。具体的な例を示すと、Java 2 Platform Standard Edition 1.5(J2SE 1.5)をサン・マイクロシステムズがリリースしたのは2004年9月30日[14]だが、Mac OS X用J2SE 1.5をアップルコンピュータが公開したのは2005年3月[15]であり、5ヶ月余りの差が生じている。
「Write once, run anywhere」(WORA、一度書けばどこでも動く)という言葉があるとおり、Javaの目標の一つにプラットフォーム非依存があげられる。 JavaコンパイラはJava仮想マシン用の中間言語(バイトコード)を生成する。 コンパイルされたJavaのプログラムは、Java仮想マシンを実行環境として動作する。 この仮想マシンがハードウェア間の差異を吸収することで、プラットフォーム非依存を実現している。 ただし、現時点では一部にプラットフォーム依存の部分があり、完全なプラットフォーム非依存ではない。
また、マルチプラットフォームにするということは、一部のプラットフォームにしかない独自の機能はJavaから使えないことを意味する。 例えば Windows 用グラフィックス API、DirectX はJavaから直接呼び出すことは出来ない。 そのため、橋渡しをするための拡張APIが提供されている。
またJavaではバージョン間の下位互換性・上位互換性の問題が議論の対象になっている。 Javaではバージョン間の互換性をある程度の水準まで達成している。 しかし、バージョンの異なる実行環境の取り扱いには課題が残っている。 例えば J2SE 1.4 実行環境用に書かれたプログラムは、実行環境に J2SE 1.3 を想定すると明示的に指定してコンパイルしなければ J2SE 1.3 実行環境では動かず、利用するライブラリが J2SE 1.4 以降から追加されたものである場合には J2SE 1.3 実行環境での実行を諦めなければならない。 J2SE 1.3 からの上位互換性は、J2SE 5.0 まで保証されている。 J2SE 1.3 以降のJavaプログラムでは下位互換性は保証されないが、Java実行環境 (JRE) の自動アップデート機能によって仮想マシンを最新バージョンにアップデートすれば解決できる。 JDK 1.1、J2SE 1.2 時代のJavaプログラムは、現在となっては古いため、上位互換性問題に引っかかる可能性がある。
その場合は、そのJavaプログラムを作った者に最新版のJavaコンパイラでもコンパイルが通るように直して貰うしかない。
この問題も、サン・マイクロシステムズのJavaコーディング規約をJavaプログラマが守っていればほぼ起きることがなく、Javaソースコード上のimport宣言や新しく加わったJavaキーワード(enum や assert)と重複するものが無ければほぼ心配することもなくなる。とくに、多くの場合において、これらの問題はコーディング規約だけでなく、統合開発環境やCheckStyle、FindBugsなど各種ツールなどによって解決できるケースがある。Javaプログラマは、日頃からCheckStyleやFindBugsを使ってプログラミングしていれば、上位互換性の問題につき当たる可能性は下がる。
|
|
|
|---|---|
| 主要技術 | プログラミング言語Java - Javaプラットフォーム - Java Development Kit - Java仮想マシン - Java Runtime Environment - Javaコンパイラ - Javaバイトコード - JAR |
| 歴史 | Javaに対する批判 - Java Community Process - サン・マイクロシステムズ |
| 言語機能 | 文法 - Javaの予約語 - パッケージ - Javadoc |
| 関連技術 | Jakarta Project - Apache Tomcat - NetBeans - Java Beans - Java Message Service - Java Transaction API - Java3D - JDBC - Java Web Start - Applet - Servlet - JavaServer Pages - Java Foundation Classes |