Java:注解和反射


1. 注解

 

1.1 注解的定義

注解就是源代碼的元數據,通熟的講就是代碼中的標簽。注解就有如下的特點:

  1. 注解是一個附屬品,依賴於其他元素(包、類、方法、屬性等等)存在。

  2. 注解本身沒有作用,在恰當的時候由外部程序進行解析才會發生作用。

 

1.2 注解的分類

  1. 按來源分

    • JDK 自帶注解,例如:@Override, @Deprecated, @SuppressWornings 。

    • 第三方注解。

    • 自定義注解。

  2. 按生命周期划分

    • SOURCE:只存在於源代碼中,編譯成 class 文件就不存在了。

    • Class:存在於源代碼中和 class 文件中。

    • RUNTIME:注解保留到運行時。

 

1.3 元注解

元注解指的是用於修飾注解的注解,包括如下幾個:

  1. @Retention:指明 Annotation 的生命周期,傳入的值是一個枚舉類型,可選值為:

    • RetentionPolicy.SOURCE

    • RetentionPolicy.CLASS

    • RetentionPolicy.RUNTIME

  2. @Target:指明 Annotation 可以修飾程序哪些元素,傳入的值為ElemetType[] 類型,值可為:

    • ElementType.CONSTRUCTOR :構造器

    • ElementType.FIELD:屬性

    • ElementType.LOCAL_VARIABLE:局部變量

    • ElementType.METHOD:方法

    • ElementType.PACKAGE:包

    • ElementType.PARAMETER:參數

    • ElementType.TYPE:類、接口(包括注解類型和 enum 聲明)

  3. @Documented:使用此修飾的注解將會被 javadoc 工具提取成文檔,使用此注解,其 @Retention 必須被設置為 RetentionPolicy.RUNTIME

  4. @Inherited:具有繼承性。

 

1.4 自定義注解

自定義注解需要注意的問題:

  1. 使用 @interface 關鍵字定義。

  2. 自動繼承 java.lang.annotation.Annotation 接口。

  3. 配置參數的類型只能是八大基本類型、String、Class、enum、Annotation 和對應的數組類型。

  4. 配置參數聲明的語法格式如下([] 表示可省略):

    類型 變量名() [default 默認值];
  5. 如果只有一個配置參數,其參數名必須為 value。

  6. 如果定義的注解含有配置參數,那在使用注解時,必須指定參數值,指定形式為:“參數名=參數值”。如果只有一個參數,直接寫參數值即可,定義中指定了默認值的參數可以不指定值,但沒有的一定要指定值

  7. 沒有成員的注解為標記,包含成員的稱為元數據

 

1.5 注解的解析

參考代碼:

(1)Info.java

 1 package com.hkl;
 2  3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7  8 /**
 9  * 注解
10  */
11 @Retention(RetentionPolicy.RUNTIME)
12 @Target({ElementType.TYPE, ElementType.METHOD})
13 public @interface Info {
14     String info();
15     String birthday();
16     int age() default 0;
17 }
18 19 @Retention(RetentionPolicy.RUNTIME)
20 @Target({ElementType.TYPE, ElementType.METHOD})
21 @interface Desc{
22     String value();
23 }

 

 

(2)App.java

 1 package com.hkl;
 2  3 import java.lang.reflect.Method;
 4  5 /**
 6  * Hello world!
 7  *
 8  */
 9 @Info(info = "hkl", birthday = "2019/7/20")
10 @Desc("這是一個類")
11 public class App 
12 {
13     @Info(info = "hkl", birthday = "2019/7/20", age = 22)
14     @Desc("這是一個方法")
15     public static void main( String[] args )
16     {
17         // 解析注解
18         try {
19             Class clazz = Class.forName("com.hkl.App");
20 21             // 獲取類修飾的注解
22             System.out.println("---------類中的注解---------");
23             if(clazz.isAnnotationPresent(Info.class)){
24                 Info classInfo = (Info) clazz.getAnnotation(Info.class);
25                 System.out.println(classInfo.info());
26                 System.out.println(classInfo.birthday());
27                 System.out.println(classInfo.age());
28             }
29 30             if(clazz.isAnnotationPresent(Desc.class)){
31                 Desc classDesc = (Desc)clazz.getAnnotation(Desc.class);
32                 System.out.println(classDesc.value());
33             }
34 35             // 獲取方法修飾的注解
36             Method[] methods = clazz.getMethods();
37 38             System.out.println("---------方法中的注解解析---------");
39             for(Method method : methods){
40                 if(method.isAnnotationPresent(Desc.class)){
41                     Desc methodDesc = (Desc)method.getAnnotation(Desc.class);
42                     System.out.println(methodDesc.value());
43                 }
44 45                 if(method.isAnnotationPresent(Info.class)){
46                     Info methodInfo = (Info)method.getAnnotation(Info.class);
47                     System.out.println(methodInfo.info());
48                     System.out.println(methodInfo.birthday());
49                     System.out.println(methodInfo.age());
50                 }
51 52             }
53         } catch (ClassNotFoundException e) {
54             e.printStackTrace();
55         }
56     }
57 }

 

2. 反射

 

2.1 什么反射

反射指的是程序在運行期間借助反射 API 取得任何類的內部信息,並通過這些內部信息去操作對應對象的內部屬性和方法。

任何一個類,在第一次使用時,就會被 JVM 加載到堆內存的方法區中。JVM 加載類成功后,就會在方法區中產生一個對應的 Class 對象(一個類只要一個 Class 對象),這個 Class 對象包含被加載類的全部結構信息。

 

2.2 獲取 Class 對象的常用方式

(1)類的 class 屬性

每一個類,都有一個 class 靜態屬性,這個靜態屬性就是類對應的 Class 對象。

1 Class<Person> cl1 = Person.class; 

 

(2)Object 對象 的 getClass() 方法

1 Person p1 = new Person();
2 Class<Person> cl2 = (Class<Person>) p1.getClass(); 

 

(3)通過 Class 類的 forName() 方法(最常用)

1 try {
2     Class cl3 = Class.forName("com.llm.hkl.Person");
3 } catch (ClassNotFoundException e) {
4     e.printStackTrace();
5 }

 

(4)通過 ClassLoader 類(不常用)

1 ClassLoader cl = Person.class.getClassLoader();
2 try {
3     Class cl4 = cl.loadClass("com.llm.hkl.Person");
4 } catch (ClassNotFoundException e) {
5     e.printStackTrace();
6 }

 

2.3 反射的基本使用

反射的基本使用包括創建對象,設置屬性和調用方法。Class 對象中大多數 get 方法有 Declared 和無 Declared,他們的區別是:

  1. 無 Declared:只能獲取到 public 修飾的,包括當前類和所有父類。

  2. 有 Declared:獲取到當前類所有的(含有 private),但不包括其父類。

 

Person 類:

 1 public class Person {
 2     private String name;
 3     private int age;
 4     public String habbit;
 5  6     public Person() {
 7     }
 8  9     public Person(String name, int age) {
10         this.name = name;
11         this.age = age;
12     }
13 14     private Person(String name, int age, String habbit) {
15         this.name = name;
16         this.age = age;
17         this.habbit = habbit;
18     }
19 20     public String getName() {
21         return name;
22     }
23 24     public void setName(String name) {
25         this.name = name;
26     }
27 28     public int getAge() {
29         return age;
30     }
31 32     public void setAge(int age) {
33         this.age = age;
34     }
35 36     public String getHabbit() {
37         return habbit;
38     }
39 40     public void setHabbit(String habbit) {
41         this.habbit = habbit;
42     }
43 44     private String say(String str){
45         String str1 = name+"說:"+str;
46         System.out.println(str1);
47         return str1;
48     }
49 50     public void eat(String food){
51         System.out.println(name+"吃"+food);
52     }
53 54     @Override
55     public String toString() {
56         return "["+name+","+age+","+habbit+"]";
57     }
58 }

測試方法:
 1 public class PersonTest {
 2     @Test
 3     public void ReflexTest() throws Exception{
 4         System.out.println("---------- new 方式創建對象 ----------");
 5         // 普通方式創建 Person 對象
 6         Person p1 = new Person("hkl", 22);
 7  8         // 直接設置屬性
 9         p1.habbit = "編程";
10         // 調用方法
11         System.out.println(p1);
12 13         //無法直接設置私有屬性和調用私有方法
14         //p1.name = "hkl";
15         //p1.say(""Hello);
16 17         //反射方式創建對象
18         System.out.println("---------- 反射方式創建對象 ----------");
19         Class<Person> clazz = Person.class;
20 21         // 調用無參構造器參加對象
22         Constructor<Person> constructor1 = clazz.getConstructor();
23         Person p2 = constructor1.newInstance();
24         System.out.println(p2);
25 26         // 通過有參構造器
27         Constructor<Person> constructor2 = clazz.getConstructor(String.class, int.class);
28         Person p3 = constructor2.newInstance("hkl", 22);
29         System.out.println(p3);
30 31         // 通過私有的構造器
32         Constructor<Person> constructor3 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
33         constructor3.setAccessible(true);
34         Person p4 = constructor3.newInstance("hkl", 22, "編程");
35         System.out.println(p4);
36 37         //通過反射設置公有屬性
38         Field personFeildHabbit = clazz.getDeclaredField("habbit");
39         personFeildHabbit.set(p2, "編程");
40 41         //通過反射設置私有屬性
42         Field personFeildAge = clazz.getDeclaredField("age");
43         personFeildAge.setAccessible(true);
44         personFeildAge.set(p2, 18);
45 46         //通過反射調用方法
47         Method personMethodToString = clazz.getDeclaredMethod("toString");
48         // 方法的返回值為調用方法的返回值
49         String str = (String)personMethodToString.invoke(p2);
50         System.out.println(str);
51 52         // 通過反射調用私有方法
53         Method personMethodSay = clazz.getDeclaredMethod("say", String.class);
54         personMethodSay.setAccessible(true);
55         String str2 = (String)personMethodSay.invoke(p2, "Hello");
56         System.out.println(str2);
57     }
58 }

 

2.5 設計模式:代理模式

使用一個代理對象將原始對象包裝起來, 然后用該代理對象取代原始對象。任何對原始對象的調用都要通過代理對象,代理對象決定是否以及何時將方法調用轉到原始對象上。

2.5.1 靜態代理

代理類和原始對象在編譯期間就確定下來了,不利於程序的擴展,且每一個代理只能為一個接口服務,這就會在開發的過程中產生多大的代理類。

靜態代理的實現:

  1. 代理類和原始對象實現相同的接口。

  2. 代理類保持原始對象的引用。

  3. 代理類里調用原始對象的方法。

 

ClothFactory 接口:

1 package com.hkl.proxy;
2 3 4 public interface ClothFactory {
5     String producer();
6 }

 

ClothFactoryProxy 類:

 

 1 package com.hkl.proxy;
 2  3 public class ClothFactoryProxy implements ClothFactory{
 4     private ClothFactory clothFactory;
 5  6     public ClothFactoryProxy(ClothFactory clothFactory) {
 7         this.clothFactory = clothFactory;
 8     }
 9 10     @Override
11     public String producer() {
12         System.out.println("代理對象做一些准備");
13         clothFactory.producer();
14         System.out.println("代理對象做一些收尾工作");
15 16         return null;
17     }
18 }

 

NikeFactory 類:

 1 package com.hkl.proxy;
 2  3 public class NikeFactory implements ClothFactory{
 4  5     public NikeFactory() {
 6     }
 7  8     @Override
 9     public String producer() {
10         System.out.println("Nike 正在生產衣服");
11         return null;
12     }
13 }

 

測試方法:

 1 @Test
 2 public void staticProxyTest(){
 3     // 創建被代理對象
 4     NikeFactory nikeFactory = new NikeFactory();
 5  6     // 創建代理對象
 7     ClothFactoryProxy clothFactoryProxy = new ClothFactoryProxy(nikeFactory);
 8  9     // 通過代理對象調用被代理對象的方法
10     clothFactoryProxy.producer();
11 }

 

2.5.2 動態代理模式

動態代理是通過 Java 的反射機制實現的。通過動態代理,只需一個代理對象就可以代理所有的對象。

 

Humen :接口

1 package com.llm.proxy;
2 public interface Human {
3     String belief();
4     void eat(String food);
5 }

 

SuperMan:類

 1 package com.hkl.proxy;
 2  3 public class SuperMan implements Human {
 4     @Override
 5     public String belief() {
 6         return "我相信,我能行!";
 7     }
 8  9     @Override
10     public void eat(String food) {
11         System.out.println("正在吃"+food);
12     }
13 }

 

動態代理類:

 1 package com.hkl.proxy;
 2  3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6  7  8 public class DynamicProxy {
 9     // 獲取代理對象
10     public static Object getInstance(Object obj){
11         MyInvocation h = new MyInvocation();
12         h.bind(obj);
13         // 動態的創建對象
14         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), h);
15     }
16 }
17 18 class MyInvocation implements InvocationHandler{
19     private Object obj;
20 21     /**
22      * 綁定被代理對象
23      * @param obj
24      */
25     public void bind(Object obj){
26         this.obj = obj;
27     }
28 29     @Override
30     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
31         // 代理對象調用被代理對象的方法
32         Object ret = method.invoke(obj, args);
33         return ret;
34     }
35 }

 

測試方法:

 1 @Test
 2 public void dynamicProxyTest(){
 3     SuperMan sm = new SuperMan();
 4     Human h = (Human)DynamicProxy.getInstance(sm);
 5     System.out.println(h.belief());;
 6     h.eat("麻辣燙");
 7  8     NikeFactory nikeFactory = new NikeFactory();
 9     ClothFactory clothFactory = (ClothFactory) DynamicProxy.getInstance(nikeFactory);
10     clothFactory.producer();
11 }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM