day26【反射_註解】課上

零.類的加載時機

在這裏插入圖片描述

一.類加載器

1.概述:
    在jvm中,負責將本地上的class文件加載到內存的對象
2.分類:
      - BootstrapClassLoader 根類加載器-->C語言寫的,我們獲取不到
      ​	也被稱爲引導類加載器,負責Java核心類的加載
      ​	比如System,String等。
        jre/lib/rt.jar下的類都是核心類 
     ---------------------------------     

      - ExtClassLoader 擴展類加載器

      ​	負責JRE的擴展目錄中jar包的加載。
      ​	在JDK中JRE的lib目錄下ext目錄

      - AppClassLoader 系統類加載器
      ​	負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包(第三方jar包)和類路徑。或者自定義的類
       ----------------------------------------------------
        AppClassLoader的父加載器是ExtClassLoader
        ExtClassLoader的父加載器是BootstrapClassLoader
        但是他們不是子父類繼承關係,他們有一個共同的爹-->ClassLoader
       ---------------------------------------------------- 
          
 3.獲取加載器對象
    類名.class.getClassLoader
    
 4.雙親委派(全盤負責委託機制) 誰用誰加載
         a.Person中有一個String
           Person本身是AppClassLoader加載
           String是BootstrapClassLoader

         b.加載順序:
           Person本身是AppClassLoader加載,String本來按理來說是AppClassLoader
           但是AppClassLoader加載String的時候,會去問一問ExtClassLoader,,ext,你加載嗎?
           ext說:偶no,我不負責加載核心類,我負責的是擴展類,所以你別急,我給你找人(BootstrapClassLoader)
           ext說:boot,你加載String嗎?
           boot:,正好我加載核心類,行吧,我加載吧
-----------------------------------------------------------
           假如:ext和boot都不加載,app纔會自己加載
               class Test{
                   new Person()
               }
               
a.AppClassLoader負責加載Test,然後按道理來講Person也是由AppClassLoader加載到內存
  但是App先不加載,先去找ExtClassLoader,Ext不負責加載自定義類,Ext就去找Boot
  但是Boot只負責加載核心類,所以Boot也不加載
b.最後App看兩個雙親不加載,自己就加載了Person
-----------------------------------------------------------  
c.類加載器的cache(緩存)機制:如果cache中保存了這個類就直接返回它,如果沒有才加載這個類,然後存入cache中,下一次如果有其他類在使用的時候就不會在加載了,直接去cache緩存拿即可。這就是爲什麼每個類只加載一次,內存只有一份的原因。

舉例:還是上述代碼中,當第一次使用System類的時候,那麼System類就會被加載了,那麼System類就會存儲到內存中了,當下面代碼中我們再一次使用System類的時候,由於內存中已經有了,那麼就不會在去加載了,這時會直接拿過來用即可。

因此方法區中每一個類的字節碼文件只有一份的原因由全盤負責、委託機制和類加載器的cache(緩存)機制共同決定。也稱爲雙親委派機制
              
 5.雙親委派作用:(一個類在內存中加載幾次呢?->1)
    能夠讓Class類加載一次
    創建一個Class對象
/*
	類名.class.getClassLorder()這個類的加載器
 	ClassLorder類的方法 getParent()返回父加載器
*/
public class ClassLoader {
	
	@Test
	public void classLorder(){
		//三個類加載器的關係
		//父子關係
		ClassLoader c1 = ClassLoader.class.getClassLoader();
		System.out.println(c1);
		
		ClassLoader c2 = c1.getParent();
		System.out.println(c2);
		
		ClassLoader c3 = c2.getParent();
		System.out.println(c3);
	}
	
    //獲取AppClassLoader 系統類加載器
	@Test
	public void app(){
        //ClassLoader爲本類類名
		ClassLoader c1 = ClassLoader.class.getClassLoader();
		System.out.println(c1);
	}
	
    //獲取ExtClassLoader 擴展類加載器
	@Test
	public void ext(){
		ClassLoader c1 = DNSNameService.class.getClassLoader();
		System.out.println(c1);
	}
	 
    //BootstrapClassLoader 根類加載器
	@Test
	public void boot(){
		//引導類加載器,不是類,JVM內部,返回值null
		ClassLoader cl = String.class.getClassLoader();
		System.out.println(cl);
	}
}

二.反射

在這裏插入圖片描述

1.概述:根據Class對象操作Class對象中的成員

三.反射之獲取Class對象

獲取Class對象的方式:
      1.new對象 調用 getClass()->Object中的方法
      2.類名.class->class->每個數據類型,不管是基本的還是引用的,jvm都賦予了他們一個靜態屬性
                          名字就叫做class
      3.Class中方法->forName(String className)

      注意:
        class就加載一次,Class對象也就一個,3中方式獲取出來的Class是同一個

     ------------------------------------------------------------       
      問題:
         3種獲取Class對象的方式,哪個在開發中最常用:forName
             使用forName(Stirng className)->擴展性更好,靈活性更高

         因爲:參數是一個字符串,將來我們可以將類的全限定名放在配置文件中,然後
             用io流讀取,讀取出來的字符串(全限定名)可以當做參數放在forName中
             這樣,我們想獲取不同的Class對象,直接改文件名就可以了,不用修改
             可以代碼實現一下:
             a.在模塊下創建prop.properties
                  className=cn.itcast.day21.e_fanshe04.Student
               
             b.創建測試類:
            public class Demo02_ForName {
              public static void main(String[] args) throws Exception {
               //創建Properties集合
              Properties properties = new Properties();
              FileInputStream fis = new FileInputStream("day21\\pro.properties");
              properties.load(fis);

             //根據className獲取對應的value
             String className = properties.getProperty("className");

             //獲取value的class對象

             Class person = Class.forName(className);
             System.out.println(person);
    }
}            
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    private Person(String name){
        this.name = name;
        System.out.println("我是私有的構造");
    }
    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 name+"..."+age;
    }
}
//測試類
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //每個數據類型,不管基本還是引用數據類型,jvm都賦予了這個類型一個靜態屬性,名字叫class
        Class aClass = Person.class;
         
        Class aClass1 = Class.forName("cn.itcast.day15.class02.Person");

        //Object類中的getClass方法
        Person person = new Person();
        Class aClass2 = person.getClass();

        System.out.println(aClass==aClass1);
        System.out.println(aClass==aClass2);
    }
}

四.獲取Class對象中的構造方法

一.獲取所有public的構造方法

   反射通用的使用方式:
      1.獲取要操作類的class對象
      2.利用Class類中的方法獲取類中的成員(構造,變量,方法)
      3.運行獲取出來的成員

   獲取Class對象中的構造方法:
      Constructor<?>[] getConstructors()->獲取所有的構造方法(public修飾的)
public class Demo01_Constructor {
    public static void main(String[] args) throws Exception{
        //獲取Person類的Class對象
        Class pClass = Class.forName("cn.itcast.day20.fanshe02.Person");
        Constructor[] constructors = pClass.getConstructors();
        //遍歷數組
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

二.獲取空參構造

       1.獲取指定的構造方法:
            Constructor<T> getConstructor(Class<?>... parameterTypes):獲取指定public的構造
                parameterTypes:需要傳遞參數類型的class對象
                如果獲取的是無參構造,參數不寫

       2.Constructor類中的方法
          T newInstance(Object...initargs)-->創建對象->new Person("柳巖",1)
            如果使用此方法創建的是無參構造,參數不用寫
            此方法相當於:new Person() 或者 new Person("柳巖",1)
 private static void method01() throws Exception {
        //獲取Person類的Class對象
        Class pClass = Class.forName("cn.itcast.day20.fanshe02.Person");

        //獲取無參構造
        Constructor constructor = pClass.getConstructor();

        Object o = constructor.newInstance();//Person p = new Person()
        System.out.println(o);
    }

三.利用空參構造創建對象的快捷方式

       1.利用空參構造創建對象的快捷方式
          直接調用Class類中的newInstance方法

         前提:被反射的類,必須具有public權限的無參構造
    private static void method02()throws Exception {
        //獲取Person類的Class對象
        Class pClass = Class.forName("cn.itcast.day20.fanshe02.Person");
        Object o = pClass.newInstance();
        System.out.println(o);
    }

四.利用反射獲取有參構造並創建對象

1.獲取指定的構造方法:
            Constructor<T> getConstructor(Class<?>... parameterTypes)
                parameterTypes:需要傳遞參數類型的class對象
                如果獲取的是無參構造,參數不寫

2.Constructor類中的方法
          T newInstance(Object...initargs)-->創建對象->new Person("柳巖",1)
            如果使用此方法創建的是無參構造,參數不用寫
public class Demo03_Constructor {
    public static void main(String[] args) throws Exception{
        //獲取Person類的Class對象
        Class pClass = Class.forName("cn.itcast.day20.fanshe02.Person");

        //獲取有參構造
        Constructor constructor = pClass.getConstructor(String.class, int.class);
        System.out.println(constructor);

        //創建對象
        //Person p = new Person("柳巖", 36)
        Object o = constructor.newInstance("柳巖", 36);
        System.out.println(o);
    }

}

五.利用反射獲取私有構造(暴力反射)

獲取私有的構造(擴展):
        Constructor<?>[] getDeclaredConstructors()->獲取所有的構造,包括私有的

        Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)  ->獲取指定的構造

        AccessibleObject類中的方法-->暴力反射
            void setAccessible(boolean flag)
                              flag:false->代表不能訪問私有的成員
                              flag:true->解除私有權限
public class Demo04_Constructor {
    public static void main(String[] args) throws Exception{
        //獲取Person類的Class對象
        Class pClass = Class.forName("cn.itcast.day20.fanshe02.Person");

        //method01(pClass);

        method02(pClass);
    }

    private static void method02(Class pClass)throws Exception {
        //獲取私有的構造方法
        Constructor dds = pClass.getDeclaredConstructor(String.class);

        //解除私有權限
        dds.setAccessible(true);

        //創建對象
        Object o = dds.newInstance("郭磊");
        System.out.println(o);
    }

    /*
       獲取所有的構造->包括私有
     */
    public static void method01(Class pClass){
        Constructor[] dds = pClass.getDeclaredConstructors();
        //遍歷數組
        for (Constructor dd : dds) {
            System.out.println(dd);
        }
    }

}

六.利用反射獲取所有成員方法

利用反射獲取類中的方法:
        Method[] getMethods()獲取所有的方法,public
public class Demo05_Method {
    public static void main(String[] args)throws Exception {
        Class c = ClassUtils.getC();//抽出來的工具類可以不抽取
        Method[] methods = c.getMethods();
        //遍歷數組
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

七.反射之獲取方法(有參,無參)

 利用反射獲取類中的方法:
        Method[] getMethods()獲取所有的方法,public

        Method getMethod(String name,Class<?>...parameterTypes)
                         name:獲取的方法名
                         parameterTypes:該方法的參數類型

         Method中有一個方法
            Object invoke(Object obj, Object... args)
                          obj:反射的類的對象
                          args:運行方法傳遞的實參

            如果執行的方法是void,調用invoke方法沒必要必須用返回值接收
            如果執行的方法沒有參數,那麼args不用寫
public class Demo06_Method {
    public static void main(String[] args)throws Exception {
        Class c = ClassUtils.getC();
        System.out.println("---------獲取setName方法-------------");
        //獲取set方法
        Method setName = c.getMethod("setName", String.class);

        //利用空參構造創建Person對象
        Object o = c.newInstance();//Person o = new Person()

        //調用方法
        setName.invoke(o, "柳巖");
        System.out.println(o);

        System.out.println("---------獲取getName方法-------------");

        Method getName = c.getMethod("getName");
        //執行getName方法,有返回值的
        Object invoke = getName.invoke(o);
        System.out.println(invoke);
    }
}

五.反射練習(編寫一個小框架)

利用反射,解析配置文件中的信息
   文件中配置的信息是
      類的全限定名   className=cn.itcast.day20.fanshe03_test.Person
      類中的某一個方法名  methodName=eat


   步驟:
      1.創建配置文件->properties
        存的信息鍵值對的形式

        問題1:配置文件放在哪裏?放在src下->切記
        問題2:項目開發完,交給用戶使用,給用戶的是編譯後的class文件,而out目錄存放的就是class文件
        問題3:如果我們將配置文件放在模塊下,out目錄下是沒有配置文件的,那麼代碼運行需要讀配置文件的信息
             所以給了用戶,用戶一執行,,報錯了,因爲沒讀到配置文件的信息
            
        解 決:如果將配置文件放在src下面,idea生成的out目錄中這個配置文件會顯示在out的項目路徑下
            
            
        注意的是:src 存放的是源代碼 編譯後產生的class文件,是同步的,但是生成的out路徑,下沒有src這個目錄,因爲src存放源代碼的

         問題:如何讀取src目錄下的文件?
              直接new FileInputStream(模塊名\\src\\配置文件名)是不行的
              因爲這樣寫,而我們給的用戶是out下的資源,而out下存放的class文件路徑是沒有src
              所以這樣寫,給了用戶,用戶一使用,直接就讀不到這個配置文件了

         解決:
           使用類的加載器
              ClassLoader類中的方法
                 InputStream getResourceAsStream("直接寫文件名")返回一個字節輸入流
                 此流會自動掃描src下的配置文件

      2.利用IO流讀取配置文件
        讀到Properties集合中

      3.獲取Properties中對應的值
         獲取類的全限定名
         方法名
      4.利用反射獲取類的Class對象
        利用反射去指定方法
//在src下創建一個pro.properties文件
className=cn.itcast.day15.class02.Person
methodName=eat
public class Test {
    public static void main(String[] args)throws Exception{
        //獲取類加載器
        ClassLoader classLoader = Test.class.getClassLoader();
        //使用類加載器的輸入流讀取配置文件
        InputStream in = classLoader.getResourceAsStream("config.properties");
        //創建Properties集合
        Properties properties = new Properties();
        //調用load方法,將流中的配置文件讀取到Properties集合中
        properties.load(in);
        //獲取配置文件中的value
        String className = properties.getProperty("className");//cn.itcast.day20.fanshe03_test.Person
        String methodName = properties.getProperty("methodName");//eat

        //根據獲取出來的className創建Person的Class對象
        Class aClass = Class.forName(className);

        //根據從配置文件中解析出來的方法名去獲取Person中的方法
        Method method = aClass.getMethod(methodName);

        //創建對象
        Object o = aClass.newInstance();
        //執行方法
        method.invoke(o);
    }
}

六.註解

一.註解的介紹

 1.jdk1.5版本的新特性->一個引用數據類型
       和類,接口,枚舉是同一個層次的
 2.作用:
        說明:對代碼進行說明,生成doc文檔(API文檔)(不會用)
        檢查:檢查代碼是否有錯誤   @Override(會用)
        分析:對代碼進行分析,起到了代替配置文件的作用(會用)

 3.JDK中的註解:
        @Override  ->  檢測此方法是否爲重寫方法
           jdk1.5版本,支持父類的方法重寫
           jdk1.6版本,支持接口的方法重寫
        @Deprecated -> 方法已經過時,不推薦使用
                       調用方法的時候,方法上會有橫線,但是能用
        @SuppressWarnings->消除警告  @SuppressWarnings("all")

二.註解的定義以及屬性的定義格式

   1.自定義註解格式:
      修飾符 @interface 註解名{
          屬性
      }
   2.屬性的定義格式:爲了提高註解作用
     - 格式1:數據類型 屬性名();-->沒有默認值的-->需要後面賦值
     - 格式2:數據類型 屬性名() default 默認值;-->可以改變的

   3.註解中能夠定義什麼樣的屬性
     - 八種基本數據類型(int,float,boolean,byte,double,char,long,short)- String類型,Class類型,枚舉類型,註解類型。
     - 以上所有類型的一維數組。int[][] arr = {{1,2},{3,4}}     [0][0]    [1,0]->3
public @interface Book {
    //書名
    String bookName();
    //價格
    double price();
    //作者
    String[] author();

    //數量
    //Integer count();錯誤,不能使用包裝類
    //int count() default 10;這個是可以的,定義屬性的時候可以指定默認值
}

三.註解的使用

//註解的使用
/*
   使用註解,就是爲註解的屬性賦值
   格式:
      @註解名(屬性名=值,屬性名=值)
   如果屬性是數組
   數組的賦值-->屬性名 = {"曹雪芹","高鶚"}
   
   註解,可以寫在類上,方法上,參數位置都行
*/
@Book(booName = "紅樓夢",price = 200.9,author = {"曹雪芹","高鶚"})
pubilc class BookShelf{
    
}
註解注意事項:
      1.空註解可以直接使用
      2.一個對象中不能連續使用同一個註解多次,但是一個對象中可以使用多個不同的註解
       (不同的位置可以使用一樣的註解,但是同樣的位置不能使用一樣的註解)
      3.使用註解時,如果此註解中有屬性,註解中的屬性一定要賦值,如果有多個屬性,,隔開
        如果註解中的屬性有數組,那麼如果數組只有一個元素值,那麼{}不用寫,反之用寫
      4.如果註解中的屬性值有默認值,那麼我們不必要寫,也不用重新賦值,反之必須寫上
      5.如果註解中只有一個屬性,並且屬性名叫value,那麼使用註解的時候,屬性名不用寫

四.註解解析的方法

註解解析:獲取註解中的屬性值

    接口:AnnotatedElement接口中的方法(用於解析註解的接口)
       boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
          判斷當前class對象是否有指定的註解
          返回值:boolean  返回的是true,證明該class對象上有註解;返回的是false,證明該class對象沒有註解

          Class<? extends Annotation> annotationClass
          傳遞的是class對象, 傳遞的其實就是該class對象上對應註解的class對象

       <T extends Annotation> getAnnotation(Class<T> annotationClass)->獲得當前class對象上指定的註解對象。

       解釋:
         參數傳遞的是註解的class對象
         傳遞哪個註解類型,返回的就是哪個註解對象


       AnnotatedElement畢竟是個接口,你有實現類
         實現類:Class類  Constructor構造方法 Field成員變量  Method成員方法
         結論:註解的解析,和哪個技術密切相關--> 反射

      需求:獲取BookShelf1類上的註解Book的屬性值
 =====================================================
    註解的解析思想:
       1.反射帶有註解的類
       2.判斷這個類上是否有註解
       3.獲取這個註解
       4.獲取註解中的屬性值

     ------------------
     假如我們要是解析方法上的註解屬性值
       1.反射帶有註解的類
       2.反射方法->getMethod-->Method
       3.判斷方法上是否有註解
       4.獲取這個註解
       5.獲取註解的屬性值
@Book(booName = "紅樓夢",price = 200.9,author = {"曹雪芹","高額"})
pubilc class BookShelf{
    
}


/*
 運行之後發現沒有輸出,而且c.isAnnotationPresent(Book.class)判斷爲false,
 明明類上有註解,但是卻返回false,爲啥呢?那麼我們接下來學習一下元註解
*/
public class Test01 {
    public static void main(String[] args)throws Exception {
        //1.反射帶有註解得類
        Class c = Class.forName("cn.itcast.day21.zhujie02.BookShelf01");
        //2.判斷這個類上是否有註解
        boolean b = c.isAnnotationPresent(Book.class);
        System.out.println(b);
        if (b){
            //如果有註解,獲取註解
            //Annotation annotation = c.getAnnotation(Book.class);
            Book book = (Book) c.getAnnotation(Book.class);
            //獲取註解中的屬性值
            System.out.println("書名:"+book.bookName());
            System.out.println("價格:"+book.price());
            System.out.println("作者:"+ Arrays.toString(book.author()));
        }

    }
}

七.元註解

**
 * 自定義的註解 Book
 * 定義屬性
 *
 * JDK的元註解,比喻註解的總管
 * 管理其他的註解
 *
 * 元註解對我們的註解進行控制
 * 1: 控制我們的註解,可以寫在哪裏,(,方法,變量上,...)
 * 2: 控制我們的註解的生命週期
 *
 * JDK的2個元註解
 *
 * @Target  指示其他註解,出現的位置->點進Target底層
 *    ElementType[] value(); 數組,可以賦值多個->點ElementType底層
 *    ElementType是數據類型,是枚舉
 *    枚舉的屬性,都是靜態修飾,直接類名調用
 *      TYPE, 其他註解可以寫在類上
 *      FIELD,其他註解可以寫在成員變量
 *      METHOD,其他註解可以寫在方法上
 *      PARAMETER,其他註解可以寫在方法參數上
 *      CONSTRUCTOR,其他註解可以寫在構造方法上
 *
 *  @Retention 指示其他註解的生命週期->點到Retention底層
 *     RetentionPolicy value(); 不是數組,賦值一個->點到RetentionPolicy底層
 *     RetentionPolicy數據類型
 *     枚舉的屬性,都是靜態修飾,直接類名調用
 *     SOURCE(默認級別) 註解僅存在於源碼中java文件中(不在class文件中,也不在方法區中)->@Override只是檢                        測方法是否爲重寫方法
 *     CLASS  註解存在於編譯後的class文件中->Class文件中出現了,方法區中沒有
 *     RUNTIME 運行時期的內存中-->方法區中出現了,一旦在方法區中出現了,我們才能利用反射獲取到註解
     
       所以當我們在註解上寫SOURCE 運行上面的Test案例判斷類上有沒有註解,纔會返回false
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    //書名
    String bookName();
    //價格
    double price() ;
    //作者
    String[] author();
}

------------------------------------
@Book(booName = "紅樓夢",price = 200.9,author = {"曹雪芹","高額"})
pubilc class BookShelf{
    @Book(booName = "紅樓夢",price = 200.9,author = {"曹雪芹","高額"})
    public void lookBook(){
        System.out.println("看書");
    }
}

八.註解再次解析

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    String bookName();
    double price();
    String[] author();
}

@Book(booName = "紅樓夢",price = 200.9,author = {"曹雪芹","高鶚"})
pubilc class BookShelf{
    
}
=================================================================
public class Test01 {
    public static void main(String[] args)throws Exception {
        //1.反射帶有註解得類
        Class c = Class.forName("cn.itcast.day21.zhujie03.BookShelf");
        //2.判斷這個類上是否有註解
        boolean b = c.isAnnotationPresent(Book.class);
        System.out.println(b);
        if (b){
            //如果有註解,獲取註解
            //Annotation annotation = c.getAnnotation(Book.class);
            Book book = (Book) c.getAnnotation(Book.class);
            //獲取註解中的屬性值
            System.out.println("書名:"+book.bookName());
            System.out.println("價格:"+book.price());
            System.out.println("作者:"+ Arrays.toString(book.author()));
        }

    }
}

九.模擬Junit練習

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
  /*
   使用@MyTest註解
   哪個方法上有@MyTest我們就讓哪個方法執行
  */
public class Test01 {
    @MyTest
    public void method01(){
        System.out.println("我是method01");
    }

    public void method02(){
        System.out.println("我是method02");
    }

    @MyTest
    public void method03(){
        System.out.println("我是method03");
    }

/*
       1.反射帶有註解的類
       2.反射方法->getMethod-->Method
       3.判斷方法上是否有註解
       4.獲取這個註解
       5.獲取註解的屬性值
 */
    public static void main(String[] args) throws Exception{
        //1.獲取本類的Class對象
        Class aClass = Test01.class;
        //創建對象
        Object o = aClass.newInstance();

        //2.獲取方法
        Method[] methods = aClass.getMethods();
//        3.判斷方法上是否有註解
        for (Method method : methods) {
            boolean b = method.isAnnotationPresent(MyTest.class);
            if (b){
               //如果方法上有註解,直接invoke執行
               method.invoke(o);
            }
        }
    }
}

十.Lombok(day28講解)

使用的註解有:
   @Data->包含get/set,toString,hashCode,equals,無參構造方法
   @Getter->生成get方法
   @Setter->生成set方法
   @NoArgsConstructor@AllArgsConstructor
   - @NoArgsConstructor:無參數構造方法。
   - @AllArgsConstructor:滿參數構造方法。
   @@EqualsAndHashCode->生成hashCode和Equals
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章