1。RTTI:
ランタイムタイプ情報を使用すると、プログラムの実行中にタイプ情報を検出および使用できます。
Javaで実行するときにオブジェクトとクラスに関する情報を特定するには、従来のRTTIとリフレクションが2つあります。 RTTIについて話しましょう。
RTTI:実行時に、オブジェクトのタイプを特定します。ただし、このタイプはコンパイル時に知られている必要があります。
RTTIの使用を確認するために例を見てみましょう。これには、多型の概念が含まれます。コードの基本クラスへの言及のみで動作させるようにすると、実際に特定のサブクラスのメソッドを呼び出すと、コンクリートオブジェクト(円、正方形、または三角形、以下の例を参照)が作成され、形状(特定のタイプのオブジェクトを無視して)を使用し、匿名(特定のタイプではなく)を使用します。
抽象クラスの形状{//これは、現在のクラスのtoString()メソッドを呼び出し、実際のコンテンツvoid draw(){system.out.println(this + "draw()")を返します。 } // toString()を抽象型として宣言し、強制統合を強制統合をメソッド抽象パブリック文字列toString();} class Circleはshape {public string toString(){return "circle"; }} class正方形はshapeを拡張します{public string toString(){return "square"; }} classトライアングルはshapeを拡張します{public string toString(){return "triangle"; }} public static void main(string [] args){// shapeオブジェクトをリスト<shape>の配列に配置すると、形状に変換され、特定のタイプ情報リスト<shapeList = arrays.aslist(new circle()、new Triangle()、new Triangle())を失います。 //アレイから取り出されると、実際、この容器のすべての要素がオブジェクトとして保持され、結果を形状に自動的に変換します。これはRTTIの基本的な使用です。 for(shape shape:shapelist){shape.draw(); }}出力の結果は次のとおりです。
cirlledraw()squareardraw()triangledraw()
アレイに堆積すると、自動的に形状に変換され、特定のタイプが失われます。配列から取り出されると(リストコンテナはすべてをオブジェクトとして保持します)、結果を形状に自動的に変換します。これは、RTTIの基本的な使用法です。 Javaのすべてのタイプ変換は、実行時、つまりRTTI:実行時にオブジェクトのタイプを識別するときに修正されます。
上記の変換は徹底的ではありません。配列の要素が取り出されると、オブジェクトは特定のタイプではなく形状に変換されます。これは、コンパイル中にコンテナとJavaジェネリックシステムによって行われ、実行時にこれを確保するためのタイプ変換操作があります。
形状オブジェクトを介してサブクラスに実行できる特定のコードは、多型によって決定されます。詳細については、形状参照によって指摘された特定のオブジェクトに依存します。
さらに、RTTIを使用して、形状参照によって指されたオブジェクトの正確なタイプをクエリしてから、サブクラスメソッドを選択的に実行できます。
2。クラスオブジェクト:
JavaでRTTIがどのように機能するかを理解するには、特別なオブジェクトクラスによって行われる実行時にタイプ情報がどのように表されるかを知る必要があります。
クラスオブジェクトは、クラスのすべての「通常の」オブジェクトを作成するために使用されます。 Javaはクラスオブジェクトを使用してRTTIを実行します。
新しいクラスがコンパイルされるたびに、クラスオブジェクト(。クラスファイル)が生成されます。このプログラムを実行しているJVMは、「クラスローダー」サブシステムを使用します。
クラスローダーサブシステム:クラスローダーチェーンが含まれていますが、JVM実装の一部であるネイティブクラスローダーは1つだけです。ネイティブクラスローダーは、通常ローカルディスクからのJava APIクラスを含む信頼できるクラスをロードします。 Webサーバーアプリケーションをサポートするためにクラスを特定の方法でロードする必要がある場合、追加のクラスローダーを添付することができます。
2.1。クラスのロードのタイミング:
このクラスは、プログラムがクラスの静的メンバーへの最初の参照を作成するときにロードされます。これは、コンストラクターが実際にクラスの静的な方法であることを証明しています。新しいオペレーターを使用してクラスの新しいオブジェクトを作成する場合、クラスの静的メンバーへの参照としても使用されます。
Javaプログラムは動的にロードされ、オンデマンドでロードされていることがわかります。クラスが必要な場合、クラスローダーはまずこのクラスのクラスオブジェクトがロードされているかどうかを確認します。ロードされていない場合、デフォルトのクラスローダーは、クラス名に基づいて.classファイルを見つけます。次は確認段階です。ロードされたとき、彼らはそれらが破損しておらず、悪いJavaコードが含まれていないことを確認するために検証を受け入れます。
2.2。クラス関連の方法、newInstance()
以下は、クラスオブジェクトの負荷を示す例です。
クラスA {//静的コードベース、初めてロードされたときに実行されます。また、クラスがstatic static {system.out.println( "loading a"); }} class b {static {system.out.println( "loading b"); }} class c {static {system.out.println( "loading c"); }} public class load {public static void main(string [] args){system.out.println( "execute main ..."); new a(); system.out.println( "new a"後); try {class.forname( "com.itzhai.test.type.b"); } catch(classNotFoundException e){system.out.println( "クラウドはクラスBを見つけません"); } system.out.println( "class. -forname bの後");新しいc(); system.out.println( "新しいcの後); }}出力の結果は次のとおりです。
メインを実行...新しいアロードbafter class.forname bloading cafter new cを実行する
クラスオブジェクトは、必要なときにのみロードされていることがわかります。 class.forname()メソッドに注意してください:
forname()メソッドは、クラスオブジェクトへの参照を取得する方法です。クラスオブジェクトへの適切な参照を取得することにより、実行時にタイプ情報を使用できます。
既に関心のあるオブジェクトがある場合は、クラスオブジェクトによって提供されるgetClass()メソッドに従うことで、クラスの参照を取得できます。
クラスが使用するコードは次のとおりです。
インターフェイスx {}インターフェイスy {}インターフェイスz {} class letter {letter(){};文字(int i){};} class newletterは文字装置を拡張しますx、y、z {newletter(){super(1); };} public class clasStest { / *** print型情報* @param c* / static void printinfo(class c){// getName()完全に適格なクラス名System.out.out.println( " + c.getName() +" IS Interface? " + chis interface()); //クラス名System.out.println( "simple name:" + c.getSimplename())を取得します。 //完全に適格なクラス名System.out.println( "Canonical name:" + c.getCanonicalName())を取得します。 } public static void main(string [] args){class c = null; try {//クラス参照c = class.forname( "com.itzhai.test.type.newletter"); } catch(classNotFoundException e){system.out.println( "com.itzhai.test.type.newletter"); System.Exit(1); } //インターフェイスタイプ情報の情報(class face:c.getinterfaces()){printinfo(face); } //スーパークラスクラスリファレンスクラスを取得= c.getsuperclass();オブジェクトobj = null; try {// newinstance()メソッドobj = up.newinstance()を介してクラスのインスタンスを作成します。 } catch(instantiationException e){system.out.println( "インスタンス化できない"); } catch(Illegalaccessexception e){system.out.println( "can ascas"); } //スーパークラスタイプ情報printinfo(obj.getclass()); }}出力は次のとおりです。
クラス名:com.itzhai.test.type.xはインターフェイスですか? TrueSimple名:XCanonical名:com.itzhai.test.test.type.xclass名:com.itzhai.test.type.yはインターフェイスですか?不明瞭な名前:ycanonical名:com.itzhai.test.type.class名:com.itzhai.test.type.zはインターフェイスですか? TrueSimple名:Zcanonical名:com.itzhai.test.test.type.zclass名:com.itzhai.test.type.letterはインターフェイスですか? falsesimple名:LetterCanonical名:com.itzhai.test.type.letter
forname()に渡された文字列は、完全に適格な名前(パッケージ名を含む)を使用する必要があることに注意してください。
PrintInfoで使用される方法により、実行時にオブジェクトの完全なクラス継承構造を見つけることができます。
ClassのNewInstance()メソッドを使用することにより、「仮想コンストラクター」を実装してクラスのインスタンスを作成する方法です。オブジェクト参照は取得されますが、参照されると文字オブジェクトを指します。 newInstance()を使用して作成されたクラスには、デフォルトのコンストラクターが必要です。 (反射APIを介して、任意のコンストラクターを使用してクラスオブジェクトを動的に作成できます)。
2.3。クラスリテラル定数:
getName()メソッドを使用することに加えて、Javaはクラスオブジェクトへの参照を生成する別の方法、つまりクラスリテラル定数を使用する別の方法も提供します。
Newletter.class;
この方法はシンプルで安全で、コンピレーション中にチェックされ、より効率的になります。通常のクラスだけでなく、インターフェイス、配列、および基本データ型にも使用できます。さらに、基本データタイプのラッパークラスには、標準のフィールドタイプもあります。タイプフィールドは、対応する基本データ型クラスオブジェクトを実行するための参照です。統一のために、form .classを使用することをお勧めします。
2.4。 .classの使用とgetname()メソッドを使用することの違いは、オブジェクト参照を作成します。
.classで作成された場合、クラスオブジェクトは自動的に初期化されません。作成手順は次のとおりです。
(1)ロードはクラスローダーによって実行されます:バイトコード(通常はクラスパスで指定されたパスでは必要ありませんが、必要ありません)を調べ、これらのバイトコードからクラスオブジェクトを作成します。
(2)リンクは、クラスのバイトコードを検証し、静的ドメインにストレージスペースを割り当てます。必要に応じて、このクラスによって作成された他のクラスへのすべての参照は解析されます。
(3)初期化クラスにスーパークラスがある場合、それを初期化し、静的初期イザーと静的初期化ブロックを実行します。
初期化は、静的メソッドへの最初の参照(コンストラクターが暗黙的に静的です)または非番号静的ドメインに遅延します。
class data1 {static final int a = 1;静的最終ダブルb = math.random(); static {system.out.println( "init data1 ..."); }} class data2 {static int a = 12; static {system.out.println( "init data2 ..."); }} class data3 {static int a = 23; static {system.out.println( "init data3 ..."); }} public class classtest2 {public static void main(string [] args){system.out.println( "data1.class:");クラスdata1 = data1.class; System.out.println(data1.a); // data1 system.out.println(data1.b); // data1初期化System.out.println(data2.a); // data2初期化try {class data3 = class.forname( "com.itzhai.test.type.data3"); // data3初期化} catch(classNotFoundException e){System.out.println( "Can con con com.itzhai.test.test.data3 ..."); } system.out.println(data3.a); }}出力の結果は次のとおりです。
data1.class:1init data1 ... 0.26771085109184534init data2 ... 12init data3 ... 23
初期化は、可能な限り「怠zy」として効果的に達成されます。
2.5。以下は、初期化を実行するかどうかを決定するためのいくつかの状況です。
(1)クラス構文はクラスへの参照を取得し、初期化を引き起こしません。
(2)class.forname()クラス参照を生成し、すぐに初期化されます。
(3)静的最終値が「コンパイラ定数」である場合、この値はクラスを初期化せずに読み取ることができます。
(4)ドメインを静的ファイナルに設定するだけで、この動作を確保するだけでは不十分です。
静的最終ダブルb = math.random();
(5)静的ドメインがBushifinalである場合、アクセスするときは、常にリンクして初期化する必要があります。
2.6。一般化されたクラスの引用:
クラス参照は、指すオブジェクトの正確なタイプを表し、オブジェクトはクラスクラスのオブジェクトです。 Javase5では、クラスの参照によって指摘されたクラスオブジェクトはジェネリックによって認定され、コンパイラは追加のタイプチェックを実施できます。
class intcls = int.class; // genericsを使用してクラスクラス<integer> genintcls = int.class; // genericsのないクラスを指し示す参照を定義します。
2.6.1。ワイルドカードを使用しますか?ジェネリックの制限を緩和してください:
class <?> intcls = int.class; intcls = string.class;
Javase5では、クラス<?>は通常のクラスよりも優れており、クラス<?>の利点は、あなたが起こっているか過失ではなく、非特異的なクラス参照を使用していることを意味するため、クラス<?>を使用することをお勧めします。
特定のタイプへのクラスへの参照を定義するには、またはそのタイプのサブタイプが拡張機能付きのワイルドカードを使用できます。スコープを作成します。
クラス<?番号> num = int.class; // numの参照範囲は数字とそのサブクラスであるため、値num = double.classを割り当てることができます。 num = number.class;
2.6.2。 genericsの下のnewinstance()メソッド:
genericsの後にクラスを使用すると、NewInstance()を呼び出して返されるオブジェクトは正確なタイプですが、GetSuperClass()を使用してジェネリックに対応するスーパークラスを取得すると、実際のタイプにいくつかの制限があります。
Dog Dog = Dogcls.NewInstance(); Abstract Class Animal {}クラスの犬は動物{} //次の執筆方法が間違っており、クラスのみを返すことができますか?スーパードッグ>タイプ//クラス<動物>動物cls = dogcls.getsuperclass();クラス<? Super Dog> AnimalCls = dogcls.getSuperClass(); //取得したスーパークラスリファレンスを介して、オブジェクトタイプオブジェクトを返すオブジェクトのみを作成できますobj = animalcls.newinstance(); 2.6.3。新しい変換構文:cast()メソッド
コードを直接見る:
動物動物= new Dog(); class <dog> dogcls = dog.class; dog dog = dogcls.cast(動物); //次の変換方法dog =(dog)animal;
CAST()メソッドを使用すると、追加の作業が行われていることがわかります。この変換方法は、次の状況で使用できます。ジェネリックバンドを作成するとき、クラス参照が保存され、このクラス参照を介して変換を実行する場合は、CAST()メソッドを使用できます。
3.タイプチェックインスタンスのof
3.1。タイプ変換の前に確認してください
コンパイラを使用すると、スーパークラスへの参照に値を割り当てるのと同じように、表示された変換操作なしで、上向きの変換割り当て操作を自由に実行できます。
ただし、表示されたタイプの変換が使用されない場合、コンパイラはダウンコンバージョン割り当てを実行することはできません。現時点では、オブジェクトが特定のタイプのインスタンスであるかどうか、キーワードのキーワードインスタンスであるかどうかを確認することもできます。
if(x instanceof dog)((dog)x).bark();
3.2。 rttiの形式:
したがって、これまでのところ、rttiの形式には次のものが含まれていることがわかります。
(1)従来のタイプ変換(形)
(2)オブジェクトのタイプを表すクラスオブジェクト
(3)キーワードインスタンス
3.3。動的インスタンスのメソッド:
class.isInstanceメソッドは、オブジェクトを動的にテストする方法を提供します。
以下は、インスタンスの使用とクラスの使用を示しています。
属性:
パブリックインターフェイス属性{}形:
/** *抽象クラスを作成 */パブリックアブストラクトクラスの形状{// } // toString()メソッドを抽象的に宣言するため、継承者にメソッドの書き換えを強制します。抽象的なパブリックストリングtoString();}丸:
public class Circleは、形状属性属性{public string toString(){return "circle"; }}四角:
パブリッククラスの正方形はshapeを拡張します{public string toString(){return "square"; }}三角形:
パブリッククラスの三角形はshapeを拡張します{public string toString(){return "triangle"; }}タイプチェック:
// instanceofcircle c = new circle(); //スーパークラスSystem.out.Formatのインスタンスのインスタンス( "Instanceof:%sは形状?%b/n"、c.tostring()、c instance of shape) Circle); // SuperClass System.out.Formatのインスタンス( "Class.IsInstance:%sは形状?%b/n"、c.toString()、c Instance of Circle)のかどうかを判断します。 shape.class.isInstance(c)); //インターフェイスがSystem.out.Formatであるかどうかを決定するインスタンス( "class.isInstance:%sは属性?%b/n"、c.toString()、aTtribute.class.isInstance(c));
Instance.ofまたはclass.Instanceメソッドは、システムのインスタンスを継承するかどうか、つまりそれ自体を判断することに加えて、それがスーパークラスかインターフェイスのインスタンスかを決定するかどうかを決定することがわかります。
以下は、動的class.instanceの使用方法を示しています。
最初に抽象型ジェネレータークラスを作成します。
パブリックアブストラクトクラスShapeCreator {private Random Rand = new Random(10); //実装クラスによって提供されるオブジェクトタイプの配列を返します。 Fornameに基づいて、クラスのリテラル定数に基づいて、2つの実装フォームが表示されます。クラスパブリックアブストラクトリスト<クラス<? shape >> types(); //オブジェクトタイプの配列でタイプオブジェクトインスタンスをランダムに生成しますpublic shape randomshape(){int n = rand.nextint(types()。size()); try {return types()。get(n).newinstance(); } catch(instantiationexception e){e.printstacktrace(); nullを返します。 } catch(Illegalaccessexception e){e.printstacktrace(); nullを返します。 }} //ランダムな配列のpublic shape [] Createarray(int size){shape [] result = new Shape [size]; for(int i = 0; i <size; i ++){result [i] = randomshape(); } return result; } //ランダムアレイ、generic arraylist public arrayList <shape> arrayList(int size){arrayList <shape> result = new arrayList <shape>(); collections.addall(result、createarray(size));返品結果; }}次に、この抽象クラスの実装を書きます。
/** * forNameジェネレーターの実装 * @Author artinking * */public class fornamecreator拡張shapecreator {private static list <class <? shape >> types = new ArrayList <class <? shape >>(); private static string [] typenames = {"com.itzhai.javanote.entity.circle"、 "com.itzhai.javanote.entity.square"、 "com.itzhai.javanote.entity.trianger"}; @suppresswarnings( "unused")private static void roader(){for(string name:typenames){try {types.add((class <?extends shape>)class.forname(name)); } catch(classNotFoundException e){e.printstacktrace(); }}} // static {loader();の読み込みに必要なタイプの配列を初期化します。 } public list <class <? shape >> types(){return types; }}最後に、インスタンスを使用して、形状の数を数えるクラスを書きます。
public class shapecount {static class shapecounterはhashmap <string、integer> {public void count(string type){integer数量= get(type); if(quanty == null){put(type、1); } else {put(type、数量 + 1); }}} //キーワードのインスタンスを介してオブジェクトタイプの指定をデモンストレーションpublic static void countshapes(shapecreator creator){shapecounter counter = new Shapecounter(); for(形状:creator.createarray(20)){if(shape instanceof circle)counter.count( "circle"); if(shape instanceof square)counter.count( "square"); if(shape instance of triangle){counter.count( "triangle"); }} system.out.println(counter); } public static void main(string [] args){countshapes(new fornamecreator()); }}抽象クラスの実装を書き直し、クラスのリテラル定数でそれを再実装します。
/***リテラルジェネレーターの実装*/パブリッククラスのリテラルクレアトール拡張shapecreator {public static final list <class <? shape >> alltype = collections.unmodifiablelist(arrays.aslist(circl.class、triangle.class、square.class));パブリックリスト<クラス<? shape >> types(){return alltype; } public static void main(string [] args){system.out.println(alltype); }}次に、class.instanceを使用して、次のように形状の数をカウントします。
/*** class.instance of dynamicテストオブジェクトを使用して、元のShapecountの単調なインスタンスのステートメントを削除* shape >> shapetypes = literalcreator.alltype;静的クラスShapecounterはHashmap <String、integer> {public void count(string type){integer Quantion = get(type); if(quanty == null){put(type、1); } else {put(type、数量 + 1); }}} // class.isInstance()public static void countshapes(shapecreator creator){shapecounter counter = new shapecounter(); for(shape shape:creator.createarray(20)){for(class <?extends shape> cls:shapetypes){if(cls.isinstance(shape)){counter.count(cls.getsimplename()); }} system.out.println(counter); } public static void main(string [] args){countshapes(new fornamecreator()); }}これで、ジェネレーターには2つの実装があります。ここに外観のレイヤーを追加して、デフォルトの実装方法を設定できます。
/***ジェネレーターの2つの実装があります。ここに外観のレイヤーを追加して、デフォルトの実装方法 */public class shapes {public static final shapecreator creator = new LiteralCreator()を設定しましょう。 public static shape randomshape(){return creator.randomshape(); } public static shape [] createarray(int size){return creator.createarray(size); } public static arrayList <shape> arrayList(int size){return creator.arrayList(size); }} 3.4。インスタンスとクラスの同等性:
InstanceOFおよびISInstance()によって生成された結果はまったく同じであり、タイプの概念を維持し、このクラスのクラスまたは派生クラスを決定します。
equals()は==と同じであり、このより実用的なクラスオブジェクトを使用すると、継承は考慮されません。
System.out.println(new Circle()Instanceof Circle); // truesystem.out.println(shape.class.isinstance(new Circle())); // truesystem.out.println((new circle())。getClass()== circl.class); // truesystem.out.println((new Circle()。getClass())。equals(shape.class)); // 間違い