1. 程式人生 > 其它 >從零開始學Java-Day18

從零開始學Java-Day18

設計模式

設計模式是Java發展過程中總結出來的一些值得借鑑的優秀程式設計經驗

設計模式一共有23種,主要分為三大類

單例設計模式

核心思想:確保例項只有一個

好處:可以節省記憶體空間,方便控制資源

實現思路

  1. 構造方法私有化--阻止外部直接呼叫本類的構造方法建立物件
  2. 建立本類物件且私有化--為了防止外部多次獲取本類物件
  3. 對外提供一個公共全域性訪問點--按照預先設定的方式來獲取物件

餓漢式:載入時事先建立好類的物件

懶漢式:不會再初始化就載入所有資源,在用的時候才載入

單例設計模式方案二:懶漢式

不會再初始化類的時候就建立本類物件,而是用的時候(呼叫get())才建立

程式碼實現思路:

  • ​ 將建立物件拆分成兩步:
    • 先定義引用型別變數,預設值null
    • 在get()裡判斷(之前是否建立過),如果沒建立過,建立,建立過返回建立的物件.
package cn.tedu.design;
//本類用於測試單例模式實現方式一:餓漢式
public class Singleton1 {
    public static void main(String[] args) {
        MySingle single = MySingle.getSingle();
        MySingle single2 = MySingle.getSingle();
        System.out.println(single);
        System.out.println(single2);
        System.out.println(single == single2);
    }
}
/*
 *思考:在構造方法與物件被私有化後,需要呼叫公共的全域性訪問點來獲取本類物件,但是,
 * 我們如何呼叫這個公共方法
 * 之前呼叫方法都是通過類物件進行呼叫,但單例因為構造方法私有化無法建立物件。
 * 解決方案:可以將公共方法設定為靜態方法,因此可以直接通過類來呼叫。
 * 因為公共方法是靜態的所以類裡建立的物件也要靜態修飾
 */
class MySingle{
    //構造方法私有化:防止外界隨意例項化本類物件
    private MySingle() {}
    private static MySingle single = new MySingle();

    public static MySingle getSingle(){
        return single;
    }
}

package cn.tedu.design;
//單例測試模式方案二:懶漢式--面試重點
public class Singleton2 {
    public static void main(String[] args) {
        MySingle2 single1 = MySingle2.getMySingle2();
        MySingle2 single2 = MySingle2.getMySingle2();
        System.out.println(single2);
        System.out.println(single1);
        System.out.println(single1 == single2);//true
    }
}

class MySingle2{
    private MySingle2(){}
    static private MySingle2 mySingle2;
    /*
     *在多執行緒下會有資料隱患
     * 解決方案一:同步程式碼塊鎖
     * 解決方案二:把該方法用 synchronized 修飾
     */
    public synchronized static MySingle2 getMySingle2() {
        if (mySingle2 == null) {
            mySingle2 = new MySingle2();
        }
        return mySingle2;
    }
}

註解與自定義註解

註解很厲害,它可以增強我們的Java程式碼,同時利用反射技術可以擴充實現很多功能。它們被廣泛應用於三大框架底層。
傳統我們通過xml文字檔案宣告方式(如下圖,但是XML比較繁瑣且不易檢查),而現在最主流的開發都是基於註解方式,程式碼量少,框架可以根據註解去自動生成很多程式碼,從而減少程式碼量,程式更易讀。例如最火爆的SpringBoot就完全基於註解技術實現。

分三類

  • JDK註解

    • @override
  • 元註解:用來定義其他註解的註解

    • @Target註解用的範圍:類上、方法上、屬性上等
    • @Retention註解的生命週期:原始檔中、位元組碼檔案中、執行中
      • SOURCE:在原始檔中有效(即原始檔保留)
      • CLASS:在class檔案中有效(即class保留)
      • RUNTIME:在執行時有效(即執行時保留)
    • @Inherited 允許子註解繼承
    • @Documented 生成javadoc時會包含註解,不常用
    • @Repeatable 註解為可重複型別註解,可以在同一個地方多次使用,不常用
  • 自定義註解

    1. 自定義註解需要元註解來指定,語法:@interface 註解名{}
    2. 使用@Target註解定義自定義註解的作用位置,可以指定多個,格式:{,}
    3. 使用@Retention註解定義自定義註解的生命週期,只能指定一個
    4. 使用註解時,@符號 + 註解名直接使用
    5. 自定義註解可以新增屬性
      1. 普通屬性int age(); 賦值格式: @Test(age = 18)
      2. 特殊屬性int value(); 賦值格式:@Test(18)
    • 一旦添加了屬性必須賦值
    • 特殊屬性不限制類型,限制的是屬性的名字
    • 特殊屬性和普通屬性賦予預設的格式一樣 int age() default 15;
package cn.tedu.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//本類用於測試自定義註解,熟悉註解的相關規則
public class TestAnnotation {
    public static void main(String[] args) {
        new MeiGeMi().skill();
    }

}

/*
 *0.首先注意:自定義註解的語法和Java不同,不要套用Java格式
 *1.註解定義要使用@interface 註解名的方式來定義
 *  1.1通過@Target註解來定義當前自定義註解使用的位置| 注意導包 | ElementType.靜態常量 | 輸入的是一個數組,可以用 , 隔開元素
 *  1.2通過使用@Retention註解來定義當前自定義註解使用的生命週期| 注意導包 |RetentionPolicy.靜態常量
 * 決定註解的存活時間,只能存在一個
 *
 */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
@interface Test{
    /*自定義註解可以新增功能--給自定義註解新增屬性
    * 注意:int age();不是方法的定義,而是自定義註解中定義age屬性的語法
    * 定義屬性後,才使用自定義註解時要在註解後的括號裡定義同類型的值,如果在定義屬性時
    * 賦予預設值了可以直接使用
    * 註解中的新功能:定義特殊屬性value*/
    int height() default 0;
    String value() default "一馬當先,萬馬無光";
}
class MeiGeMi{
    @Test
    String name;
    public void skill(){
        System.out.println("Explosion!!!!!!!!!!!!!!");
    }
}

反射

獲取位元組碼物件

  • Class.forName("類的全路徑")
  • 類名.class
  • 物件.getClass();

反射是一種經典常用的技術,通常用來獲取或才做其他人的資源

反射的前提是獲取位元組碼物件 + 反射技術獲取目標類的所有構造方法

具體方法詳見API手冊

單元測試方法

package cn.tedu.reflection;
//本類用於測試反射而準備的物料類
public class Student {
    String name;
    private int age;
    private static String skill;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void learn(){
        System.out.println("學習!");
    }
    private void play(String gameName){
        System.out.println("沖沖衝!!!" + gameName);
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
package cn.tedu.reflection;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;

//本類用來測試反射
public class TestReflect1 {
    /*單元測試方法:是Java測試的最小單位,使用靈活
    * 語法要求:@Test
    *          void + 沒有引數 + public
    * 使用時需要導包*/
    @Test
    public void getClazz() throws ClassNotFoundException {
        Class<?> stu1 = Class.forName("cn.tedu.reflection.Student");
        Class<Student> stu2 = Student.class;
        Class<? extends Student> stu3 = new Student().getClass();
        System.out.println(stu1);//class cn.tedu.reflection.Student
        System.out.println(stu1.getName());//cn.tedu.reflection.Student
        System.out.println(stu2.getPackage().getName());//cn.tedu.reflection
        System.out.println(stu3.getSimpleName());//Student
    }
    @Test
    public void getConstruct() throws NoSuchMethodException {
        Class<Student> stu = Student.class;
        Constructor<?>[] cs = stu.getConstructors();
        Method[] method = stu.getMethods();
        for (Method method1 : method) {
            Class[] cp = method1.getParameterTypes();
            System.out.println(Arrays.toString(cp));
        }

        for (Constructor<?> c : cs) {
            //System.out.println(c.getName());
            Class[] cp = c.getParameterTypes();
            System.out.println(Arrays.toString(cp));
        }
    }

}