自定義註解與常用設計模式

  • 2019 年 11 月 7 日
  • 筆記

1.什麼是註解

註解分為:內置註解,自定義註解。內置註解就是JDK 自帶的,而自定義註解則是自己定義的比如許多框架(spring) 用到的

內置註解:

(1) @SuppressWarnings 再程式前面加上可以在javac編譯中去除警告–階段是SOURCE (2) @Deprecated 帶有標記的包,方法,欄位說明其過時—-階段是SOURCE (3)@Overricle 打上這個標記說明該方法是將父類的方法重寫–階段是SOURCE

自定義註解:

1.@Target

@Target說明了Annotation所修飾的對象範圍:Annotation可被用於 packages、types(類、介面、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變數、枚舉值)、方法參數和本地變數(如循環變數、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。

1. CONSTRUCTOR:用於描述構造器

2. FIELD:用於描述域

3. LOCAL_VARIABLE:用於描述局部變數

4. METHOD:用於描述方法

5. PACKAGE:用於描述包

6. PARAMETER:用於描述參數

7. TYPE:用於描述類、介面(包括註解類型) 或enum聲明 2.@Retention

表示需要在什麼級別保存該注釋資訊,用於描述註解的生命周期(即:被描述的註解在什麼範圍內有效) 3.@Documented 4.@Inherited

程式碼示例:

package com.cheng.entity;    import com.cheng.MavenTest.Zhujie.setPorter;  import com.cheng.MavenTest.Zhujie.setTable;      @setTable(value = "tree")  public  class Strudents {  	@setPorter(leng = 10, name = "name")  	public String name;  	@setPorter(leng = 10, name = "age")  	private Integer age;    	static {    		System.out.println("靜態塊");  	}    	public Strudents() {  		System.out.println("無參構造函數");    	}    	public  Strudents(String name) {  		System.out.println("有參構造函數 傳入參數為:"+ name);  	}      	public String getName() {  		return name;  	}  	public void setName(String name) {  		this.name = name;  	}  	public Integer getAge() {  		return age;  	}  	public void setAge(Integer age) {  		this.age = age;  	}        }
package com.cheng.MavenTest;    import java.lang.annotation.ElementType;  import java.lang.annotation.Retention;  import java.lang.annotation.RetentionPolicy;  import java.lang.annotation.Target;  import java.lang.reflect.Field;          public class Zhujie {  	//自定義表欄位映射註解  	@Retention(RetentionPolicy.RUNTIME)  	public @interface setPorter{  		String name();  		int leng();  	}  	//自定表映射註解ORM  	@Target(value={ElementType.TYPE})  	@Retention(RetentionPolicy.RUNTIME)  	public @interface setTable{    		String value();    	}    	public static void main(String[] args) throws ClassNotFoundException {  		//反射獲取類  		Class<?>  c=Class.forName("com.cheng.entity.Strudents");  		//獲取類  		setTable st=c.getAnnotation(setTable.class);  		//獲取實體類欄位  		 Field[] declaredFields = c.getDeclaredFields();  		 StringBuffer stringBuffer = new StringBuffer();  		 stringBuffer.append(" select ");  		 for(int i=0;i<declaredFields.length;i++){  			 Field f=declaredFields[i];  			 //獲取註解值  			 setPorter  sp=f.getAnnotation(setPorter.class);  			 stringBuffer.append(" " + sp.name()+" ");  			 if(declaredFields.length-1==i){  				 stringBuffer.append(" from ");  			 }else{  				 stringBuffer.append(" , ");  			 }    		 }  		 stringBuffer.append(" "+st.value());  		 System.out.println(stringBuffer.toString());  	}    }

結果:

什麼是設計模式

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使程式碼編製真正工程化,設計模式是軟體工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。本章系Java之美[從菜鳥到高手演變]系列之設計模式,我們會以理論與實踐相結合的方式來進行本章的學習,希望廣大程式愛好者,學好設計模式,做一個優秀的軟體工程師!

設計模式分類

總體來說設計模式分為三大類:

創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。

結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。

其實還有兩類:併發型模式和執行緒池模式。用一個圖片來整體描述一下:

設計模式的六大原則

1、開閉原則(Open Close Principle)

開閉原則就是說對擴展開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程式的擴展性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。

2、里氏代換原則(Liskov Substitution Principle)

里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承復用的基石,只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對「開-閉」原則的補充。實現「開-閉」原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。—— From Baidu 百科

3、依賴倒轉原則(Dependence Inversion Principle)

這個是開閉原則的基礎,具體內容:真對介面編程,依賴於抽象而不依賴於具體。

4、介面隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟體的設計思想,從大型軟體架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。

5、迪米特法則(最少知道原則)(Demeter Principle)

為什麼叫最少知道原則,就是說:一個實體應當盡量少的與其他實體之間發生相互作用,使得系統功能模組相對獨立。

6、合成復用原則(Composite Reuse Principle)

原則是盡量使用合成/聚合的方式,而不是使用繼承。

什麼是單利模式?

單例保證一個對象JVM中只能有一個實例,常見單例 懶漢式、餓漢式

什麼是懶漢式,就是需要的才會去實例化,執行緒不安全。

什麼是餓漢式,就是當class文件被載入的時候,初始化,天生執行緒安全。

懶漢式

class SingletonTest {  	public static void main(String[] args) {  		Singleton sl1 = Singleton.getSingleton();  		Singleton sl2 = Singleton.getSingleton();  		System.out.println(sl1 == sl2);  	}  }    public class Singleton {  	// 當需要的才會被實例化  	private static Singleton singleton;    	private Singleton() {    	}    	synchronized public static Singleton getSingleton() {  		if (singleton == null) {  			singleton = new Singleton();  		}  		return singleton;  	}    }

餓漢式

lass SingletonTest1 {  	public static void main(String[] args) {  		Singleton1 sl1 = Singleton1.getSingleton();  		Singleton1 sl2 = Singleton1.getSingleton();  		System.out.println((sl1 == sl2)+"-");  	}  }    public class Singleton1 {  	//當class 文件被載入初始化  	private static Singleton1 singleton = new Singleton1();    	private Singleton1() {    	}    	public static Singleton1 getSingleton() {  		return singleton;  	}    }

工行模式?

實現創建者和調用者分離

public interface Car {  	public void run();  }  public class AoDi implements Car {  	@Override  	public void run() {       System.out.println("奧迪....");  	}  }  public interface Car {  	public void run();  }      public class CarFactory {  	static public Car createCar(String carName) {  		Car car = null;  		if (carName.equals("奧迪")) {  			car = new AoDi();  		} else if (carName.equals("Benz")) {  			car = new BenChi();  		}  		return car;    	}  	public static void main(String[] args) {  		Car car1 = CarFactory.createCar("奧迪");  		Car car2 = CarFactory.createCar("Benz");  		car1.run();  		car2.run();  	}  }

工廠方法

public interface Car {    	public void run();    }    public class AoDi implements Car {    	@Override  	public void run() {  		System.out.println("奧迪....");  	}    }        public class BenChi implements Car {    	@Override  	public void run() {  		System.out.println("Benz....");  	}    }          public class AoDiChiFactory {  	static public Car createCar() {  		return new AoDi();  	}  }    public interface BenChiFactory  {  	static public Car createCar() {  		return new BenChi();  	}  }  public class Main {    	public static void main(String[] args) {  		Car c1 = AoDiChiFactory.createCar();  		Car c2 = BenChiFactory.createCar();  		c1.run();  		c2.run();  	}    }

什麼是代理?

通過代理控制對象的訪問,可以詳細訪問某個對象的方法,在這個方法調用處理,或調用後處理。既(AOP微實現) ,AOP核心技術面向切面編程。

代理應用場景

安全代理 可以屏蔽真實角色

遠程代理 遠程調用代理類RMI

延遲載入 先載入輕量級代理類,真正需要在載入真實

代理的分類

靜態代理(靜態定義代理類)

動態代理(動態生成代理類)

Jdk自帶動態代理

Cglib 、javaassist(位元組碼操作庫)

靜態代理

靜態代理需要自己生成代理類

public class XiaoMing implements Hose {  	@Override  	public void mai() {  		System.out.println("我是小明,我要買房啦!!!!haha ");  	}  }  class Proxy  implements Hose {  	private XiaoMing xiaoMing;  	public Proxy(XiaoMing xiaoMing) {  		this.xiaoMing = xiaoMing;  	}  	public void mai() {  		System.out.println("我是中介 看你買房開始啦!");  		xiaoMing.mai();  		System.out.println("我是中介 看你買房結束啦!");  	}  	public static void main(String[] args) {  		Hose proxy = new Proxy(new XiaoMing());  		proxy.mai();  	}  }

JDK動態代理(不需要生成代理類)

實現InvocationHandler 就可以了

public interface Hose {    	/**  	 *  	 * @methodDesc: 功能描述:(買房代理)  	 * @author: 余勝軍  	 * @param:  	 * @createTime:2017年8月27日 上午2:54:34  	 * @returnType: void  	 * @copyright:上海每特教育科技有限公司  	 */  	public void mai();    }      public class XiaoMing implements Hose {    	@Override  	public void mai() {  		System.out.println("我是小明,我要買房啦!!!!haha ");  	}    }    public class JDKProxy implements InvocationHandler {  	private Object tarjet;    	public JDKProxy(Object tarjet) {  		this.tarjet = tarjet;  	}    	@Override  	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  		System.out.println("我是房產中介.....開始監聽你買房啦!");  		Object oj = method.invoke(tarjet, args);  		System.out.println("我是房產中介.....結束監聽你買房啦!");  		return oj;    	}    }    class Test222 {  	public static void main(String[] args) {  		XiaoMing xiaoMing = new XiaoMing();  		JDKProxy jdkProxy = new JDKProxy(xiaoMing);  		Hose hose=(Hose) Proxy.newProxyInstance(xiaoMing.getClass().getClassLoader(), xiaoMing.getClass().getInterfaces(), jdkProxy);  		hose.mai();  	}    }

CGLIB動態代理

import java.lang.reflect.Method;    import net.sf.cglib.proxy.Enhancer;  import net.sf.cglib.proxy.MethodInterceptor;  import net.sf.cglib.proxy.MethodProxy;    public class Cglib implements MethodInterceptor {    	@Override  	public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  		System.out.println("我是買房中介 , 開始監聽你買房了....");  		Object invokeSuper = methodProxy.invokeSuper(o, args);  		System.out.println("我是買房中介 , 開結束你買房了....");  		return invokeSuper;    	}    }    class Test22222 {  	public static void main(String[] args) {  		Cglib cglib = new Cglib();  		Enhancer enhancer = new Enhancer();  		enhancer.setSuperclass(XiaoMing.class);  		enhancer.setCallback(cglib);  		Hose hose = (Hose) enhancer.create();  		hose.mai();  	}  }

7CGLIB與JDK動態代理區別

jdk動態代理是由Java內部的反射機制來實現的,cglib動態代理底層則是藉助asm來實現的。總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過將asm生成的類進行快取,這樣解決asm生成類過程低效問題)。還有一點必須注意:jdk動態代理的應用前提,必須是目標類基於統一的介面。如果沒有上述前提,jdk動態代理不能應用。

注:asm其實就是java位元組碼控制.