Java中的多態的簡單總結

       時間在流逝,春節也快要到了。別的同學都已經在家好多天,自己還在學校繼續忙着。要想人前顯貴,就得人後受罪,依然記得高中的時候物理老師送給我們的話,雖然有時候感覺很寂寞,但是想想現在的付出是爲了以後的美好生活,還是會很有動力。

今天總結面向對象設計中的多態(polymorphism)了,剛開始接觸多態這個概念的時候覺得很混亂,隨着學習的不斷深入,漸漸開始理解這種機制。

  1. 多態的概念
  2. 多態的實現機制
  3. final關鍵字

一、多態的概念

       從字面上理解,多態可以理解爲多種形態,多種類型。我們知道,通過繼承可以使用父類型(supertype)來引用子類型變量(subtype),也就是說,每個子類對象也可以看做是超類的對象,在程序中可以將多種類型(從同一基類導出的類型)視爲同一類型來處理。而同一段代碼也就可以毫無差別的運行在不同類型之上了。這些,就是實現多態機制的基礎。一個對象變量可以引用多種實際類型的現象被稱之爲多態

來看一段代碼:

import java.util.*;

/**
 * This program demonstrates inheritance.
 * @version 1.21 2013/01/25
 * @author LiMing
 */
public class ManagerTest
{
   public static void main(String[] args)
   {
      // construct a Manager object
      Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      boss.setBonus(5000);

      Employee[] staff = new Employee[3];

      // fill the staff array with Manager and Employee objects

      staff[0] = boss;
      staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
      staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);

      // print out information about all Employee objects
      for (Employee e : staff)
         System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
   }
}

class Employee
{
   public Employee(String n, double s, int year, int month, int day)
   {
      name = n;
      salary = s;
      GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
      hireDay = calendar.getTime();
   }

   public String getName()
   {
      return name;
   }

   public double getSalary()
   {
      return salary;
   }

   public Date getHireDay()
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }

   private String name;
   private double salary;
   private Date hireDay;
}

class Manager extends Employee
{
   /**
    * @param n the employee's name
    * @param s the salary
    * @param year the hire year
    * @param month the hire month
    * @param day the hire day
    */
   public Manager(String n, double s, int year, int month, int day)
   {
      super(n, s, year, month, day);
      bonus = 0;
   }

   public double getSalary()
   {
      double baseSalary = super.getSalary();
      return baseSalary + bonus;
   }

   public void setBonus(double b)
   {
      bonus = b;
   }

   private double bonus;
}

        我們可以想象,一個經理Manager必然是一個僱員Employee,經理是子類型,而僱員是父類型。在代碼中我們通過

staff[0]=boss; 這條語句將Manager型的引用賦給了Employee類型,在Employee類中擁有一個方法getSalary(),而在Manager中有同樣的一個方法getSalary(),程序運行的最後結果我們驚奇的發現,Employee型的對象引用竟然正確的調用了我們希望的Manager的getSalary方法。這個例子作爲多態的簡單演示,下面我們來討論多態的實現機制。

二、多態的實現機制

       首先我們先了解幾個術語:綁定(binding),《Java編程思想》中說“將一個方法調用同一個方法主體關聯起來被稱作綁定”

這句話個人理解是這樣的:假設集合A是一個由所有類中的所有的方法組成的方法集合,集合B是由全部的對象以及每個對象所擁有

的動作(方法)組成的集合,當我們程序在出現方法調用的時候,必然是由集合B和集合A中的兩個元素進行交互。B中的一個元素調

用A中的方法時,通過一個過程來找到確定的該調用的方法,這個過程被稱之爲綁定。
在程序執行之前進行的綁定被稱之爲前期綁定(靜態綁定)這是由編譯器以及連接程序確定的(至於前期綁定的具體過程還請高手不

吝賜教),在程序運行過程中實現的綁定被稱之爲後期綁定(動態綁定、運行時綁定)。在Java中所有的方法都是通過動態綁定來實

現多態的
     我們主要來研究動態綁定這個概念,首先我們需要區分變量的實際類型(actual type)與聲明類型(declared type),我們

知道一個變量必須要被聲明爲某種類型。請看下面這兩句代碼:

Employee obj = new Manager();
obj.getSalary();

這裏obj的聲明類型是Employee,一個引用變量可以是一個null值或者是一個對聲明類型實例的引用。實例可以是該類型本身或者它

的子類型。變量的實際類型是被變量引用的對象的實際類。在這裏obj引用的是Manager類型的變量,所以obj的聲明類型是Employee

,實際類型是Manager。當調用getSalary方法時是由obj的實際類型所決定的,因爲在程序運行之前我們並不能夠確定某個變量將引

用何種類型,所以需要在運行時加以確定,從而調用相應的方法,這就是動態綁定。

 

我們來看看動態綁定的工作機制:
   1>編譯器查看對象的聲明類型以及方法名。假設調用obj.function(param),且隱式參數obj被聲明爲C類的對象(C類作爲子類來看

待)。需要注意的是:有可能存在多個名字爲funciton的,但參數類型不一樣的方法。例如可能存在function(int),function

(String)等。編譯器會列舉C類中的所有名爲function的方法和其超類中訪問屬性爲public的且名爲function的方法。至此編譯器

已經獲得了所有可能被調用的候選方法。
   2>接下來編譯器將查看調用方法時提供的類型參數。如果所有名爲function的方法中存在一個與提供的類型參數完全匹配,就選

擇這個方法。這個過程被稱之爲重載解析(overloading resolution)
總之,當程序運行時並且採用動態綁定調用方法時,虛擬機一定調用與obj所引用的對象的實際類型最適合的那個類的方法。假設

obj的實際類型是A,需要調用function(int)型的方法,它是B類的子類,如果A類定義了function(int)方法,就直接調用它;

否則在A類的超類中尋找function(int),以此類推。
我們來看一段代碼:

public class DynamicBinding {

	/**
	 * 演示動態綁定
	 * @author LiMing
	 * @since 2013/01/25
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Person[] p =new Person[3];
		p[0]=new Person();
		p[1]=new Student();
		p[2]=new GraduateStudent();
		
		for(Person obj:p)
			printObject(obj);
		
	}
	public static void printObject(Object o){
		System.out.println(o.toString());
	}
}
class Person{
	public Person(){
		
	}
	public String toString(){
		return "This is class Person!";
	}	
}
class Student extends Person{
	public Student(){
		
	}
	public String toString(){
		return "This is class Student!";
	}
}
class GraduateStudent extends Student{
	public GraduateStudent(){
		
	}
	public String toString(){
		return "This is calss GraduateStudent!";
	}
}

匹配方法的簽名和綁定的方法的實現是兩個獨立的事情。引用變量的聲明類型決定了編譯時匹配哪個方法。編譯器在編譯時,會根據類型參數、參數個數和參數順序找到匹配的方法。一個方法可能在幾個子類中都被實現。Java虛擬機在運行是動態綁定的方法的實現,這是由變量的實際類型決定的。

 

 


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章