Java言語のClassクラス

11788 ワード

基本概念
Objectクラスでは、すべてのサブクラスで継承されるメソッドが定義されています.public final Class getClass()
このメソッドの戻り値タイプはクラスであり,Java反射の元であり,実際には反射とはプログラムの実行結果からもよく理解できる,すなわち,オブジェクト反射によってクラス名を求めることができる.
オブジェクトが鏡に映った後に得られる情報:あるクラスの属性、方法、コンストラクタ、あるクラスがどのインタフェースを実現したのか.各クラスについて、JREは変更されないClassタイプのオブジェクトを保持します.Classオブジェクトには、特定の構造(class、interface、enum、annotation、primitive type、void)に関する情報が含まれます.
Class自体もクラスです
Classオブジェクトはシステム確立オブジェクトのみである.
ロードされたクラスはJVMに1つのClassインスタンスしかありません.
Classオブジェクトは、JVMにロードされた.classファイルに対応します.
各クラスのインスタンスは、自分がどのClassインスタンスによって生成されたかを覚えています.
クラス内のすべてのロード構造はクラスによって完全に得ることができる.
ClassクラスはReflectionの根源であり、動的にロード、実行したいクラスに対しては、まず対応するClassオブジェクトを取得する必要があります.
JavaにClassオブジェクトがあるタイプ
Java言語では、すべてがオブジェクトです.オブジェクトは主に2つに分けられます.1つは通常のクラスで作成されたインスタンスオブジェクトで、1つはClassクラスオブジェクトです.各クラスの実行時のタイプ情報はクラスオブジェクトによって表され、このオブジェクトにはクラスに関する情報が含まれています.実際、JavaのインスタンスオブジェクトはClassオブジェクトによって作成され、JavaはClassオブジェクトを使用してRTTI(ランタイムタイプ識別、Run-Time Type Identification)を実行し、マルチステートはRTTIに基づいて実現されます.
ではJavaでClassオブジェクトを持つタイプは?
class:外部クラス、メンバー(メンバー内部クラス、静的内部クラス)、ローカル内部クラス、匿名内部クラスinterface:インタフェース[]:配列Enum:列挙annotation:注記@interface primitive type:基本データ型void
コードを使って、これらのタイプのClassオブジェクトは何ですか?
Class c1 = Object.class; //  
Class c2 = Comparable.class; //   
Class c3 = String[].class; //     
Class c4 = int[][].class; //     
Class c5 = Override.class; //   
Class c6 = ElementType.class; //   
Class c7 = Integer.class; //       (   )
Class c10 = int.class; //       
Class c8 = void.class; //    
Class c9 = Class.class; // Class
​
System.out.println(c1); // class java.lang.Object
System.out.println(c2); // interface java.lang.Comparable
System.out.println(c3); // class [Ljava.lang.String;
System.out.println(c4); // class [[I
System.out.println(c5); // interface java.lang.Override
System.out.println(c6); // class java.lang.annotation.ElementType
System.out.println(c7); // class java.lang.Integer
System.out.println(c10);// int
System.out.println(c8); // void
System.out.println(c9); // class java.lang.Class
int[] a = new int[10];
int[] b = new int[100];
​
/*
        ,           ,     Class  
 */
System.out.println(a.getClass()); //class [I
System.out.println(b.getClass()); //class [I
​
System.out.println(b.getClass().hashCode()); //1735600054
System.out.println(a.getClass().hashCode()); //1735600054

各クラスにはクラスオブジェクトがあり、新しいクラスをコンパイルするたびにクラスオブジェクトが生成されます.より正確には、同じ名前の.classファイルに保存されます.プログラムでこのクラスを使用する必要がある場合(クラスを必要とするオブジェクトやクラスを参照する静的変数を含む)は、クラスローダによってクラスをメモリに追加します.
Classクラスには共通の構築方法はありません.Classオブジェクトは、クラスのロード時にJava仮想マシンおよび呼び出しクラスローダのdefineClassメソッドによって自動的に構築されるため、クラスオブジェクトを明示的に宣言することはできません.
Classオブジェクトを取得する方法は、次の3つです.Class.forName(“ ”) .getClass() .class ( )
Class.forNameとインスタンスオブジェクト.getClass()Class.forNameメソッドは、Classクラスの静的メンバーである.forName実行中、入力されたパラメータクラスがまだロードされていない場合、JVMはクラスローダを呼び出してこのパラメータクラスをロードし、ロードされたClassオブジェクトを返すことが分かった.Classオブジェクトは、他のオブジェクトと同様に、参照を取得して操作できます.このパラメータクラスの静的文ブロックは、クラスのロード中に実行されます.Class .forNameにロードするクラスが見つからない場合は、ClassNotFoundException例外が放出されます.
スタティック文ブロック(static)はクラスが最初にロードされたときに実行され、Classオブジェクトは必要なときにのみロードされます.Class.forNameの利点は、Class参照を得るためにそのタイプのオブジェクトを持つ必要がなく、フルネームでそのタイプのClass参照を返すことができることである.このタイプのオブジェクトが既に存在する場合、getClass()メソッドを呼び出すことでClassリファレンスを取得できます.このメソッドはルートクラスObjectの一部に属し、オブジェクトの実際のタイプを表すClassリファレンスを返します.
public class TestClass {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("  .A");
        B b = new B();
​
        Class b2 = b.getClass();
    }
}
class A{
    static {
        System.out.println("My name is A");
    }
}
class B{
    static {
        System.out.println("My name is B");
    }
}
​
/*
    :
    My name is A
    My name is B
*/

AとBの2つのクラスには静的文ブロックがあることがわかります.したがって、Class.forNameキーとnewキーを使用して実行すると、AとBはそれぞれメモリにロードされ、2つのクラスがメモリにロードされると、内部の静的文ブロックが実行されます.一方、newキーでオブジェクトを作成すると、対応する実際にこのオブジェクトを作成したクラスがメモリにロードされるため、getClass()メソッドを実行すると、クラスロードの操作は実行されず、javaスタックから直接このタイプのClass参照が返されます.
クラス文字定数
Javaはまた、Classオブジェクトへの参照を生成する別の方法を提供します.つまり、Cat.classは、コンパイル時にチェックされるため、より簡単で安全です(try文ブロックに配置する必要はありません).さらにforName()メソッドの呼び出しを根絶し、すべてがより効率的になります.クラスの字面量は、通常のクラスだけでなく、インタフェース、配列、および基本データ型にも適用できます.
.classを使用してClassオブジェクトへの参照を作成する場合、そのClassオブジェクトは自動的に初期化されません(これはClass.forNameメソッドとは異なります).クラスオブジェクトの初期化フェーズは、静的メソッドまたは非常数の静的ドメインへの最初の参照に遅延されたときに実行されます.
public class TestClass {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
              ,  class      Class  
         */
        Class b = B.class;
        Class a = A.class;
        //          A B             
        //              Class  
​
        System.out.println(A.a);
        System.out.println(B.s);
        
        /*
           My name is A
            0
            My name is B
            lalala
        */
    }
}
class A{
    static  int a = 0;
    static {
        System.out.println("My name is A");
    }
}
class B{
    static String s = "lalala";
    static {
        System.out.println("My name is B");
    }
}

 
AとBの静的メンバーを呼び出すと、この2つのクラスオブジェクトが初期化され、静的文ブロックの内容が先に出力され、メインプログラムで2つの静的メンバー変数の値が出力されることが分かった.次に、Aクラスのa変数を定数static final int a = 0;に変更し、メインプログラムの再呼び出し時にAの静的文ブロックが印刷されていないことがわかり、参照定数静的メンバーがクラスオブジェクトを初期化しないことを示します.
クラスへのClass参照を取得するために.class構文のみを使用すると、初期化は開始されないこともわかります.ただし、Class.forNameを使用して参照を生成すると、すぐに初期化されます.
フィールドがstatic finalで修飾されている場合、「コンパイル時定数」と呼ばれます.「Aクラスのaフィールドのように、このフィールドを呼び出すときにAクラスを初期化することはありません.staticやfinalで修飾されたフィールドは、コンパイル期間中に結果を定数プールに入れてしまうためです.ただし、1つのドメインをstaticやfinalに設定するだけでは、Bのsフィールドを呼び出すと、クラスのsフィールドはコンパイル時定数ではないため初期化されます.
クラスがメモリにロードされると、どのような方法でクラスのClassオブジェクトを取得しても、同じJavaヒープ・アドレスへのClass参照が返されます.JVMでは同じタイプのClassオブジェクトは2つも作成されません.実際には、どのClassオブジェクトに対しても、Java仮想マシンにおける唯一のクラス・ローダとクラス自体で決定する必要があります.つまり、2つのClassオブジェクトが同じClassファイルに由来していても、ロードするクラスローダが異なる限り、2つのClassオブジェクトは等しくありません.ここでの「等しい」クラスを表すClassオブジェクトを含むequals()、isAssignableFrom()、isInstance()などのメソッドの戻り結果は、instanceofキーワードを使用してオブジェクトの属する関係を判定する結果も含まれます.したがって、Java仮想マシンでは親委任モデルを使用してクラスローダ間の関係を整理し、Classオブジェクトの一意性を保証します.
 
参考ブログアドレス:
https://blog.csdn.net/dufufd/article/details/80537638
https://blog.csdn.net/BraveLoser/article/details/82500474