<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      雁過請留痕...
      代碼改變世界

      【轉(zhuǎn)】JAVA反射與注解

      2018-04-25 11:56  xiashengwang  閱讀(3188)  評論(1)    收藏  舉報

      轉(zhuǎn)載自:https://www.daidingkang.cc/2017/07/18/java-reflection-annotations/

      前言

      現(xiàn)在在我們構(gòu)建自己或公司的項目中,或多或少都會依賴幾個流行比較屌的第三方庫,比如:Butter KnifeRetrofit 2Dagger 2GreenDao等,如果你沒用過,那你需要找時間補(bǔ)一下啦;有時在使用后我們會好奇他們到底是怎么做到這種簡潔、高效、松耦合等諸多優(yōu)點的,當(dāng)然這里我不探討它們具體怎么實現(xiàn)的 (可以看看我之前寫的幾篇文章) ,而關(guān)心的是它們都用到同樣的技術(shù)那就是本篇所講的反射注解,并實現(xiàn)的依賴注入。

      閱讀本篇文章有助于你更好的理解這些大形框架的原理和復(fù)習(xí)Java的知識點。為什么要把反射放在前面講呢,實際上是因為我們學(xué)習(xí)注解的時候需要用到反射機(jī)制,所以,先學(xué)習(xí)反射有助于理解后面的知識。

      JAVA反射

      主要是指程序可以訪問,檢測和修改它本身狀態(tài)或行為的一種能力,并能根據(jù)自身行為的狀態(tài)和結(jié)果,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語義。

      反射機(jī)制是什么

      面試有可能會問到,這句話不管你能不能理解,但是你只要記住就可以了

      反射機(jī)制就是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機(jī)制。

      用一句話總結(jié)就是反射可以實現(xiàn)在運行時可以知道任意一個類屬性和方法

      反射機(jī)制能做什么

      反射機(jī)制主要提供了以下功能:

      • 在運行時判斷任意一個對象所屬的類;
      • 在運行時構(gòu)造任意一個類的對象;
      • 在運行時判斷任意一個類所具有的成員變量和方法;
      • 在運行時調(diào)用任意一個對象的方法;
      • 生成動態(tài)代理(ps:這個知識點也很重要,后續(xù)會為大家講到)

      Java 反射機(jī)制的應(yīng)用場景

      • 逆向代碼 ,例如反編譯
      • 與注解相結(jié)合的框架 例如Retrofit
      • 單純的反射機(jī)制應(yīng)用框架 例如EventBus
      • 動態(tài)生成類框架 例如Gson

      反射機(jī)制的優(yōu)點與缺點

      為什么要用反射機(jī)制?直接創(chuàng)建對象不就可以了嗎,這就涉及到了動態(tài)與靜態(tài)的概念

      • 靜態(tài)編譯:在編譯時確定類型,綁定對象,即通過。

      • 動態(tài)編譯:運行時確定類型,綁定對象。動態(tài)編譯最大限度發(fā)揮了java的靈活性,體現(xiàn)了多態(tài)的應(yīng)用,有以降低類之間的藕合性。

      優(yōu)點

      • 可以實現(xiàn)動態(tài)創(chuàng)建對象和編譯,體現(xiàn)出很大的靈活性,特別是在J2EE的開發(fā)中它的靈活性就表現(xiàn)的十分明顯。比如,一個大型的軟件,不可能一次就把把它設(shè)計的很完美,當(dāng)這個程序編譯后,發(fā)布了,當(dāng)發(fā)現(xiàn)需要更新某些功能時,我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如這樣的話,這個軟件肯定是沒有多少人用的。采用靜態(tài)的話,需要把整個程序重新編譯一次才可以實現(xiàn)功能的更新,而采用反射機(jī)制的話,它就可以不用卸載,只需要在運行時才動態(tài)的創(chuàng)建和編譯,就可以實現(xiàn)該功能。

      缺點

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

      理解Class類和類類型

      想要了解反射首先理解一下Class類,它是反射實現(xiàn)的基礎(chǔ)。
      類是java.lang.Class類的實例對象,而Class是所有類的類(There is a class named Class)
      對于普通的對象,我們一般都會這樣創(chuàng)建和表示:

      1
      Code code1 = new Code();

      上面說了,所有的類都是Class的對象,那么如何表示呢,可不可以通過如下方式呢:

      1
      Class c = new Class();

      但是我們查看Class的源碼時,是這樣寫的:

      1
      2
      3
      private  Class(ClassLoader loader) { 
      classLoader = loader;
      }

      可以看到構(gòu)造器是私有的,只有JVM可以創(chuàng)建Class的對象,因此不可以像普通類一樣new一個Class對象,雖然我們不能new一個Class對象,但是卻可以通過已有的類得到一個Class對象,共有三種方式,如下:

      1
      2
      3
      Class c1 = Code.class; 這說明任何一個類都有一個隱含的靜態(tài)成員變量class,這種方式是通過獲取類的靜態(tài)成員變量class得到的
      Class c2 = code1.getClass(); code1是Code的一個對象,這種方式是通過一個類的對象的getClass()方法獲得的
      Class c3 = Class.forName("com.trigl.reflect.Code"); 這種方法是Class類調(diào)用forName方法,通過一個類的全量限定名獲得

      這里,c1、c2、c3都是Class的對象,他們是完全一樣的,而且有個學(xué)名,叫做Code的類類型(class type)。
      這里就讓人奇怪了,前面不是說Code是Class的對象嗎,而c1、c2、c3也是Class的對象,那么Code和c1、c2、c3不就一樣了嗎?為什么還叫Code什么類類型?這里不要糾結(jié)于它們是否相同,只要理解類類型是干什么的就好了,顧名思義,類類型就是類的類型,也就是描述一個類是什么,都有哪些東西,所以我們可以通過類類型知道一個類的屬性和方法,并且可以調(diào)用一個類的屬性和方法,這就是反射的基礎(chǔ)。

      舉個簡單例子代碼:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      public class ReflectDemo {
      public static void main(String[] args) throws ClassNotFoundException {
      //第一種:Class c1 = Code.class;
      Class class1=ReflectDemo.class;
      System.out.println(class1.getName());

      //第二種:Class c2 = code1.getClass();
      ReflectDemo demo2= new ReflectDemo();
      Class c2 = demo2.getClass();
      System.out.println(c2.getName());

      //第三種:Class c3 = Class.forName("com.trigl.reflect.Code");
      Class class3 = Class.forName("com.tengj.reflect.ReflectDemo");
      System.out.println(class3.getName());
      }
      }

      執(zhí)行結(jié)果:

      1
      2
      3
      com.tengj.reflect.ReflectDemo
      com.tengj.reflect.ReflectDemo
      com.tengj.reflect.ReflectDemo

      Java反射相關(guān)操作

      在這里先看一下sun為我們提供了那些反射機(jī)制中的類:
      java.lang.Class;
      java.lang.reflect.Constructor; java.lang.reflect.Field;
      java.lang.reflect.Method;
      java.lang.reflect.Modifier;

      前面我們知道了怎么獲取Class,那么我們可以通過這個Class干什么呢?
      總結(jié)如下:

      • 獲取成員方法Method
      • 獲取成員變量Field
      • 獲取構(gòu)造函數(shù)Constructor

      下面來具體介紹

      1. 獲取成員方法信息

        兩個參數(shù)分別是方法名和方法參數(shù)類的類類型列表。

      1
      2
      3
      4
      5
      6
      7
      8
      public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到該類所有的方法,不包括父類的 
      public Method getMethod(String name, Class<?>... parameterTypes) // 得到該類所有的public方法,包括父類的

      //具體使用
      Method[] methods = class1.getDeclaredMethods();//獲取class對象的所有聲明方法
      Method[] allMethods = class1.getMethods();//獲取class對象的所有public方法 包括父類的方法
      Method method = class1.getMethod("info", String.class);//返回次Class對象對應(yīng)類的、帶指定形參列表的public方法
      Method declaredMethod = class1.getDeclaredMethod("info", String.class);//返回次Class對象對應(yīng)類的、帶指定形參列表的方法

      舉個例子:

      例如類A有如下一個方法:

      1
      2
      3
      public void fun(String name,int age) {
      System.out.println("我叫"+name+",今年"+age+"歲");
      }

      現(xiàn)在知道A有一個對象a,那么就可以通過:

      1
      2
      3
      4
      Class c = Class.forName("com.tengj.reflect.Person");  //先生成class
      Object o = c.newInstance(); //newInstance可以初始化一個實例
      Method method = c.getMethod("fun", String.class, int.class);//獲取方法
      method.invoke(o, "tengj", 10); //通過invoke調(diào)用該方法,參數(shù)第一個為實例對象,后面為具體參數(shù)值

      完整代碼如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      public class Person {
      private String name;
      private int age;
      private String msg="hello wrold";
      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;
      }

      public Person() {
      }

      private Person(String name) {
      this.name = name;
      System.out.println(name);
      }

      public void fun() {
      System.out.println("fun");
      }

      public void fun(String name,int age) {
      System.out.println("我叫"+name+",今年"+age+"歲");
      }
      }

      public class ReflectDemo {
      public static void main(String[] args){
      try {
      Class c = Class.forName("com.tengj.reflect.Person");
      Object o = c.newInstance();
      Method method = c.getMethod("fun", String.class, int.class);
      method.invoke(o, "tengj", 10);
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

      執(zhí)行結(jié)果:

      我叫tengj,今年10歲
      

      怎樣,是不是感覺很厲害,我們只要知道這個類的路徑全稱就能玩弄它于鼓掌之間。

      有時候我們想獲取類中所有成員方法的信息,要怎么辦。可以通過以下幾步來實現(xiàn):

      1.獲取所有方法的數(shù)組:

      1
      2
      3
      4
      Class c = Class.forName("com.tengj.reflect.Person");
      Method[] methods = c.getDeclaredMethods(); // 得到該類所有的方法,不包括父類的
      或者:
      Method[] methods = c.getMethods();// 得到該類所有的public方法,包括父類的

      2.然后循環(huán)這個數(shù)組就得到每個方法了:

      1
      for (Method method : methods)

      完整代碼如下:
      person類跟上面一樣,這里以及后面就不貼出來了,只貼關(guān)鍵代碼

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      public class ReflectDemo {
      public static void main(String[] args){
      try {
      Class c = Class.forName("com.tengj.reflect.Person");
      Method[] methods = c.getDeclaredMethods();
      for(Method m:methods){
      String methodName= m.getName();
      System.out.println(methodName);
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

      執(zhí)行結(jié)果:

      getName
      setName
      setAge
      fun
      fun
      getAge
      

      這里如果把c.getDeclaredMethods();改成c.getMethods();執(zhí)行結(jié)果如下,多了很多方法,以為把Object里面的方法也打印出來了,因為Object是所有類的父類:

      getName
      setName
      getAge
      setAge
      fun
      fun
      wait
      wait
      wait
      equals
      toString
      hashCode
      getClass
      notify
      notifyAll
      
      1. 獲取成員變量信息

      想一想成員變量中都包括什么:成員變量類型+成員變量名

      類的成員變量也是一個對象,它是java.lang.reflect.Field的一個對象,所以我們通過java.lang.reflect.Field里面封裝的方法來獲取這些信息。

      單獨獲取某個成員變量,通過Class類的以下方法實現(xiàn):

      參數(shù)是成員變量的名字

      1
      2
      3
      4
      5
      6
      7
      8
      public Field getDeclaredField(String name) // 獲得該類自身聲明的所有變量,不包括其父類的變量
      public Field getField(String name) // 獲得該類自所有的public成員變量,包括其父類變量

      //具體實現(xiàn)
      Field[] allFields = class1.getDeclaredFields();//獲取class對象的所有屬性
      Field[] publicFields = class1.getFields();//獲取class對象的public屬性
      Field ageField = class1.getDeclaredField("age");//獲取class指定屬性
      Field desField = class1.getField("des");//獲取class指定的public屬性

      舉個例子:

      例如一個類A有如下成員變量:

      1
      private int n;

      如果A有一個對象a,那么就可以這樣得到其成員變量:

      1
      2
      Class c = a.getClass();
      Field field = c.getDeclaredField("n");

      完整代碼如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      public class ReflectDemo {
      public static void main(String[] args){
      try {
      Class c = Class.forName("com.tengj.reflect.Person");
      //獲取成員變量
      Field field = c.getDeclaredField("msg"); //因為msg變量是private的,所以不能用getField方法
      Object o = c.newInstance();
      field.setAccessible(true);//設(shè)置是否允許訪問,因為該變量是private的,所以要手動設(shè)置允許訪問,如果msg是public的就不需要這行了。
      Object msg = field.get(o);
      System.out.println(msg);
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

      執(zhí)行結(jié)果:

      hello wrold
      

      同樣,如果想要獲取所有成員變量的信息,可以通過以下幾步

      1.獲取所有成員變量的數(shù)組:

      1
      Field[] fields = c.getDeclaredFields();

      2.遍歷變量數(shù)組,獲得某個成員變量field

      1
      for (Field field : fields)

      完整代碼:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class ReflectDemo {
      public static void main(String[] args){
      try {
      Class c = Class.forName("com.tengj.reflect.Person");
      Field[] fields = c.getDeclaredFields();
      for(Field field :fields){
      System.out.println(field.getName());
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

      執(zhí)行結(jié)果:

      name
      age
      msg
      
      1. 獲取構(gòu)造函數(shù)

      最后再想一想構(gòu)造函數(shù)中都包括什么:構(gòu)造函數(shù)參數(shù)
      同上,類的成構(gòu)造函數(shù)也是一個對象,它是java.lang.reflect.Constructor的一個對象,所以我們通過java.lang.reflect.Constructor里面封裝的方法來獲取這些信息。

      單獨獲取某個構(gòu)造函數(shù),通過Class類的以下方法實現(xiàn):

      這個參數(shù)為構(gòu)造函數(shù)參數(shù)類的類類型列表

      1
      2
      3
      4
      5
      6
      7
      8
      public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //  獲得該類所有的構(gòu)造器,不包括其父類的構(gòu)造器
      public Constructor<T> getConstructor(Class<?>... parameterTypes) // 獲得該類所以public構(gòu)造器,包括父類

      //具體
      Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//獲取class對象的所有聲明構(gòu)造函數(shù)
      Constructor<?>[] publicConstructors = class1.getConstructors();//獲取class對象public構(gòu)造函數(shù)
      Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//獲取指定聲明構(gòu)造函數(shù)
      Constructor publicConstructor = class1.getConstructor(String.class);//獲取指定聲明的public構(gòu)造函數(shù)

      舉個例子:

      例如類A有如下一個構(gòu)造函數(shù):

      1
      2
      3
      public A(String a, int b) {
      // code body
      }

      那么就可以通過:

      1
      Constructor constructor = a.getDeclaredConstructor(String.class, int.class);

      來獲取這個構(gòu)造函數(shù)。

      完整代碼:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class ReflectDemo {
      public static void main(String[] args){
      try {
      Class c = Class.forName("com.tengj.reflect.Person");
      //獲取構(gòu)造函數(shù)
      Constructor constructor = c.getDeclaredConstructor(String.class);
      constructor.setAccessible(true);//設(shè)置是否允許訪問,因為該構(gòu)造器是private的,所以要手動設(shè)置允許訪問,如果構(gòu)造器是public的就不需要這行了。
      constructor.newInstance("tengj");
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

      執(zhí)行結(jié)果:

      tengj
      

      注意:Class的newInstance方法,只能創(chuàng)建只包含無參數(shù)的構(gòu)造函數(shù)的類,如果某類只有帶參數(shù)的構(gòu)造函數(shù),那么就要使用另外一種方式:

      1
      fromClass.getDeclaredConstructor(String.class).newInstance("tengj");

      獲取所有的構(gòu)造函數(shù),可以通過以下步驟實現(xiàn):

      1.獲取該類的所有構(gòu)造函數(shù),放在一個數(shù)組中:

      1
      Constructor[] constructors = c.getDeclaredConstructors();

      2.遍歷構(gòu)造函數(shù)數(shù)組,獲得某個構(gòu)造函數(shù)constructor:

      1
      for (Constructor constructor : constructors)

      完整代碼:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class ReflectDemo {
      public static void main(String[] args){
      Constructor[] constructors = c.getDeclaredConstructors();
      for(Constructor constructor:constructors){
      System.out.println(constructor);
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

      執(zhí)行結(jié)果:

      public com.tengj.reflect.Person()
      public com.tengj.reflect.Person(java.lang.String)
      
      1. 其他方法

      注解需要用到的

      1
      2
      3
      4
      Annotation[] annotations = (Annotation[]) class1.getAnnotations();//獲取class對象的所有注解 
      Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//獲取class對象指定注解
      Type genericSuperclass = class1.getGenericSuperclass();//獲取class對象的直接超類的
      Type Type[] interfaceTypes = class1.getGenericInterfaces();//獲取class對象的所有接口的type集合

      獲取class對象的信息

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      boolean isPrimitive = class1.isPrimitive();//判斷是否是基礎(chǔ)類型 
      boolean isArray = class1.isArray();//判斷是否是集合類
      boolean isAnnotation = class1.isAnnotation();//判斷是否是注解類
      boolean isInterface = class1.isInterface();//判斷是否是接口類
      boolean isEnum = class1.isEnum();//判斷是否是枚舉類
      boolean isAnonymousClass = class1.isAnonymousClass();//判斷是否是匿名內(nèi)部類
      boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判斷是否被某個注解類修飾
      String className = class1.getName();//獲取class名字 包含包名路徑
      Package aPackage = class1.getPackage();//獲取class的包信息
      String simpleName = class1.getSimpleName();//獲取class類名
      int modifiers = class1.getModifiers();//獲取class訪問權(quán)限
      Class<?>[] declaredClasses = class1.getDeclaredClasses();//內(nèi)部類
      Class<?> declaringClass = class1.getDeclaringClass();//外部類

      getSuperclass():獲取某類的父類
      getInterfaces():獲取某類實現(xiàn)的接口

      通過反射了解集合泛型的本質(zhì)

      擴(kuò)展的知識點,了解就可以了。后續(xù)會為大家寫一篇關(guān)于泛型的文章。

      首先下結(jié)論:

      Java中集合的泛型,是防止錯誤輸入的,只在編譯階段有效,繞過編譯到了運行期就無效了。

      下面通過一個實例來驗證:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      /**
      * 集合泛型的本質(zhì)
      */
      public class GenericEssence {
      public static void main(String[] args) {
      List list1 = new ArrayList(); // 沒有泛型
      List<String> list2 = new ArrayList<String>(); // 有泛型


      /*
      * 1.首先觀察正常添加元素方式,在編譯器檢查泛型,
      * 這個時候如果list2添加int類型會報錯
      */
      list2.add("hello");
      // list2.add(20); // 報錯!list2有泛型限制,只能添加String,添加int報錯
      System.out.println("list2的長度是:" + list2.size()); // 此時list2長度為1


      /*
      * 2.然后通過反射添加元素方式,在運行期動態(tài)加載類,首先得到list1和list2
      * 的類類型相同,然后再通過方法反射繞過編譯器來調(diào)用add方法,看能否插入int
      * 型的元素
      */
      Class c1 = list1.getClass();
      Class c2 = list2.getClass();
      System.out.println(c1 == c2); // 結(jié)果:true,說明類類型完全相同

      // 驗證:我們可以通過方法的反射來給list2添加元素,這樣可以繞過編譯檢查
      try {
      Method m = c2.getMethod("add", Object.class); // 通過方法反射得到add方法
      m.invoke(list2, 20); // 給list2添加一個int型的,上面顯示在編譯器是會報錯的
      System.out.println("list2的長度是:" + list2.size()); // 結(jié)果:2,說明list2長度增加了,并沒有泛型檢查
      } catch (Exception e) {
      e.printStackTrace();
      }

      /*
      * 綜上可以看出,在編譯器的時候,泛型會限制集合內(nèi)元素類型保持一致,但是編譯器結(jié)束進(jìn)入
      * 運行期以后,泛型就不再起作用了,即使是不同類型的元素也可以插入集合。
      */
      }
      }

      執(zhí)行結(jié)果:

      list2的長度是:1
      true
      list2的長度是:2
      

      思維導(dǎo)圖

      有助于理解上述所講的知識點

      拓展閱讀
      Java反射機(jī)制深入詳解 - 火星十一郎 - 博客園
      Java反射入門 - Trigl的博客 - CSDN博客
      Java反射機(jī)制 - ①塊腹肌 - 博客園
      Java 反射機(jī)制淺析 - 孤旅者 - 博客園
      反射機(jī)制的理解及其用途 - 每天進(jìn)步一點點! - ITeye博客
      Java動態(tài)代理與反射詳解 - 浩大王 - 博客園

      JAVA注解

      概念及作用

      1. 概念
      • 注解即元數(shù)據(jù),就是源代碼的元數(shù)據(jù)
      • 注解在代碼中添加信息提供了一種形式化的方法,可以在后續(xù)中更方便的 使用這些數(shù)據(jù)
      • Annotation是一種應(yīng)用于類、方法、參數(shù)、變量、構(gòu)造器及包聲明中的特殊修飾符。它是一種由JSR-175標(biāo)準(zhǔn)選擇用來描述元數(shù)據(jù)的一種工具。
      1. 作用
      • 生成文檔
      • 跟蹤代碼依賴性,實現(xiàn)替代配置文件功能,減少配置。如Spring中的一些注解
      • 在編譯時進(jìn)行格式檢查,如@Override等
      • 每當(dāng)你創(chuàng)建描述符性質(zhì)的類或者接口時,一旦其中包含重復(fù)性的工作,就可以考慮使用注解來簡化與自動化該過程。

      什么是java注解?

      在java語法中,使用@符號作為開頭,并在@后面緊跟注解名。被運用于類,接口,方法和字段之上,例如:

      1
      2
      3
      4
      @Override
      void myMethod() {
      ......
      }

      這其中@Override就是注解。這個注解的作用也就是告訴編譯器,myMethod()方法覆寫了父類中的myMethod()方法。

      java中內(nèi)置的注解

      java中有三個內(nèi)置的注解:

      • @Override:表示當(dāng)前的方法定義將覆蓋超類中的方法,如果出現(xiàn)錯誤,編譯器就會報錯。
      • @Deprecated:如果使用此注解,編譯器會出現(xiàn)警告信息。
      • @SuppressWarnings:忽略編譯器的警告信息。

      本文不在闡述三種內(nèi)置注解的使用情節(jié)和方法,感興趣的請看這里

      元注解

      自定義注解的時候用到的,也就是自定義注解的注解;(這句話我自己說的,不知道對不對)

      元注解的作用就是負(fù)責(zé)注解其他注解。Java5.0定義了4個標(biāo)準(zhǔn)的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。

      Java5.0定義的4個元注解:

      1. @Target

      2. @Retention

      3. @Documented

      4. @Inherited

      java8加了兩個新注解,后續(xù)我會講到。

      這些類型和它們所支持的類在java.lang.annotation包中可以找到。

      @Target

      @Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標(biāo)。

      作用:用于描述注解的使用范圍(即:被描述的注解可以用在什么地方)

      取值(ElementType)有:

      類型用途
      CONSTRUCTOR 用于描述構(gòu)造器
      FIELD 用于描述域
      LOCAL_VARIABLE 用于描述局部變量
      METHOD 用于描述方法
      PACKAGE 用于描述包
      PARAMETER 用于描述參數(shù)
      TYPE 用于描述類、接口(包括注解類型) 或enum聲明

      比如說這個注解表示只能在方法中使用:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      @Target({ElementType.METHOD})
      public @interface MyCustomAnnotation {

      }

      //使用
      public class MyClass {
      @MyCustomAnnotation
      public void myMethod()
      {
      ......
      }
      }

      @Retention

      @Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機(jī)忽略,而另一些在class被裝載時將被讀取(請注意并不影響class的執(zhí)行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。

      作用:表示需要在什么級別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)

      取值(RetentionPoicy)有:

      類型用途說明
      SOURCE 在源文件中有效(即源文件保留) 僅出現(xiàn)在源代碼中,而被編譯器丟棄
      CLASS 在class文件中有效(即class保留) 被編譯在class文件中
      RUNTIME 在運行時有效(即運行時保留) 編譯在class文件中

      使用示例:

      1
      2
      3
      4
      5
      6
      7
      8
      /***
      * 字段注解接口
      */
      @Target(value = {ElementType.FIELD})//注解可以被添加在屬性上
      @Retention(value = RetentionPolicy.RUNTIME)//注解保存在JVM運行時刻,能夠在運行時刻通過反射API來獲取到注解的信息
      public @interface Column {
      String name();//注解的name屬性
      }

      @Documented

      @Documented用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標(biāo)記注解,沒有成員。

      作用:將注解包含在javadoc中

      示例:

      1
      2
      3
      java.lang.annotation.Documented
      @Documented
      public @interface MyCustomAnnotation { //Annotation body}

      @Inherited

      • 是一個標(biāo)記注解
      • 闡述了某個被標(biāo)注的類型是被繼承的
      • 使用了@Inherited修飾的annotation類型被用于一個class,則這個annotation將被用于該class的子類
        @Inherited annotation類型是被標(biāo)注過的class的子類所繼承。類并不從實現(xiàn)的接口繼承annotation,方法不從它所重載的方法繼承annotation
      • 當(dāng)@Inherited annotation類型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強(qiáng)了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn),或者到達(dá)類繼承結(jié)構(gòu)的頂層。

      作用:允許子類繼承父類中的注解

      示例,這里的MyParentClass 使用的注解標(biāo)注了@Inherited,所以子類可以繼承這個注解信息:

      1
      2
      3
      4
      java.lang.annotation.Inherited
      @Inherited
      public @interface MyCustomAnnotation {
      }
      1
      2
      3
      4
      @MyCustomAnnotation
      public class MyParentClass {
      ...
      }
      1
      2
      3
      public class MyChildClass extends MyParentClass { 
      ...
      }

      自定義注解

      格式

      1
      2
      3
      public @interface 注解名{
      定義體
      }

      注解參數(shù)的可支持?jǐn)?shù)據(jù)類型:

      • 所有基本數(shù)據(jù)類型(int,float,double,boolean,byte,char,long,short)
      • String 類型
      • Class類型
      • enum類型
      • Annotation類型
      • 以上所有類型的數(shù)組

      規(guī)則

      • 修飾符只能是public 或默認(rèn)(default)
      • 參數(shù)成員只能用基本類型byte,short,int,long,float,double,boolean八種基本類型和String,Enum,Class,annotations及這些類型的數(shù)組
      • 如果只有一個參數(shù)成員,最好將名稱設(shè)為”value”
      • 注解元素必須有確定的值,可以在注解中定義默認(rèn)值,也可以使用注解時指定,非基本類型的值不可為null,常使用空字符串或0作默認(rèn)值
      • 在表現(xiàn)一個元素存在或缺失的狀態(tài)時,定義一下特殊值來表示,如空字符串或負(fù)值

      示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      /**
      * test注解
      * @author ddk
      *
      */
      @Target(ElementType.FIELD)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface TestAnnotation {
      /**
      * id
      * @return
      */
      public int id() default -1;
      /**
      * name
      * @return
      */
      public String name() default "";
      }

      注解處理器類庫

      java.lang.reflect.AnnotatedElement

      Java使用Annotation接口來代表程序元素前面的注解,該接口是所有Annotation類型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受注解的程序元素,該接口主要有如下幾個實現(xiàn)類:

      •  Class:類定義
      •  Constructor:構(gòu)造器定義
      •  Field:累的成員變量定義
      •  Method:類的方法定義
      •  Package:類的包定義

      java.lang.reflect 包下主要包含一些實現(xiàn)反射功能的工具類,實際上,java.lang.reflect 包所有提供的反射API擴(kuò)充了讀取運行時Annotation信息的能力。當(dāng)一個Annotation類型被定義為運行時的Annotation后,該注解才能是運行時可見,當(dāng)class文件被裝載時被保存在class文件中的Annotation才會被虛擬機(jī)讀取。

      AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的AnnotatedElement對象之后,程序就可以調(diào)用該對象的如下四個個方法來訪問Annotation信息:

      • 方法1: T getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null。
      • 方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有注解。
      • 方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.
      • 方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會對其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。

      注解處理器示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      /***********注解聲明***************/

      /**
      * 水果名稱注解
      * @author peida
      *
      */
      @Target(ElementType.FIELD)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface FruitName {
      String value() default "";
      }

      /**
      * 水果顏色注解
      * @author peida
      *
      */
      @Target(ElementType.FIELD)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface FruitColor {
      /**
      * 顏色枚舉
      * @author peida
      *
      */
      public enum Color{ BULE,RED,GREEN};

      /**
      * 顏色屬性
      * @return
      */
      Color fruitColor() default Color.GREEN;

      }

      /**
      * 水果供應(yīng)者注解
      * @author peida
      *
      */
      @Target(ElementType.FIELD)
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      public @interface FruitProvider {
      /**
      * 供應(yīng)商編號
      * @return
      */
      public int id() default -1;

      /**
      * 供應(yīng)商名稱
      * @return
      */
      public String name() default "";

      /**
      * 供應(yīng)商地址
      * @return
      */
      public String address() default "";
      }

      /***********注解使用***************/

      public class Apple {

      @FruitName("Apple")
      private String appleName;

      @FruitColor(fruitColor=Color.RED)
      private String appleColor;

      @FruitProvider(id=1,name="陜西紅富士集團(tuán)",address="陜西省西安市延安路89號紅富士大廈")
      private String appleProvider;

      public void setAppleColor(String appleColor) {
      this.appleColor = appleColor;
      }
      public String getAppleColor() {
      return appleColor;
      }

      public void setAppleName(String appleName) {
      this.appleName = appleName;
      }
      public String getAppleName() {
      return appleName;
      }

      public void setAppleProvider(String appleProvider) {
      this.appleProvider = appleProvider;
      }
      public String getAppleProvider() {
      return appleProvider;
      }

      public void displayName(){
      System.out.println("水果的名字是:蘋果");
      }
      }

      /***********注解處理器***************/
      //其實是用的反射


      public class FruitInfoUtil {
      public static void getFruitInfo(Class<?> clazz){

      String strFruitName=" 水果名稱:";
      String strFruitColor=" 水果顏色:";
      String strFruitProvicer="供應(yīng)商信息:";

      Field[] fields = clazz.getDeclaredFields();

      for(Field field :fields){
      if(field.isAnnotationPresent(FruitName.class)){
      FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
      strFruitName=strFruitName+fruitName.value();
      System.out.println(strFruitName);
      }
      else if(field.isAnnotationPresent(FruitColor.class)){
      FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
      strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
      System.out.println(strFruitColor);
      }
      else if(field.isAnnotationPresent(FruitProvider.class)){
      FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
      strFruitProvicer=" 供應(yīng)商編號:"+fruitProvider.id()+" 供應(yīng)商名稱:"+fruitProvider.name()+" 供應(yīng)商地址:"+fruitProvider.address();
      System.out.println(strFruitProvicer);
      }
      }
      }
      }

      /***********輸出結(jié)果***************/
      public class FruitRun {

      /**
      * @param args
      */
      public static void main(String[] args) {

      FruitInfoUtil.getFruitInfo(Apple.class);

      }

      }

      ====================================
      水果名稱:Apple
      水果顏色:RED
      供應(yīng)商編號:1 供應(yīng)商名稱:陜西紅富士集團(tuán) 供應(yīng)商地址:陜西省西安市延安路89號紅富士大廈

      Java 8 中注解新特性

      • @Repeatable 元注解,表示被修飾的注解可以用在同一個聲明式或者類型加上多個相同的注解(包含不同的屬性值)
      • @Native 元注解,本地方法
      • java8 中Annotation 可以被用在任何使用 Type 的地方
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
       //初始化對象時
      String myString = new @NotNull String();
      //對象類型轉(zhuǎn)化時
      myString = (@NonNull String) str;
      //使用 implements 表達(dá)式時
      class MyList<T> implements @ReadOnly List<@ReadOnly T>{
      ...
      }
      //使用 throws 表達(dá)式時
      public void validateValues() throws @Critical ValidationFailedException{
      ...
      }

      思維導(dǎo)圖

      拓展閱讀

      深入理解Java:注解 - 牛奶、不加糖 - 博客園
      Java 注解基礎(chǔ)知識 - 簡書
      【譯】從java注解分析ButterKnife工作流程 - 簡書

       
      主站蜘蛛池模板: 性视频一区| 亚洲国产成人无码av在线影院 | 无码 人妻 在线 视频| 国产精品成人av电影不卡| 亚洲av日韩av永久无码电影 | japanese人妻中文字幕| 国产精品自在线拍国产手机版| 国产精品视频一区二区不卡| 国产剧情91精品蜜臀一区| jk白丝喷浆| 免费人妻无码不卡中文18禁| 久久99国产一区二区三区| 国产稚嫩高中生呻吟激情在线视频 | 亚洲一区二区偷拍精品| 国产精品亚韩精品无码a在线| 四虎成人在线观看免费| 成人特黄特色毛片免费看| 成年女人片免费视频播放A| 亚洲日韩成人av无码网站| 久久妇女高潮喷水多| 四虎精品永久在线视频| 色婷婷狠狠久久综合五月| 久久热这里这里只有精品| 精品日本乱一区二区三区| 亚洲鸥美日韩精品久久| 国产午夜一区二区在线观看 | 久久这里只精品国产免费9| 印江| 国产欧美日韩精品丝袜高跟鞋| 粉嫩一区二区三区国产精品| 亚洲成av人片在www鸭子| 欧洲码亚洲码的区别入口| 亚洲熟妇自偷自拍另欧美| www久久只有这里有精品| 手机看片福利一区二区三区| A男人的天堂久久A毛片| 不卡一区二区国产在线| 久久精品日日躁夜夜躁| 国产乱人伦AV在线麻豆A | 亚洲免费福利在线视频| 宾馆人妻4P互换视频|