go基礎
1. 註解
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 }