原型模式
一顽决、原型模式(Prototype Pattern)
介紹:原型模式(Prototype Pattern)是指原型實例指定創(chuàng)建對象的 種類族铆,并且通過拷貝這些原型創(chuàng)建新的對象。
調(diào)用者不需要知道任何創(chuàng)建細節(jié)恭朗,不調(diào)用構(gòu)造函數(shù)
屬于創(chuàng)建型模式
適用場景
類初始化消耗資源較多屏镊。
new產(chǎn)生的一個對象需要非常繁瑣的過程(數(shù)據(jù)準(zhǔn)備、訪問權(quán)限等)
構(gòu)造函數(shù)比較復(fù)雜痰腮。
循環(huán)體中生產(chǎn)大量對象時而芥,可讀性下降。
總結(jié) :原型模式就是如果快速構(gòu)建對象的方法總結(jié)诽嘉, 簡單工廠將getter蔚出、setter封裝到某個方法中 JDK提供的實現(xiàn)Cloneable接口,實現(xiàn)快速復(fù)制 scope=“prototype”虫腋,scope=”singleton”
淺克隆:實現(xiàn)對象拷貝的 == 類A == 骄酗,需要實現(xiàn) ==Cloneable== 接口,并覆寫 ==clone()== 方法
淺拷貝是按位拷貝對象悦冀,它會創(chuàng)建一個新對象趋翻,這個對象有著原始對象屬性值的一份精確拷貝。如果屬性是基本類型盒蟆,拷貝的就是基本類型的值踏烙;如果屬性是內(nèi)存地址(引用類型),拷貝的就是內(nèi)存地址 历等,因此如果其中一個對象改變了這個地址讨惩,就會影響到另一個對象。即默認(rèn)拷貝構(gòu)造函數(shù)只是對對象進行淺拷貝復(fù)制(逐個成員依次拷貝)寒屯,即只復(fù)制對象空間而不復(fù)制資源荐捻。
深克隆:對于 ==類B== 的引用類型的成員變量 ==T== 黍少,需要實現(xiàn) ==Cloneable== 并重寫 ==clone()== 方法
對于基本數(shù)據(jù)類型的成員對象,因為基礎(chǔ)數(shù)據(jù)類型是值傳遞的处面,所以是直接將屬性值賦值給新的對象厂置。基礎(chǔ)類型的拷貝魂角,其中一個對象修改該值昵济,不會影響另外一個。
對于引用類型野揪,比如數(shù)組或者類對象访忿,因為引用類型是引用傳遞,所以淺拷貝只是把內(nèi)存地址賦值給了成員變量囱挑,它們指向了同一內(nèi)存空間醉顽。改變其中一個,會對另外一個也產(chǎn)生影響平挑。
// 淺拷貝
class A
... //屬性
privat T obj;
@Override
protected Object clone() throws CloneNotSupportedException {
//Subject 如果也有引用類型的成員屬性
return super.clone();
}
//深拷貝
class B
... //屬性
privat T obj;
@Override
protected Object clone() throws CloneNotSupportedException {
//Subject 如果也有引用類型的成員屬性
B b = super.clone();
b.obj = (T) obj.clone();
retrun b;
}
優(yōu)點
原型模式性能比直接new一個對象性能高
簡化了創(chuàng)建過程
缺點
必須配備克隆(或者可拷貝)方法
對克隆復(fù)雜對象或?qū)寺〕龅膶ο筮M行復(fù)雜改造時系草,易帶來風(fēng)險通熄。
深拷貝、淺拷貝要運用得當(dāng)
克隆破壞單例模式
Cloneable 源碼分析
ArrayList 就實現(xiàn)了 Cloneable 接口
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
代理模式
一晓猛、代理模式(Proxy Pattern)
介紹:代理模式(Proxy Pattern)的定義也非常簡單饿幅,是指為其他對象提供一種代理,以控制對這個對象的訪問戒职。 代理對象在客服端和目標(biāo)對象之間起到中介作用.
代理模式屬于結(jié)構(gòu)型設(shè)計模式栗恩。使用 代理模式主要有兩個目的:
一保護目標(biāo)對象,
二增強目標(biāo)對象洪燥。
代理類型
//靜態(tài)代理
// Subject主題接口
public interface Subject {
void doTask();
}
// 真是主題類,真正處理請求的類
public class RealSubject implements Subject {
@Override
public void doTask() {
System.out.println("RealSubject doTask ...");
}
}
// 攔截對真實主題對象訪問搔谴,代理類
public class Proxy implements Subject {
private Subject realSubject;
public Proxy(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public void doTask() {
System.out.println("記日志等 .... ");
realSubject.doTask();
}
}
// 客戶端代理
public class Client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
Subject proxy = new Proxy(realSubject);
proxy.doTask();
}
}
//動態(tài)代理
Subject 魁袜、RealSubject 同上
1) 首先創(chuàng)建調(diào)用處理器
//TODO 重點
/*
1. 實現(xiàn)InvocationHandler接口 implements InvocationHandler
2.重寫invoke方法 public Object invoke(Object proxy, Method method, Object[] args)
3.重寫方法中調(diào)用method.invoke(subject, args);
*/
public class SubjectHandler<T extends Subject> implements InvocationHandler {
private T subject;
public SubjectHandler(T subject) {
this.subject = subject;
}
// TODO 2
public T getInstance(T target) throws Exception{
this.target = target;
Class<?> clazz = target.getClass();
return (T)Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在代理真實對象前我們可以添加一些自己的操作
System.out.println("記日志等 .... ");
Object invoke = method.invoke(subject, args);
// 在代理真實對象后我們也可以添加一些自己的操作
System.out.println("task ???");
return invoke;
}
}
2) 創(chuàng)建動態(tài)代理
public static void main(String[] args) {
// 我們要代理的真實對象
Subject realSubject = new RealSubject();
// 我們要代理哪個真實對象,就將該對象傳進去敦第,最后是通過該真實對象來調(diào)用其方法的
InvocationHandler handler = new SubjectHandler<>(realSubject);
/*
* 通過Proxy的newProxyInstance方法來創(chuàng)建我們的代理對象峰弹,我們來看看其三個參數(shù)
* 第一個參數(shù) handler.getClass().getClassLoader() ,我們這里使用handler這個類的ClassLoader對象來加載我們的代理對象
* 第二個參數(shù)realSubject.getClass().getInterfaces()芜果,我們這里為代理對象提供的接口是真實對象所實行的接口鞠呈,表示我要代理的是該真實對象,這樣我就能調(diào)用這組接口中的方法了
* 第三個參數(shù)handler右钾, 我們這里將這個代理對象關(guān)聯(lián)到了上方的 InvocationHandler 這個對象上
*/
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
// 調(diào)用代理類方法
subject.doTask();
//TODO 2 調(diào)用
RealSubject realSubject = new SubjectHandler<RealSubject>().getInstance(new RealSubject());
Method method = obj.getClass().getMethod("doTask",null);
method.invoke(obj);
}
//CGLib代理
1)首先創(chuàng)建調(diào)用處理器
/*
1. MethodInterceptor implements MethodInterceptor
2.重寫intercept方法 public Object (Object o, Method method, Object[] objects, MethodProxy methodProxy)
3.重寫方法中調(diào)用methodProxy.invokeSuper(o,objects);
*/
public class SubjectCGLibHandler<T extends Subject> implements MethodInterceptor {
public Object getCGLibProxy(Class<?> clazz) throws Exception{
//相當(dāng)于Proxy蚁吝,代理的工具類
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
}
2) 創(chuàng)建動態(tài)代理
public static void main(String[] args) {
try {
//查看項目中產(chǎn)生的cglib動態(tài)類
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E://cglib_proxy_classes");
RealSubject proxy = (RealSubject) new SubjectCGLibHandler().getCGLibProxy(RealSubject.class);
System.out.println(proxy);
proxy.doTask();
} catch (Exception e) {
e.printStackTrace();
}
}
為什么JDK動態(tài)代理中要求目標(biāo)類實現(xiàn)的接口數(shù)量不能超過65535個
Class文件是一組以8字節(jié)為基礎(chǔ)單位的二進制流
各個數(shù)據(jù)項目嚴(yán)格按照順序緊湊排列在class文件中
中間沒有任何分隔符旱爆,這使得class文件中存儲的內(nèi)容幾乎是全部程序運行的程序
Java虛擬機規(guī)范規(guī)定,Class文件格式采用類似C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來存儲數(shù)據(jù)窘茁,這種結(jié)構(gòu)只有兩種數(shù)據(jù)類型:無符號數(shù)和表
接口索引計數(shù)器(interfaces_count)怀伦,占2字節(jié)
參考第一句話:class文件是一組8字節(jié)為基礎(chǔ)的二進制流,interface_count占2字節(jié)山林。也就是16.00000000,00000000 所以房待,證明
interface_count的數(shù)量最多是2^16次方 最大值=65535
這是在JVM的層面上決定了它的數(shù)量最多是65535
且在java源碼中也可以看到
if (var2.size() > 65535) {
throw new IllegalArgumentException("interface limit exceeded: " var2.size());
直接做了65535的長度的校驗,所以驼抹,JDK的動態(tài)代理要求桑孩,目標(biāo)類實現(xiàn)的接口數(shù)量不能超過65535
最后編輯于 :2021.03.30 15:50:25
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者