長年のJava開発の経験から、長い目で見れば、開発者が****使用しない方がよいJava SEの機能/APIは、次のようなものだと私は学びました。
反射
バイトコード
バイトコード
スレッドローカル
クラスローダ
弱/ソフトリファレンス
ソケット
1.リフレクション
リフレクション(反射)は、Springや「Hibernate」などの多くの一般的なライブラリに存在するメカニズムで、ビジネスコードに反映させることで、私はリフレクションの使用を避けることをお勧めします。私がリフレクションを使わない理由を以下に列挙します:
1つ目は、コードの読みやすさ/ツールのサポートです。IDEを開き、Javaコード中の相互依存性を見つけてください。メソッド呼び出しをリレクションに置き換えて、ステップを繰り返してみてください。物事は手に負えなくなってきています。通常は、カプセル化してから状態を修正すべきです。これが具体的なコード例です:
public class Secret {
private String secrecy;
public Secret(String secrecy) {
this.secrecy = secrecy;
}
public String getSecrecy() {
return null;
}
}
public class TestSecrecy {
public static void main(String[] args) throws Exception {
Secret s = new Secret("TOP SECRET");
Field f = Secret.class.getDeclaredField("secrecy");
f.setAccessible(true);
System.out.println(f.get(s));
}
}
上のコードを見ればわかるように、getDeclaredField()メソッドのパラメータは実行時にしか発見できません。そして、ご存知のように、実行時に発生するバグは、スクリプトを実行しない場合よりも常に厄介です。
第二に、リフレクション呼び出しの最適化はJITによって実行され、最適化の中には適用に時間がかかるものや、適用すらされないものもあります。しかし、一般的なビジネス・アプリケーションでは、このようなパフォーマンス・オーバーヘッドに気づかないことがあります。
要するに、開発者はAOPを通じてビジネス・レイヤーでリフレクションを賢く使うべきです。
2.バイトコード操作
バイトコード操作ですが、Java EEアプリケーションでCGLIBやASMを直接使っているのを見たら、私はすぐに逃げ出すでしょう。
コンパイル中に実行可能なコードがないことほど悪いことはありません。実際、製品が実行されているときには、どのコードが実行されているのか見当もつきません。そのため、トラブルに遭遇したとき、実行時のトラブルシューティングやデバッグにエラーを投げるのは自然なことですが、それはかえってトラブルを増やすことになります。
3.スレッドローカル
私がビジネス・レイヤーのコードでスレッド・ロカルを見たときに戦慄する2つの無関係な理由があります。第一に、ThreadLocalsのヘルプでは、メソッド呼び出しチェーンを通じて明示的に渡されない変数がたくさん使われているのを目にすることがあります。これはある文脈では便利ですが、一旦不注意になると、コードに予期しない依存関係をたくさん構築することになります。
2つ目の無関係な理由は、ThreadLocalsにデータを保存することがメモリリークの引き金になる、私の日々の仕事に関連するものです。私が遭遇したPermgenリークの少なくとも10分の1はThreadLocalsの使用が原因で、クラスローダーとスレッドプールの組み合わせで、"java.lang.OutOfMemoryError:Permgen space "例外がすぐに現れることがあります!.
4.クラスローダー
まず第一に、クラス・ローダーは複雑な獣です。その階層構造、委譲の仕組み、クラスキャッシュなどをまず理解しなければなりません。理解したつもりでも、うまく動作しないことがあります。これは最終的にクラスローダーのリーク問題につながります。ですから、このタスクはアプリケーションサーバーに任せることをお勧めします。
5.弱/ソフトリファレンス
ここまでで、Javaの内部メソッドについての理解が深まったはずです。ソフト参照を使ってキャッシュをすべて書き換えるのは賢明ではありません。ハンマーを手にしたとき、釘がないか探し回るのはわかります。しかし、キャッシュはハンマーにとって良い釘ではありません。なぜですか?ソフト・リファレンスに基づいてキャッシュを構築することは、GC自身に複雑さを実装するのではなく、GCに複雑さを委ねる方法の良い例かもしれません。
ソフト参照を使用してデータを作成し、メモリが枯渇したときにGCが来てそれをクリーンアップするキャッシュの例です。しかし、キャッシュ内の削除されたオブジェクトは含まれず、次のキャッシュミスで再作成される可能性があります。メモリがまだ少ない場合は、GC をトリガーして再度クリーンアップを行います。お分かりのように、全体が悪循環に陥り、アプリケーション全体がCPUとGCの操作の絶え間ない状態になります!
6.ソケット
古いjava.net.Socketは複雑すぎて、うまく使うのが難しいんです。ブロッキングはその根本的な欠陥だと思います。典型的なJava EEアプリケーションをWebフロントエンドで書く場合、アプリケーションは多数のユーザーをサポートするために高い並行性を必要とします。
Nettyのような、よりうまく物事を進めるために使える素晴らしいサードパーティライブラリが数多くあるので、開発者は試してみるとよいでしょう。



