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虚拟机在运行是动态绑定的方法的实现,这是由变量的实际类型决定的。

 

 


 

 

你可能感兴趣的:(Java)