実行時型情報あなたは、プログラムの種類に関する情報が実行されている見つけて使用できるようにします。Javaはそれは我々が、実行時にオブジェクトやクラスの情報を認識いかがでしたでしょうか?
主に二つの方法があります:1、伝統的なRTTIが、彼は我々はすでにコンパイル時にすべてのタイプを知っていることを想定しています。2. 反射が、それは、私たちは、実行時に情報クラスを見つけて使用することができます。
まず、RTTIのためになぜ必要
例で見てみましょう:
これは、基本クラス、派生クラス下方膨張の上部に、クラス階層図の典型です。オブジェクト指向プログラミングの基本的な目的は、次のとおり基本クラス(形状)の操作にコードのみの参照を作成します。あなたがプログラムを展開する(例えば、形状ロンボイド由来)新しいクラスを追加する場合、この方法では、元のコードには影響しません。この場合、動的バインディングShapeインタフェースのdraw()メソッド、目的は)シェイプ参照の一般化とクライアントのプログラマは(引き分けを呼び出すことができるようにすることです。ドロー()すべての派生クラスで被覆され、それが結合動的であるため、これもシェイプ参照の一般化によって呼び出され、正しい動作を生成することができます。これは多型です。
抽象 クラスの形状{ 無効ドロー(){ System.out.println(本 + ".draw()" ); } @オーバーライド 抽象 パブリック文字列のtoString(); } クラスサークル延び形状を{ @オーバーライド パブリック文字列のtoString(){ 戻り「サークル」。 } } クラススクエア延び形状を{ @オーバーライド 公共の文字列のtoString(){ リターン「スクエア」。 } } クラストライアングル延び形状を{ @オーバーライド パブリック文字列のtoString(){ 戻り「トライアングル」。 } } パブリック クラス図形{ 公共 静的 ボイドメイン(文字列[]引数){ 一覧 <形状>形状=は、Arrays.asList(新しいサークル()、新しいトライアングル()、新スクエア()); 用(:形状図形形状){ shape.draw(); } } }
結果:
Circle.draw()
Triangle.draw()
Square.draw()
分析:1.toString()は、それによって強制的方法後継をオーバーライド、抽象宣言し、フォーマットされていない形のインスタンス化を妨げています。
2.オブジェクトが(例えば「+」として、式文字列オブジェクト)文字列式に表示されている場合、のtoString()が自動的に文字列オブジェクトを生成するために呼び出されます。
派生クラスのオブジェクトがリスト<形状>配列に形状が、アップキャスト形状もShapeオブジェクトの特定のタイプを失ったときとき3.変換が最大になります。、彼らはShapeクラスオブジェクトの配列です。
4.(すべてのものに、そのような容器は、オブジェクトとして保持する)配列から要素を削除する場合、変換の結果は、形状う。彼らはJavaで実行時に正しさのために、すべてのキャストをチェックしているので、これは、最も基本的な使用RTTIフォームです。また、これはRTTIの名前の意味は次のとおりです。実行時には、オブジェクトの種類を識別するために。
5.しかし、移行が完了RTTI例ない:リスト<形状>提供される情報は、形状タイプで格納されるため、オブジェクトが、形状ではなく、特定の派生型に変換されます。コンパイル時、この汎用コンテナの保証では、実行時に、変換操作の種類によってこれを確認します。
何6.Shapeターゲットの特定の実装、それは具体的なオブジェクト(派生クラスのオブジェクト)とほとんどの人が対象の特定の種類できるだけ知りたいの現実への基準点であるが、唯一の家族やジェネリッククラスの契約、のような:形状、書き込みへの容易なコードは、より優れた設計を実現するためにそうすることを、理解および変形は、その「多型」、オブジェクト指向プログラミングの基本的な目標です。
二、クラスオブジェクト
使用RTTIは、あなたは、Shape参照物体点の正確なタイプ照会し、次に選択するか、特定の特性を拒否することができます。JavaでどのようにRTTI理解するためには、第一のタイプの情報が実行時にどのように表現されるかを知っている必要があります。クラスは、オブジェクト、この仕事を引き受けるClassオブジェクトがクラスに関連する情報が含まれています。Classオブジェクトは、すべてのオブジェクトを作成するために使用される、Javaはリストラ操作の実装に加えて、クラスオブジェクト、クラスのカテゴリを使用してRTTIを行い、RTTIの使用の多くを持っている他の方法があります。
書き込みとは、新しいクラスをコンパイルするたびに、それはクラスのオブジェクトを生成します、同じ名前が存在しているの.classファイルを。Java仮想マシン(JVM)でこのプログラムを実行すると、このクラスのオブジェクトを生成し、「クラスローダ」サブシステムを知られていました。クラスローダサブシステムは、クラスのロードを選ぶためにチェーンを含むが、JVM実装の一部であるだけでネイティブクラスローダ、実際にあります。Webサーバーアプリケーションのサポート、そして、彼らは追加のクラスローダをマウントします:ネイティブクラスローダの負荷は、このチェーンは、追加のクラスローダを追加するために通常必要はありませんが、次のような特別なニーズを持っている場合、通常はローカルディスクローディングコンテンツです。
初めて使用すると、すべてのクラスは、動的にJVMにロードされます。最初のクラスの静的メンバを参照することが作成されると、そのクラスをロードします。これはまた、コンストラクタは静的メソッドで、オブジェクトの新しいクラスを作成するために、新しい演算子を使用基準のクラスの静的メンバとして扱われることを証明しています。このように、Javaプログラムが完全に実行する前にロードされていないときに必要に応じて、各部分がロードされます。クラスローダこのクラスのClassオブジェクトがロードされているかどうかを最初にチェック、すでにロードされていない場合は、クラス名に基づいて.classファイルのデフォルトのクラスローダに見えます。
クラスClassオブジェクトがメモリにロードされると、このクラスのすべてのオブジェクトを作成するために使用されます。
クラスキャンディ{ 静的{ System.out.println( "ロードキャンディ" ); } } クラスガム{ ガム(){ System.out.println( "構築ガム" ); } 静的{ System.out.println( "ロードガム" ); } } パブリック クラスSweetShop { 公共 静的 ボイドメイン(文字列[]引数){ System.out.println(「内部メイン」)。 新しいキャンディ(); してみてください{ Class.forNameの( "ガム" ); } キャッチ(ClassNotFoundExceptionが電子){ e.printStackTrace(); } 新しいガム(); 新しいキャンディ(); } }
结果:
インサイドメイン
ロードキャンディ
ロードガム
構築ガム
Class.forNameの()は、クラスを初期化します:各クラスは、クラスの静的句がされた最初のロード時に実行される、第二の負荷に注意を払うには行く時間ではありません。
Class.forNameの(「」);このメソッドは、クラスのクラスで静的のメンバー。私たちは(労働クラスローダである)、それへの参照を取得して操作することができますようなクラスは、他のオブジェクト上のオブジェクト。forName()はオブジェクト参照クラス、クラスclazz = Class.forNameの(「XXXを得る方法である 」); xxxはパッケージ名でXXXのクラスへの参照を持っていれば!!!完全な名前でなければなりません、あなたがすることができますまた、 :クラスclazz = xxx.getClass();オブジェクトのクラスを取得する方法。クラスは、より一般的に用いられている方法の多くがある含まれています:
同等の新しいインスタンスを取得するための公共TのnewInstance()
公共ネイティブブールでisinstance(Object obj)指定されたオブジェクトクラス・パラメータのインスタンスであります
公共ネイティブブールisAssignableFrom(クラス<? > CLS) これは、決定Class
指定されたオブジェクトを持つクラスまたはインタフェースを表すClass
パラメータは、クラスまたはインタフェースによって表されるかどうかは、同じであるか、またはスーパークラスまたはスーパーインタフェースであります
公共ネイティブブールisInterface();インタフェースか否かを判断します
公共ネイティブブールでIsArray();配列かどうかを決定します
公共ネイティブブールisPrimitive();基本的なタイプかどうかを決定します
コメントかどうかを判断するためのパブリックブールisAnnotation()
パブリックブールisSynthetic()シンクブロックか否かを判断します
公共の文字列のgetName()は、このようなタイプの情報を含む、完全なパケットと名前を返します(または、すべての基本的な型が参照型です)
パブリッククラスローダのgetClassLoader()は、クラスのクラスローダを取得します。
公共TypeVariable <クラス<T >> [] getTypeParameters()はジェネリックを取得します。
<?スーパーT>公共ネイティブクラスgetSuperclass();親ジェネリッククラスとスーパークラスを取得します。
公共パッケージgetPackage()パッケージ名を取得します。
パブリッククラス<?> []でgetInterfaces()クラスによって実装されるインターフェースのリストを取得します
パブリックタイプ[] getGenericInterfacesは()インターフェースとジェネリックの実装のリストを取得します
公共ネイティブクラスは、<?> getComponentType()コンポーネントタイプクラスの配列を返します。これは、配列クラスを表さない場合、このメソッドはnullを返します。そのような配列は、そのようなコンポーネントクラスの型を返す場合
公共のネイティブのint getModifiers()修飾子のクラスを返します。
公共のネイティブオブジェクト[] getSigners(); //非常に感謝私に教えて明らかに有用下さいコメントがある場合、私はまだ知りません
ネイティブのボイドsetSigners(オブジェクト[]署名者)
パブリックメソッドgetEnclosingMethod(定義局所または匿名の内部クラスを得る方法)
公共のString getSimpleName()最も簡単なクラス名を取得します。
公共の文字列getCanonicalName()パッケージ名とクラス名
パブリック文字列getTypeNameメソッドは()を[クラス名]そうでない場合、及びgetSimpleNameように、アレイであります
パブリックフィールド[]れるGetFields()はパブリッククラスフィールドを取得します
publicフィールドが[] getDeclaredFields()クラス内のすべてのフィールドを取得します。
パブリッククラス<?> [] getClasses()すべての公共内部クラスとクラスの親クラスを取得します。
パブリッククラス<?> [] getDeclaredClasses()すべての内部クラスのクラスを取得し、ない親クラスであって、
クラスと親クラスのすべてのパブリックメソッドを取得するために、公開メソッド[] getMethods()
パブリックメソッド[] getDeclaredMethods()はクラスのすべてのメソッドを取得し、しない親クラスであって
パブリックコンストラクタ<?> [] getConstructors()は、すべてのパブリックコンストラクタを取得します。
パブリックコンストラクタ<?> [] getDeclaredConstructors()は、すべてのクラスのコンストラクタを取得します。
publicフィールドのgetField(文字列名)国民は、フィールド名に基づいてフィールドを取得します。
publicフィールドgetDeclaredField(文字列名)は、フィールド名に基づいて、すべてのフィールドを取得します。
パブリックメソッドgetMethod publicメソッドのメソッド名とパラメータを取得する(文字列名、クラス<?> ... parameterTypesパラメータ)
パブリックメソッドgetDeclaredMethod(文字列名、クラス<?> ... parameterTypesパラメータ)メソッド名とパラメータに応じてすべてのメソッドを取得します
パブリックコンストラクタ<T> getConstructor(クラス<?> ... parameterTypesと)publicコンストラクタのパラメータに基づいて得ました
パブリックコンストラクタ<T> getDeclaredConstructor(クラス <?> ... parameterTypesパラメータ) 抗に基づいて、すべての構築方法、パラメータ取得
...そのような重要な方法の記載漏れなどが思い出さしてくださいありがとう〜
クラスリテラル
:Javaはリテラル用いクラス参照を生成するための別の方法、即ちありClassDemo.classを(全く初期化クラスが存在しないため)、このようにシンプルかつ効率的な、そして同じことが、このようにインターフェースに基本的なタイプのアレイを適用し;。パッケージの基本的なタイプの場合、標準のフィールド型があります:
ブール値。クラス = Boolean.TYPEの 文字。クラス = Character.TYPEの バイト。クラス = Byte.TYPE 短いです。クラス = Short.TYPEの int型。クラス = Integer.TYPEの 長いです。クラス = Long.TYPEの フロート。クラス = Float.TYPE ダブル。クラス = Double.TYPEの 無効。クラス = Void.TYPE
あなたはオブジェクトへの参照をこのように作成すると、自動的に準備作業を行うためにクラスを使用するために、Classオブジェクトを初期化しません実際には3つのステップが含まれます。
1.クラスローダによってロードされます。このステップでは、バイトコードを見つけ、これらのバイトコードからクラスオブジェクトを作成します。
作成したクラスの他のすべてのクラスに解決の参照2.接続、クラスのバイトコード検証における接続フェーズ、メモリ空間ドメインの静的割り当て、必要に応じて、。
3.初期化、クラスは親クラス、初期化し、ブロックイニシャライザ静的初期化スタティックを実行している場合。
初期化は、第1の時間基準または非定数静的フィールドの(コンストラクタが暗黙的に静的である)静的メソッドの時に実行されます。
クラスInitableA { 静的 最終 int型 staticFinalA = 47 。 静的 最終 INT staticFinalB = ClassInitialization.random.nextInt(1,000 )。 静的{ System.out.println( "初期化InitableA" ); } } クラスInitableB { 静的 int型 staticNoFinal = 147 。 静的{ System.out.println( "初期化InitableB" ); } } パブリック クラスClassInitialization { パブリック 静的ランダムランダム= 新しいランダム(47 )。 パブリック 静的 ボイドメイン(文字列[]引数){ System.out.println(InitableA.staticFinalA)。 System.out.println(InitableA.staticFinalB)。 System.out.println(InitableB.staticNoFinal)。 } }
結果:
1 初期化InitableA 258 初期化InitableB 147
以下のような静的ファイナル「は、コンパイル時定数」である場合 staticFinalAは、あなたがそのクラスを初期化する必要が読み取ることができますが、 staticFinalBは、それはコンパイル時定数ではありませんので、それは、クラスを初期化します。唯一のstatic修飾子 staticNoFinalにしてそれを読む前に、最初のリンクは、このドメインのストレージを割り当て、ストレージ・スペースを初期化します。
クラスのジェネリック参照
一般的な構文を使用することにより、コンパイラは、追加型チェックを強制することができます:クラス<整数> intClass = int.classを、あなたが制限を緩和したい場合は、ワイルドカードを使用することができます;「?」クラスanyClass = AnyClass.class場合は<?>サブクラスの種類を限定することを意図するものでキーワード拡張を使用することができます。numCLass = int.classをクラス<数を拡張?>; numClass = double.class。
Classオブジェクトを作成するための一般的な構文クラス<玩具>を使用している場合、のnewInstance()オブジェクトは単なるオブジェクトではなかったこと、正確な情報を返します。
あなたが手玩具親ToyFather上にある場合、toyClass.getSuperclass()メソッドは、のみではなく、正確なToyFatherより<スーパーのおもちゃ?>クラスに戻ることができ、および戻りオブジェクト(とき)のnewInstance。