多態通過分離做什么和怎么做,從另一個角度將接口與實現分離開來;通過多態來消除類型之間的耦合關系,在Java中,多態也叫動態綁定,后期綁定或運行時綁定,那么什么是方法綁定?
方法調用綁定:
將一個方法與調用同一個方法的主體關聯起來被稱為綁定;若在程序執行前進行綁定(由編譯器和連接程序實現),叫做前期綁定;還有一種叫后期綁定,就是在運行時根據對象的類型進行綁定,也叫動態綁定或運行時綁定,也就是說,編譯器不知道對象的類型,但是方法調用機制能找到正確的方法體;在Java中除了static
方法和final
方法之外,所有的其他方法都是后期綁定;
因此,Java中所有的方法都是通過動態綁定來實現多態的,但如果直接訪問某個域,則這個訪問就會在編譯其進行解析;一般我們都通過將子類向上轉型為父類來實現多態,父類可以是抽象類,只要子類實現到父類的所有抽象方法,就可以將子類轉型為抽象的父類;Java里的抽象類本身是不能被實例化,但可以將子類的引用向上轉型為抽象的父類。
如:
abstract class Jack{
public abstract void doSomething();
}
class Product extends Jack{
@Override
public void doSomething() {
System.out.println("Product");
}
}
public static void main(String[] args) {
//聲明一個抽象類Jack的變量,並指向其子類的實例化對象,合法的,Java的多態性會保證在運行時可以得到其正確的類型;
Jack jack=new Product();
//Jack jack=new Jack();//非法,不能實例化抽象類對象
jack.doSomething();
}
Java反射
Java反射機制是指在運行狀態時,可以知道任意一個類的的所有屬性和方法,對任意一個對象都可以調用它的任意一個方法;通過反射,可以在運行時實例化對象
Java反射提供的功能包括:
- 在運行時判斷一個對象所屬的類;
- 運行時構造任意一個類的對象;
- 運行時判斷任意一個類的成員變量與方法;
- 運行時調用任意一個對象的方法;
- 生成動態代理;
我們想得到一個類的所有信息,第一步就是要得到類的Class對象,如果知道了一個對象或類的名字,就可以通過簡單的:
Class<?> clz=對象.getClass();
Class<?> clz=類的名字.class
得到,但如果在編譯期得不到具體類型,則可以通過Class.forName()
來得到,但這個方法生成的結果在編譯時是不可知的,所有的方法特征簽名都是在運行時提取出來的。這是由Java的反射機制來提供足夠的支持。在得到這個類的Class對象后,我們就可以反射來構造對象,進而得到這個類的所有的信息。
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
Java里的Class
類與java.lang.reflect
類庫一起對反射進行支持,該類庫包含了Field
,Method
,Constructor
類(每個類都實現了Member
接口),這些類型的對象是在JVM在運行時創建的,用以表示未知類里對應的成員。這樣我們就可以使用Constructor
創建新的對象,用get()
,set()
方法修改與Field對象關聯的字段,用invoke()
方法調用與Method
對象關聯的方法。(摘自《Java編程思想》第四版)
下面簡要介紹利用反射實現的動態代理;
動態代理
步驟:
- 新建委托類,實現動態代理要求委托類必須實現某個接口;
- 新建中間類,用來連接代理類和委托類,這個中間必須實現
InvocationHandler
接口,這個接口只有一個invoke()
方法; - 通過
Proxy
類新建代理類對象;
舉例:
interface Operate{
void method1();
void method2();
void method3();
}
/**
* 委托類
* @author wood
*
*/
class Entrust implements Operate{
@Override
public void method1() {
// TODO Auto-generated method stub
System.out.println("*method1");
}
@Override
public void method2() {
// TODO Auto-generated method stub
System.out.println("*method2");
}
@Override
public void method3() {
// TODO Auto-generated method stub
System.out.println("*method3");
}
}
/**
* 連接委托類與代理類的中間類;
* @author wood
*
*/
class DynamecProxyHandler implements InvocationHandler{
private Object proxied;//委托類對象
public DynamecProxyHandler(){
}
public DynamecProxyHandler(Object object){
this.proxied=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] arg2)
throws Throwable {
// TODO Auto-generated method stub
Object object=method.invoke(proxied, arg2);
System.out.println(method.getName());
return object;
}
}
//***********************
DynamecProxyHandler dymaProxy=new DynamecProxyHandler(new Entrust());
//通過Proxy類的靜態函數生成代理對象;
Operate operate=(Operate)Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[]{Operate.class}, dymaProxy);
operate.method1();
operate.method2();
operate.method3();
我們通過Procy.newProcyInstance
函數新建了一個代理對象,實際的代理類就是在這時候動態生成了,我們調用該代理對象的函數就會調用到中間類的invoke
函數,而invoke
函數實現調用委托類的對應函數;
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader
是類加載器;
interfaces
是委托類的接口,生成代理需要實現這個接口;
實際上Java的動態代理就是兩層的靜態代理:開發者提供一個委托類A,程序動態生成了一個代理類B,開發者還需要提供一個實現了InvocationHandler
的接口C,用類C來連接委托類A和委托類B,類C是代理類B的委托類,是類A的代理類;用戶直接調用代理類B,B將調用轉發給委托類C,C再將調用轉發給委托類A;
參考:
《Java編程思想》第四版
公共技術點之 Java 動態代理