JavaSE筆記之註解和反射(含案例代碼)

来源:https://www.cnblogs.com/ZengYoujiu/archive/2023/03/22/17237995.html
-Advertisement-
Play Games

一.註解 1. 註解入門 Annotation是從JDK5.0開始引入的新技術 Annotation的作用: 不是程式本身,可以對程式做出解釋(這一點和註釋(comment)沒什麼區別) 可以被其他程式(比如:編譯器等)讀取 Annotation的格式: 註解是以“@註解名”在代碼中存在的,還可以添 ...


一.註解

1. 註解入門

  • Annotation是從JDK5.0開始引入的新技術

  • Annotation的作用:

    • 不是程式本身,可以對程式做出解釋(這一點和註釋(comment)沒什麼區別)
    • 可以被其他程式(比如:編譯器等)讀取
  • Annotation的格式:

    • 註解是以“@註解名”在代碼中存在的,還可以添加一些參數值,例如:

      @SuppressWarnings(value="unchecked")
      
  • Annotation在哪裡使用?

    • 可以附加在package,class,method,field等上面,相當於給他們添加了額外的輔助信息,我們可以通過反射機制編程實現對這些元數據的訪問

2. 內置註解

  • @Override

    ​ 定義在java.lang.Override中,此註解只適用於修辭方法,表示一個方法聲明打算重寫超類中的另一個方法聲明。

  • @Deprecated

    ​ 定義在java.lang.Deprecated中,此註解可以用於修辭方法屬性,表示不鼓勵程式員使用這樣的元素,通常是因為它很危險或者存在更好的選擇。

  • @SuppressWarnings

    定義在java.lang.SuppressWarnings中,用來抑制編譯時的警告信息。

    與前兩個註解有所不同,你需要添加一個參數才能正確使用,這些參數都是已經定義好了的,我們選擇性地使用就好了。例如:

    @SuppressWarnings("all")
    @SuppressWarnings("unchecked")
    @SuppressWarnings(value={"unchecked","deprecation"})
    
    //什麼是註解
    public class Test01 extends Object{
        //@Override  重寫的註解
        @Override
        public String toString(){
            return super.toString();
        }
        
    	//@SuppressWarnings("all") 抑制所有警告信息
        @SuppressWarnings("all")
        public static void main(String[] args) {
            test();
        }
    
        //@Deprecated  不推薦程式員使用,但是可以使用,或者存在更好的方式
        @Deprecated
        public  static void test(){
            System.out.println("Deprecated");
        }
    }
    

3. 元註解

  • 元註解的作用就是負責註解其他註解,Java定義了4個標準的meta-annotation類型,它們被用來提供對其他annotation類型作說明。

  • 這些類型和它們所支持的類在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)

    • @Target:用於描述註解的使用範圍(即被描述的註解可以用在什麼地方)

    • @Retention:表示需要在什麼級別保存該註釋信息,用於描述註解的生命周期

      (SOURCE < CLASS < RUNTIME

    • @Document:說明該註解將包含在javaDoc中

    • @Inherited:說明子類可以繼承父類中的該註解

package ReflectDemo;

//測試元註解

import java.lang.annotation.*;

@MyAnnotation()
public class Test02 {
    public void test(){

    }
}

// 定義一個註解
// Target  表示我們的註解可以用在哪些地方
@Target(value = {ElementType.METHOD , ElementType.TYPE})

//  Retention  表示我們的註解在什麼地方還有效
//  runtime > class > sources
@Retention(value = RetentionPolicy.RUNTIME)

// Documented  表示是否將我們的註解生成在JavaDoc中
@Documented

// Inherited  子類可以繼承父類的註解
@Inherited

@interface MyAnnotation{

}

4. 自定義註解

​ 使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation介面。

  • @interface 用來聲明一個註解,格式:public @interface 註解名
  • 其中的每一個方法實際上是聲明瞭一個配置參數
  • 方法的名稱就是參數的名稱
  • 返回值的類型就是參數的類型(返回值只能是基本類型,Class,String,enum )
  • 可以通過default來聲明參數的預設值
  • 如果只有一個參數成員,一般參數名為value
  • 註解元素必須要有值,我們定義註解元素時,經常使用空字元串、0作為預設值
//自定義註解
public class Test03 {
    //註解可以顯式賦值,如果沒有預設值,我們就必須給註解賦值
    @MyAnnotation2(age = 18, name = "Jack")
    public void test(){}

    @MyAnnotation3("John")
    public void test2(){}
}


@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //註解的參數 : 參數類型 + 參數名 ();
    String name() default "";
    int age();
    int id() default -1;  //如果預設值為-1,代表不存在
    String[] schools() default {"北京大學", "清華大學"};
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    String value();
}

二.反射

1、靜態語言 VS 動態語言

動態語言:

  • 是一類在運行時可以改變其結構的語言:例如新的函數、對象、甚至代碼可以被引進,已有的函數可以被刪除或是其他結構上的變化。通俗點說就是在運行時代碼可以根據某些條件改變自身結構。
  • 主要動態語言:Object-C、C#、JavaScript、PHP、Python等。

靜態語言:

  • 與動態語言相對應的,運行時結構不可變的語言就是靜態語言,如Java、C、C++。
  • Java不是動態語言,但Java可以稱之為“準動態語言”。即Java有一定的動態性,我們可以利用反射機制獲得類似動態語言的特性。Java的動態性讓編程的時候更加靈活。

2、Java Reflection

  • Reflection(反射)是Java被視為動態語言的關鍵,反射機制允許程式在執行期間藉助於Reflection API取得任何類的內部信息並能直接操作任意對象的內部屬性及方法

    Class c = Class.forName("java.lang.String")
    
  • 載入完類之後,在堆記憶體的方法區中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象就包含了完整的類的結構信息。我們可以通過這個對象看到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象地稱之為:反射。

     正常方式: 引入需要的“包類”名稱 --> 通過new實例化  --> 取得實例化對象
    
     反射方式: 實例化對象 --> getClass()方法  --> 得到完整的“包類”名稱
    

3、Java反射機制提供的功能

  • 在運行時判斷任意一個對象所屬的類
  • 在運行時構造任意一個類的對象
  • 在運行時判斷任意一個類所具有的成員變數和方法
  • 在運行時獲取泛型信息
  • 在運行時調用任意一個對象的成員變數和方法
  • 在運行時處理註解
  • 生成動態代理
  • ......

4、Java反射優點和缺點

優點:

  • 可以實現動態創建對象和編譯,體現出很大的靈活性

缺點:

  • 對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求。這類操作總是慢於直接執行相同的操作。

5、反射相關的主要API

  • java.lang.Class:代表一個類
  • java.lang.reflect.Method:代表類的方法
  • java.lang.reflect.Filed:代表類的成員變數
  • java.lang.reflect.Constructor:代表類的構造器
  • ......

6、 Class類

在Object類中定義了以下的方法,此方法將被所有子類繼承:

public  final  Class getClass(){};

以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂的反射從程式的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱。

public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通過反射獲取類的Class對象
        Class c1 = Class.forName("ReflectDemo.User");
        System.out.println(c1);
        Class c2 =Class.forName("ReflectDemo.User");
        Class c3 = Class.forName("ReflectDemo.User");
        //一個類在記憶體中只有一個Class對象
        //一個類被載入後,類的整個結構都會被封裝在Class對象中
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
    }
}
//實體類 pojo
class User {
    private int id;
    private String name;
    public User() {
    }
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

​ 對象“照鏡子”後可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現了哪些介面。

​ 對於每個類而言,JRE都為其保留一個不變的Class類型的對象。一個Class對象包含了特定某個結構( class / interface / enum / annotation / primitive type / void / [] )的有關信息。

  • Class本身也是一個類
  • Class對象只能由系統建立對象
  • 一個載入的類在JVM中只會有一個Class實例
  • 一個Class對象對應的是一個載入到JVM中的一個.class文件
  • 每個類的實例都會記得自己是由哪個Class實例所生成
  • 通過Class可以完整地得到一個類中的所有被載入的結構
  • Class類是Reflection的根源,針對任何你想動態載入、運行的類,唯有先獲得相應的Class對象

6.1、Class類的常用方法

6.2、獲取Class類的實例(重要,面試常問)

  1. 若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠,程式性能最高。

    Class  clazz = Person.class;
    
  2. 已知某個類的實例,調用該實例的getClass()方法獲取Class對象

    Class  clazz = person.getClass();
    
  3. 已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取,可能拋出ClassNotFoundException

    Class  clazz = Class.forName("demo01.Student");
    
  4. 內置基本數據類型可以直接用 類名.Type

  5. 還可以利用ClassLoader(瞭解)

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("The person is : " + person.name);

        //方式一:通過對象獲得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        //方式二:forName獲得
        Class c2 = Class.forName("ReflectDemo.Student");
        System.out.println(c2.hashCode());

        //方式三:通過類名.class獲得
        Class<Student> c3 = Student.class;
        System.out.println(c3.hashCode());

        //方式四:基本內置類型的包裝類都有一個Type屬性
        Class c4 = Integer.TYPE;
        System.out.println(c4);

        //獲得父類類型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
class Person{
     int id;
     String name;

    public Person() {
    }

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}


class Student extends Person{
    public Student(){
        this.name = "student";
    }
}
class Teacher extends Person{
    public Teacher(){
        this.name = "teacher";
    }

}

哪些類型可以有Class對象?

  • class:外部類,成員(成員內部類,靜態內部類),局部內部類,匿名內部類

  • interface:介面

  • [] :數組

  • enum:枚舉

  • annotation:註解@interface

  • primitive type :基本數據類型

  • void

//所有類型的Class
public class Test03 {
    public static void main(String[] args) {
        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 c8 = void.class;  //void
        Class c9 = Class.class;  //Class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        //只要元素類型與維度一樣,就是同一個class
        int[] a = new int[10] ;
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }
}

6.3、類的載入和初始化

Java記憶體分配

類的載入過程(瞭解)

當程式主動使用某個類時,如果該類還未被載入到記憶體中,則系統會通過如下三個步驟來對該類進行初始化:

  • 載入:將class文件位元組碼內容載入到記憶體中,並將這些靜態數據轉換成方法區的運行時數據結構,然後生成一個代表這個類的java.lang.Class對象

  • 鏈接:將Java類的二進位代碼合併到JVM的運行狀態之中的過程。

    • 驗證:確保載入的類信息符合JVM規範,沒有安全方面的問題
    • 準備:正式為類變數(static)分配記憶體並設置類變數預設初始值的階段,這些記憶體都將在方法區中進行分配
    • 解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程
  • 初始化:

    • 執行類構造器()方法的過程。類構造器()方法是由編譯期自動收集類中所有類變數的賦值動作和靜態代碼塊中的語句合併產生的。(類構造器是構造類信息的,不是構造該類對象的構造器)

    • 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化

    • 虛擬機會保證一個類的()方法在多線程環境中被正確加鎖和同步

public class Test04 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(A.m);
    }
}

/*
    1.載入到記憶體,會產生一個類對應Class對象
    2.鏈接,鏈接結束後 m = 0
    3.初始化
    <clinit>(){
        System.out.println("A類靜態代碼塊初始化");
        m = 300;
        m = 100;
    }
      最終: m = 100
 */

class A{
    static{
        System.out.println("A類靜態代碼塊初始化");
        m = 300;
    }

    static int m = 100;

    public A(){
        System.out.println("A類的無參構造函數初始化");
    }
}

什麼時候會發生類初始化?(重要)

  • 類的主動引用(一定會發生類的初始化)

    • 當虛擬機啟動,先初始化main()方法所在的類
    • new一個類的對象
    • 調用類的靜態成員(除了final常量)和靜態方法
    • 使用java.lang.reflect包的方法對類進行反射調用
    • 當初始化一個類,如果其父類沒有被初始化,則先會初始化它的父類
  • 類的被動引用(不會發生類的初始化)

    • 當訪問一個靜態域時,只有真正聲明這個域的類才會被初始化。如:當通過子類引用父類的靜態變數,不會導致子類初始化
    • 通過數組定義類引用,不會觸發此類的初始化
    • 引用常量不會觸發此類的初始化(常量在鏈接階段就存入調用類的常量池中了)
public class Test05 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.主動引用
        // Son son = new Son();

        //2.反射也會產生主動引用
        // Class.forName("ReflectDemo.Son");

        //不會產生類的引用的方法
        //System.out.println(Son.b);
        //Son[] sons = new Son[5];
        //System.out.println(Son.M);

    }
}

class Father{
    static int b = 2;
    static {
        System.out.println("父類被載入...");
    }

}

class Son extends Father{
    static {
        System.out.println("子類被載入...");
        m = 300;
    }
    static int m = 100;
    static final int M = 1;
}

類載入器

  • 類載入器的作用:將class文件位元組碼內容載入到記憶體中,並將這些靜態數據轉換成方法區的運行時數據結構,然後在堆中生成一個代表這個類的java.lang.Class對象,作為方法區中類數據的訪問入口。
  • 類緩存:標準的JavaSE類載入器可以按要求查找類,但一旦某個類被載入到類載入器中,它將維持載入(緩存)一段時間。不過JVM垃圾回收機制可以回收這些Class對象

類載入器作用是用來把類(class)裝在進記憶體的。JVM規範定義瞭如下類型的類的載入器:

public class Test06 {
    public static void main(String[] args) throws ClassNotFoundException {

        //獲取系統類的載入器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //獲取系統類載入器的父類載入器  -->  擴展類載入器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        
        //獲取擴展類載入器的父類載入器 --> 根載入器( C/C++ )
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);

        //測試當前類是哪個載入器載入的
        System.out.println(Class.forName("ReflectDemo.Test06").getClassLoader());

        //測試JDK內置的類是誰載入的
        System.out.println(Class.forName("java.lang.Object").getClassLoader());

        //如何獲得系統類載入器可以載入的路徑
        System.out.println(System.getProperty("java.class.path"));
    }
}

獲取運行時類的完整結構

​ 通過反射可以獲取運行時類的完整結構:Filed、Method、Constructor、Superclass、Interface、Annotation

  • 實現的全部介面
  • 所繼承的父類
  • 全部的構造器
  • 全部的方法
  • 全部的Field
  • 註解
  • ......
//通過反射獲得類的信息
public class Test07 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class c1 = Class.forName("ReflectDemo.User");

        //獲得類的名字
        System.out.println(c1.getName());  //獲得包名 + 類名
        System.out.println(c1.getSimpleName());  //獲得類名

        //獲得類的屬性
        System.out.println("========");
        Field[] fields = c1.getFields();  //只能找到public屬性
        for (Field field : fields) {
            System.out.println(field);
        }

        fields = c1.getDeclaredFields();  //找到全部的屬性,包括私有屬性
        for (Field field : fields) {
            System.out.println(field);
        }

        //獲得類的方法
        System.out.println("========");
        Method[] methods = c1.getMethods();  //獲得本類及其父類的全部public方法
        for (Method method : methods) {
            System.out.println("getMethods : " + method);
        }

        methods = c1.getDeclaredMethods();  //獲得本類的所有方法
        for (Method method : methods) {
            System.out.println("getDeclaredMethods : " + method);
        }

        //獲得指定方法  (需要傳遞參數類型,方便有方法重載時定位方法)

        Method getName = c1.getMethod("getName",null);
        Method setName = c1.getMethod("setName",String.class);

        System.out.println(getName);
        System.out.println(setName);

        //獲得構造器
        System.out.println("========");
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("getConstructors : " + constructor);
        }

        constructors = c1.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("getDeclaredConstructors : " + constructor);
        }

        //獲得指定的構造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(int.class,String.class );
        System.out.println(declaredConstructor);

    }
}

6.4、通過反射創建類的對象

(1)調用Class對象的newInstance()方法,要求如下:

  • 類必須有一個無參數的構造器;
  • 類的構造器的訪問許可權需要足夠。

(2)如果沒有無參構造器或者訪問許可權不足,則可以通過獲取構造器來創建對象。

通過構造器創建對象,步驟如下:

  1. 通過Class類的getDeclaredConstructor( Class ... parameterTypes )取得本類的指定形參類型的構造器
  2. 向構造器的形參中傳遞一個對象數組進去,裡面包含了構造器中所需的各個參數
  3. 通過Constructor實例化對象

調用指定的方法

​ 通過反射,調用類中的方法,通過Method類完成

  1. 通過Class類的getMethod(String name, Class ... parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型;

  2. 使用Object invoke(Object obj, Object[] args) 進行調用,並向方法中傳遞要設置的obj對象的參數信息。

Object invoke ( Object obj, Object ... args )

  • Object對應原方法的返回值,若原方法無返回值,此時返回null
  • 若原方法為靜態方法,此時形參Object obj 可為null
  • 若原方法形參列表為空,則Object[] args 為null
  • 若原方法聲明為private,則需要在調用此invoke方法前,顯示調用方法對象的setAccessible(true)方法,將可訪問private的方法。
public class Test08 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //獲得Class對象
        Class c1 = Class.forName("ReflectDemo.User");

        //構造一個對象
        User user = (User)c1.newInstance();
        System.out.println(user);

        //通過構造器創建對象
        System.out.println("========");
        Constructor constructor = c1.getDeclaredConstructor(int.class, String.class);
        System.out.println(constructor.newInstance(3, "Lily"));

        //通過反射調用普通方法
        System.out.println("========");
        User user2 = (User)c1.newInstance();
        //通過反射獲取一個方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        setName.invoke(user2,"May");
        System.out.println(user2.getName());

        //通過反射操作屬性
        System.out.println("========");
        User user3 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        name.setAccessible(true);  //不能直接操作私有屬性,我們需要關閉程式的安全監測  屬性或者方法的setAccessible(true)
        name.set(user3,"Jack");
        System.out.println(user3.getName());

    }
}

創建對象的效率
創建對象的效率排序如下:類的實例化方式創建 >> 通過反射的方式創建(setAcccessible(true);) > 通過反射的方式創建。

6.5、反射操作泛型(瞭解即可)

  • Java採用泛型擦除的機制來引入泛型,Java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換問題。但是,一旦編譯完成,所有和泛型有關的類型全部擦除
  • 為了通過反射操作這些類型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類型來代表不能被歸一到Class類中的類型但是又和原始類型齊名的類型
  • ParameterizedType:表示一種參數化類型,比如 Collection
  • GenericArrayType:表示一種元素類型是參數化類型或者類型變數的數組類型
  • TypeVariable:是各種類型變數的公共父介面
  • WildcardType:代表一種通配符類型表達式

6.6、反射操作註解(重要,框架基礎)

//反射操作註解
public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("ReflectDemo.Stud");

        //通過反射獲得註解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //獲得註解的value的值
        TableAnnotation annotation = (TableAnnotation) c1.getAnnotation(TableAnnotation.class);
        String value = annotation.value();
        System.out.println(value);

        //獲得類指定的註解
        Field name = c1.getDeclaredField("name");
        FiledAnnotation annotation1 = name.getAnnotation(FiledAnnotation.class);
        System.out.println(annotation1.columnName());
        System.out.println(annotation1.type());
        System.out.println(annotation1.length());
    }
}


@TableAnnotation("db_student")
class Stud{
    @FiledAnnotation(columnName ="db_id",type = "int", length = 10)
    private int id;
    @FiledAnnotation(columnName = "db_name",type = "varchar",length = 10)
    private String name;
    @FiledAnnotation(columnName = "db_age", type = "int" , length = 3)
    private int age;

    public Stud() {
    }

    public Stud(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Stud{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


//類名的註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableAnnotation{
    String value();
}

//屬性的註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledAnnotation{
    String columnName();
    String type();
    int length();
}

(補充)6.7、雙親委派機制

什麼是雙親委派機制?
雙親委派機制是當類載入器需要載入某一個.class位元組碼文件時,則首先會把這個任務委托給他的上級類載入器,遞歸這個操作,如果上級沒有載入該.class文件,自己才會去載入這個.class。這是一種任務委派模式。
雙親委派機制原理:
如果一個類載入器收到了要載入某個類的請求,它要做的首要事情不是載入,而是將這個請求委托給父類的載入器去執行
如果父類載入器還存在其父類載入器,則進一步向上委托,依次遞歸,請求最終將到達頂層的啟動類載入器;
如果父類載入器可以完成類載入任務,就成功返回,倘若父類載入器無法完成此載入任務,子載入器才會嘗試自己去載入
如果最後沒有任何載入器能載入,則報錯"ClassNotFoundException"。
原理圖示:

本博客為博主在學習B站up主狂神關於反射和註解相關課程的學習筆記,博客中大部分內容為課上ppt內容的整理和總結。
在此附上狂神在b站的視頻鏈接(講的是真的好,幫忙宣傳)
關於雙親委派機制的內容,在此附上參考博客地址:雙親委派機制 詳解(手畫詳圖)面試高頻 你值得擁有!!!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前提 小白一個,啥都不會,歡迎指點。 題目 隨機生成10個整數(1-100的範圍),保存到數組,並倒序列印以及求平均值,求最大值和最大值的下標,並查找裡面知否有8。 思路 隨機生成-->採用random(),註意範圍在( 1-100) 。 求取最大值下標插入索引 在再次建立一個索引,以此判斷隨機生成 ...
  • JavaSE:多線程學習 01 初識進程 1.1 Process & Thread 1、首先簡要介紹程式。程式是指令和數據的有序集合,其本身沒有任何運行的含義,只是一個靜態的概念。 2、進程則是執行程式的一次執行過程,是一個動態的概念。是系統資源分配的單位。 3、通常在一個進程中可以包含若幹線程。線 ...
  • Swift 備忘清單 IT寶庫整理的Swift開發速查清單,該清單提供了使用 Swift 的示例,涵蓋 Swift 基礎知識、控制流、類型、結構/類、運算符、函數方法等。入門,為開發人員分享快速參考備忘單。 開發速查表大綱 入門 變數 類型註釋 算術運算符 字元串插值 多行字元串 代碼註釋 組成一個 ...
  • 本文介紹基於Python中ArcPy模塊,對大量柵格遙感影像文件進行批量掩膜與批量重採樣的操作。 首先,我們來明確一下本文的具體需求。現有一個存儲有大量.tif格式遙感影像的文件夾;且其中除了.tif格式的遙感影像文件外,還具有其它格式的文件。 我們希望,依據一個已知的面要素矢量圖層文件,對上述文件 ...
  • aliases: [] tags : " #QA #Java " summary: [POI生成Excel超出的單元格樣式的最大數量] author : [yaenli] notekey: [20230322-100908] 問題現象 使用Apache POI生成Excel時,如果創建的單元格樣式過 ...
  • Tomcat生命周期管理 各種組件如何統一管理 Tomcat的架構設計是清晰的、模塊化、它擁有很多組件,加入在啟動Tomcat時一個一個組件啟動,很容易遺漏組件,同時還會對後面的動態組件拓展帶來麻煩。如果採用我們傳統的方式的話,組件在啟動過程中如果發生異常,會很難管理,比如你的下一個組件調用了sta ...
  • Tcl語言線上運行編譯,是一款可線上編程編輯器,在編輯器上輸入Tcl語言代碼,點擊運行,可線上編譯運行Tcl語言,Tcl語言代碼線上運行調試,Tcl語言線上編譯,可快速線上測試您的Tcl語言代碼,線上編譯Tcl語言代碼發現是否存在錯誤,如果代碼測試通過,將會輸出編譯後的結果。 該線上工具由IT寶庫提 ...
  • 直入主題: Q1:為什麼要用分散式鎖? 在分散式系統中,多個進程或線程可能會同時訪問共用資源,這可能會導致數據不一致、併發性問題、性能下降等問題。為瞭解決這些問題,我們通常會使用分散式鎖來協調多個進程或線程對共用資源的訪問。 分散式鎖是一種協調機制,它通過在共用資源上設置鎖來防止多個進程或線程同時訪 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...