1.1 注解的定義
注解就是源代碼的元數據,通熟的講就是代碼中的標簽。注解就有如下的特點:
-
注解是一個附屬品,依賴於其他元素(包、類、方法、屬性等等)存在。
-
注解本身沒有作用,在恰當的時候由外部程序進行解析才會發生作用。
1.2 注解的分類
-
按來源分
-
JDK 自帶注解,例如:@Override, @Deprecated, @SuppressWornings 。
-
第三方注解。
-
自定義注解。
-
-
按生命周期划分
-
SOURCE:只存在於源代碼中,編譯成 class 文件就不存在了。
-
Class:存在於源代碼中和 class 文件中。
-
RUNTIME:注解保留到運行時。
-
1.3 元注解
元注解指的是用於修飾注解的注解,包括如下幾個:
-
@Retention:指明 Annotation 的生命周期,傳入的值是一個枚舉類型,可選值為:
-
RetentionPolicy.SOURCE -
RetentionPolicy.CLASS -
RetentionPolicy.RUNTIME
-
-
@Target:指明 Annotation 可以修飾程序哪些元素,傳入的值為ElemetType[] 類型,值可為:
-
ElementType.CONSTRUCTOR:構造器 -
ElementType.FIELD:屬性 -
ElementType.LOCAL_VARIABLE:局部變量 -
ElementType.METHOD:方法 -
ElementType.PACKAGE:包 -
ElementType.PARAMETER:參數 -
ElementType.TYPE:類、接口(包括注解類型和 enum 聲明)
-
-
@Documented:使用此修飾的注解將會被 javadoc 工具提取成文檔,使用此注解,其 @Retention 必須被設置為
RetentionPolicy.RUNTIME。 -
@Inherited:具有繼承性。
1.4 自定義注解
自定義注解需要注意的問題:
-
使用 @interface 關鍵字定義。
-
自動繼承
java.lang.annotation.Annotation接口。 -
配置參數的類型只能是八大基本類型、String、Class、enum、Annotation 和對應的數組類型。
-
配置參數聲明的語法格式如下([] 表示可省略):
類型 變量名() [default 默認值]; -
如果只有一個配置參數,其參數名必須為 value。
-
如果定義的注解含有配置參數,那在使用注解時,必須指定參數值,指定形式為:“參數名=參數值”。如果只有一個參數,直接寫參數值即可,定義中指定了默認值的參數可以不指定值,但沒有的一定要指定值。
-
沒有成員的注解為標記,包含成員的稱為元數據。
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,他們的區別是:
-
無 Declared:只能獲取到 public 修飾的,包括當前類和所有父類。
-
有 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 靜態代理
代理類和原始對象在編譯期間就確定下來了,不利於程序的擴展,且每一個代理只能為一個接口服務,這就會在開發的過程中產生多大的代理類。
靜態代理的實現:
-
代理類和原始對象實現相同的接口。
-
代理類保持原始對象的引用。
-
代理類里調用原始對象的方法。
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 }
