1. 程式人生 > >Java學習筆記--設計原則與設計模式、類載入、反射的介紹

Java學習筆記--設計原則與設計模式、類載入、反射的介紹

設計原則、設計模式

  • 面向物件思想設計原則
    • 單一職責原則:每個類應該只有一個職責,對外只能提供一種功能。其實就是”高內聚,低耦合”。
    • 開閉原則:對擴充套件開放,對修改關閉。即在設計一個模組的時候,應當使這個模組可以在不被修改的前提下被擴充套件。
    • 里氏替換原則:在任何父類出現的地方都可以用它的子類來替代。
    • 依賴注入原則:要依賴於抽象,不要依賴於具體實現。
    • 介面分離原則:不應該強迫程式依賴它們不需要使用的方法。介面分離原則的意思就是:一個介面不需要提供太多的行為,一個介面應該只提供一種對外的功能,不應該把所有的操作都封裝到一個介面當中。
    • 迪米特原則:一個物件應當對其他物件儘可能少的瞭解,不和陌生人說話。
    • 合成複用原則:儘量使用物件組合,而不是繼承來達到複用的目的。該原則就是在一個新的物件裡面使用一些已有的物件,使之成為新物件的一部分,新的物件通過向這些物件的委派達到複用已有功能的目的。
  • 設計模式
    • 建立型模式:簡單工廠模式,工廠方法模式,抽象工廠模式,建造者模式,原型模式,單例模式。(6個)
    • 結構型模式:外觀模式、介面卡模式、代理模式、裝飾模式、橋接模式、組合模式、享元模式。(7個)
    • 行為型模式:模版方法模式、觀察者模式、狀態模式、職責鏈模式、命令模式、訪問者模式、策略模式、備忘錄模式、迭代器模式、直譯器模式。(10個)
  • 簡單(靜態)工廠模式:由一個工廠物件決定創建出哪種例項。
  • 工廠方法模式(虛擬構造子模式/多型性工廠模式):定義一個建立例項物件的工廠介面,將實際建立工作推遲到子類中。
  • 單例模式:只能得到一個物件例項,構造方法私有。
//餓漢式:類一載入就建立物件
public class Student {
	// 構造私有
	private Student() {
	}

	// 靜態方法只能訪問靜態成員變數,加靜態
	// 為了不讓外界直接訪問修改這個值,加private
	private static Student s = new Student();

	// 提供公共的訪問方式
	// 為了保證外界能夠直接使用該方法,加靜態
	public static Student getStudent() {
		return s;
	}
}
//懶漢式:用的時候,才去建立物件
public class
Teacher { private Teacher() { } private static Teacher t = null; //懶漢式單例模式可能出現執行緒安全問題,所以需要加上synchronized修飾 public synchronized static Teacher getTeacher() { if (t == null) { t = new Teacher(); } return t; } }
  • 模版方法模式:定義一個操作中演算法的骨架,而將一些步驟延遲到子類中,模板方法使得子類可以不改變演算法的結構即可重定義該演算法的某些特定步驟。
public abstract class GetTime {
	// 計算出一段程式碼的執行時間
	public long getTime() {
		long start = System.currentTimeMillis();
		code();
		long end = System.currentTimeMillis();
		return end - start;
	}
	public abstract void code();//這裡定義抽象方法,具體實現放在子類當中
}
  • 裝飾設計模式:裝飾模式就是使用被裝飾類的一個子類的例項,在客戶端將這個子類的例項交給裝飾類。是繼承的替代方案。裝飾模式通常是用一個抽象裝飾類來實現要裝飾的介面,具體裝飾類再繼承抽象裝飾類。IO流中這種模式較為常見。
interface Phone {
    public abstract void call();
}

class IPhone implements Phone {

    @Override
    public void call() {
        System.out.println("手機正在打電話");
    }
}

abstract class PhoneDecorate implements Phone {

    private Phone p;

    public PhoneDecorate(Phone p) {
        this.p = p;
    }

    @Override
    public void call() {
        this.p.call();
    }
}

class RingPhoneDecorate extends PhoneDecorate {

    public RingPhoneDecorate(Phone p) {
        super(p);
    }

    @Override
    public void call() {
        System.out.println("手機正在播放彩鈴");
        super.call();
    }
}

class MusicPhoneDecorate extends PhoneDecorate {

    public MusicPhoneDecorate(Phone p) {
        super(p);
    }

    @Override
    public void call() {
        super.call();
        System.out.println("手機正在播放音樂");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Phone p = new IPhone();
        p.call();
        System.out.println("------------");
        // 需求:在接電話前,聽彩鈴
        PhoneDecorate pd = new RingPhoneDecorate(p);
        pd.call();
        System.out.println("------------");

        // 需求:在接電話後,聽音樂
        pd = new MusicPhoneDecorate(p);
        pd.call();
        System.out.println("------------");

        // 需求:手機在接前聽彩鈴,接後聽音樂
        pd = new RingPhoneDecorate(new MusicPhoneDecorate(p));
        pd.call();
    }
}

類載入

  • 當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過載入,連線,初始化三步來實現對這個類進行初始化。
  • 載入。就是指將class檔案讀入記憶體,併為之建立一個Class物件。任何類被使用時系統都會建立一個Class物件。
  • 連線。驗證:是否有正確的內部結構,並和其他類協調一致。準備:負責為類的靜態成員分配記憶體,並設定預設初始化值。解析:將類的二進位制資料中的符號引用替換為直接引用。
  • 初始化。在堆疊中開闢空間,對成員變數進行初始化。
  • 類載入器負責將.class檔案載入到內在中,併為之生成對應的Class物件。
  • Bootstrap ClassLoader 根類載入器
    • 也被稱為引導類載入器,負責Java核心類的載入
    • 比如System,String等。在JDK中JRE的lib目錄下rt.jar檔案中
  • Extension ClassLoader 擴充套件類載入器
    • 負責JRE的擴充套件目錄中jar包的載入。
    • 在JDK中JRE的lib目錄下ext目錄
  • Sysetm ClassLoader 系統類載入器
    • 負責在JVM啟動時載入來自java命令的class檔案,以及classpath環境變數所指定的jar包和類路徑

反射

  • 反射就是通過class檔案物件,去使用該檔案中的成員變數,構造方法,成員方法。
  • 獲得class檔案物件有三種方式:
    • Object類的getClass()方法
    • 資料型別的靜態屬性class
    • Class類中的靜態方法forName(String className),其中className為類的全路徑名
  • 通過呼叫Class類的方法獲得位元組碼檔案物件所表示的類的構造方法物件,再通過呼叫構造方法物件的方法能夠實現建立物件等操作。
//得到所有public構造方法的物件陣列
public Constructor<?>[] getConstructors() throws SecurityException
//得到所有構造方法的物件陣列,包括public, protected, default and private
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
//得到一個public構造方法的物件,parameterTypes是一個Class物件陣列,用來說明構造方法的形參型別,即通過該引數判斷是哪個構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
                              throws NoSuchMethodException,
                                     SecurityException
  • 以上是Class類的方法,下面是Constructor類的方法:
//使用構造方法物件所表示的構造方法來例項化一個物件。用指定的引數進行新建物件的初始化。
public T newInstance(Object... initargs)
              throws InstantiationException,
                     IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException
//父類AccessibleObject中的方法,如果accessible flag設定為true,表明反射的物件在使用時可以越過許可權修飾符的限制
public void setAccessible(boolean flag)
                   throws SecurityException                     
  • 通過反射給成員變數賦值,需要用到如下方法:
//返回一個public成員變數物件,name引數用來表明哪個成員變數
public Field getField(String name)
               throws NoSuchFieldException,
                      SecurityException
//返回一個成員變數物件,name引數用來表明哪個成員變數
public Field getDeclaredField(String name)
                       throws NoSuchFieldException,
                              SecurityException
  • 以上是Class類的方法,下面是Field類的方法:
/*將成員變數物件所對應的成員變數的值設定為value
該方法的第一個引數表示要設定的成員變數所屬物件
獲得成員變數所屬物件的方式就是前面講到的通過Constructor類的newInstance方法建立
*/
public void set(Object obj, Object value)
         throws IllegalArgumentException,
                IllegalAccessException
  • 同理,通過反射呼叫成員方法,需要用到如下方法:
//返回一個public成員方法物件,name引數用來表明哪個成員方法,parameterTypes是一個Class物件陣列,用來說明成員方法形參的型別
public Method getMethod(String name, Class<?>... parameterTypes)
                 throws NoSuchMethodException,
                        SecurityException
  • 以上是Class類的方法,下面是Method類的方法:
/*呼叫該成員方法物件所對應的成員方法,傳入的實參為args
該方法的第一個引數表示要呼叫的成員方法所屬物件
獲得成員變數所屬物件的方式就是前面講到的通過Constructor類的newInstance方法建立
*/
public Object invoke(Object obj, Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException
JDK動態代理
  • 動態代理需要用到兩個方法:
/*Proxy類的方法,建立一個代理物件,代理物件能夠代為執行目標物件的方法,且可以在執行前後可以加上其他操作
interfaces引數是代理類需要實現的介面,與目標物件實現的介面相同
h引數實下面InvocationHandler類的例項,其中重寫的invoke方法就是代理物件呼叫方法時真正呼叫的
*/
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException
/*InvocationHandler類方法
proxy引數用來接收代理物件
method引數用來接收被呼叫的方法名稱
args傳遞方法呼叫需要用到的引數
*/
Object invoke(Object proxy,
              Method method,
              Object[] args)
       throws Throwable
  • 下面給出動態代理的示例程式碼:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface UserDao {
    public abstract void add();

    public abstract void delete();

    public abstract void update();

    public abstract void find();
}

class UserDaoImpl implements UserDao {

    @Override
    public void add() {
        System.out.println("新增功能");
    }

    @Override
    public void delete() {
        System.out.println("刪除功能");
    }

    @Override
    public void update() {
        System.out.println("修改功能");
    }

    @Override
    public void find() {
        System.out.println("查詢功能");
    }

}

class MyInvocationHandler implements InvocationHandler {
    private Object target; // 目標物件

    MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("許可權校驗");
        Object result = method.invoke(target, args);
        System.out.println("日誌記錄");
        return result;
    }
}

public class TestDemo {
    public static void main(String[] args) {
        UserDao ud = new UserDaoImpl();
        ud.add();
        ud.delete();
        ud.update();
        ud.find();
        System.out.println("-----------");
        MyInvocationHandler handler = new MyInvocationHandler(ud);
        UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
                .getClassLoader(), ud.getClass().getInterfaces(), handler);
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.find();
    }
}