多态意味着多种形态,是指属性和方法在子类中含有多种形态。允许不同类的对象对同一消息做出响du应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)。
一、方法多态和对象多态
方法多态就是覆写和重载。
对象的多态性就是子类和父类的相互转换。子类转父类向上转换可以直接转换,父类转子类向下转换需要强制类型转换。
对象的多态性的实际用处是让我们不用关心某个对象具体是什么类型,就可以使用该对象的某些方法,而这些方法通过抽象类或者接口实现,多态就是提供父类调用子类的一个手段。
例子如下:
abstract class Person{
private String name;
private int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
abstract void say();
}
class Student extends Person{
Student(String name,int age){
super(name,age);
}
public void say() {
System.out.println("I am a student");
}
}
class Teacher extends Person{
private String profession;
Teacher(String name,int age,String profession){
super(name,age);
this.profession = profession;
}
public void say() {
System.out.println("I am a teacher");
}
}
class School{
private String schoolname;
private Person person;
School(String schoolname,Person person){
this.schoolname = schoolname;
this.person = person;
}
public void findPerson() {
person.say();
}
}
public class Test{
public static void main(String[] args) {
Student student = new Student("lisi",21);
Teacher teacher = new Teacher("dudu",32,"maths");
School school1 = new School("changning",student);
School school2 = new School("changning",teacher);
school1.findPerson();
school2.findPerson();
}
}
1、创建了一个抽象类作为父类;
2、创建了两个子类实现父类中的抽象方法;
3、创建一个工具类调用父类方法;
4、在main方法中创建了子类的实例;
5、将实例传递给工具类实例化父类;
6、调用父类的抽象方法,自动调用该子类对象覆写的方法。
在这个过程中我们并没有具体分辨子类类型来实现子类的方法,而是直接将子类对象实例化父类,调用父类中的抽象方法时就会自动根据实例化父类的子类对象来调用覆写父类抽象方法的子类方法。
实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
二、静态多态和动态多态
静态多态性:包括变量的隐藏、方法的重载(指同一个类中,方法名相同[方便记忆],但是方法的参数类型、个数、次序不同,本质上是多个不同的方法)。
动态多态性:是指子类在继承父类(或实现接口)时重写了父类(或接口)的方法,程序中用父类(或接口)引用去指向子类的具体实例,从代码形式上看是父类(或接口)引用去调用父类(接口)的方法,但是在实际运行时,JVM能够根据父类(或接口)引用所指的具体子类,去调用对应子类的方法。
三、编译时多态和运行时多态
转自:http://blog.csdn.net/why_still_confused https://blog.csdn.net/why_still_confused/article/details/51295707
编译时多态:
方法重载都是编译时多态。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
方法覆盖表现出两种多态性,当对象引用本类实例时,为编译时多态,否则为运行时多态。例如,以下声明p、m引用本类实例,调用toString()方法是编译时多态。
运行时多态:
1.当以下父类对象p引用子类实例时,p.toString)执行谁的setName()方法?
Person p = new Man();
p.toString();
Java支持运行时多态,意为p.toString()实际执行p所引用实例的toString(),究竟执行Person类还是Man类的方法,运行时再确定。如果Man类声明了toString()方法,则执行之;否则执行Person类的toString()方法。
程序运行时,Java从实例所属的类(new 类)开始寻找匹配的方法执行,如果当前类中没有匹配的方法,则沿着继承关系逐层向上,依次在父类或各祖先类中寻找匹配方法,直到Object类。
父类对象只能执行那些在父类中声明、被子类覆盖了的子类方法,如toString(),不能执行子类增加的成员方法。
2.将上述例子中toString方法改为getName,因为在Object类中有toString类,无法测试Person与Man中所匹配的执行方法。
public class Test { //例子2
public static void main(String[] args) {
Person p = new Man();
System.out.println(((Man) p).getName()); //返回结果为Man
}
}
class Person{}
class Man extends Person{
public String getName(){
String name = "Man";
return name;
}
}
此例中Person类型要引用Man类的实例,因Person中未定义setName()方法,故需要把Person类显式地转换为Man类,然后调用Man中的getName方法。
3.将例子1中Person和Man的方法名改为静态的getName()方法,会返回什么结果呢?
public class Test { //例子3
public static void main(String[] args) {
Person p = new Man();
System.out.println(p.type); //返回结果为P
System.out.println(p.getName()); //返回结果为Person
}
}
class Person{
String type = "P";
public static String getName() {
String name = "Person";
return name;
}
}
class Man extends Person{
String type = "M";
public static String getName(){
String name = "Man";
return name;
}
}
例子中子类Man隐藏父类Person的属性,而 Person p = new Man() 表示“先声明一个Person类的对象p,然后用Man类对p进行实例化”,即引用类型为Person类,实际代表的是Man类。因此,访问的是Person的属性及静态方法,详细解释如下。
所谓静态,就是在运行时,虚拟机已经认定此方法属于哪个类。“重写”只能适用于实例方法,不能用于静态方法。对于静态方法,只能隐藏,重载,继承。
子类对于父类静态方法的隐藏(hide),子类的静态方法完全体现不了多态,就像子类属性隐藏父类属性一样,在利用引用访问对象的属性或静态方法时,是引用类型决定了实际上访问的是哪个属性,而非当前引用实际代表的是哪个类。因此,子类静态方法不能覆盖父类的静态方法。
父类中属性只能被隐藏,而不能被覆盖;而对于方法来说,方法隐藏只有一种形式,就是父类和子类存在相同的静态方法。
四、多态的好处
1、可替换性(substitutability)多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2、可扩充性(extensibility)多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。
3、接口性(interface-ability)多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
4、灵活性(flexibility)它在应用中体现了灵活多样的操作,提高了使用效率。
5、简化性(simplicity)多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。