对于Java的多态性,很多人难以理解,起初我也一样,最近有点点琢磨出来了其中的原理,在此跟大家分享,例子代码也不复杂,相信看完或多或少会有点收获。
多态性可以理解为:给对象一个激励,让对象自行决定应该做出何种响应。(在网上看到的一句话,个人觉得对多态性的概括很全面,个人对多态性的理解也是用这句话来概括。)
刚开始理解这句话可能有点难下面来看例子具体分析。
Java的多态性表现形式有两种,函数重载(overload)和函数覆盖(override,也就是函数重写)。现在理解不了没事,来看例子。
首先:
//多态性之函数(方法)重载
package com.polymorphism;
public class TestPolymorphism{
//函数1
private void dosomething() {
System.out.println("我是无行参函数");
}
//函数2
private void dosomething(int a) {
System.out.println("我是形参为int类型的函数:"+a);
}
//函数3
private void dosomething(String str) {
System.out.println("我是形参为String类型的函数:"+str);
}
//函数4
private void dosomething(int a,String str) {
System.out.println("我是形参为int和String类型的函数:"+a+" "+str);
}
//函数5
private void dosomething(String str,int a) {
System.out.println("我是形参为String和int类型的函数:"+str+" "+a);
}
public static void main(String[] args) {
TestPolymorphism p=new TestPolymorphism();
p.dosomething();//第一次调用
p.dosomething(10);//第二次调用
p.dosomething("hello");//第三次调用
p.dosomething(10, "hello");//第四次调用
p.dosomething("hello", 10);//第五次调用
}
}
结果:
我是无行参函数
我是形参为int类型的函数:10
我是形参为String类型的函数:hello
我是形参为int和String类型的函数:10 hello
我是形参为String和int类型的函数:hello 10
对于这个TestPolymorphism类,函数也很简单,只不过大家会看到,所有的函数都是同名的,都是dosomething(),那么如果在主函数调用这个函数的话,他怎么知道去调用哪一个呢?然而大家再看看每个函数,是不是有不同的地方呢?如形参个数、形参类型什么的。对,每个函数都跟其他的不一样。在Java中,同一个类里面写多个函数名相同,但是形参类型、个数以及顺序至少有一个不同的函数,就叫做函数重载。如果两个函数一摸一样,那就会出错。
那么,来看主方法的调用,(虽然p是一个TestPolymorphism类的引用,指向这个类的一个对象,但习惯上的叫法就叫p对象)先创建p这个对象。
第一次调用,这个dosomething()没有实参,所以p只会找到没有形参的函数,也就是第一个函数然后执行,而不会去执行后面的2、3、4、5函数。
第二次调用,这个dosomething()传入了一个int类型的实参10,所以p会找到形参为int类型的函数,也就是第二个函数去执行。
第三次调用,这个dosomething()传入了一个String类型的实参“hello”字符串,所以p会找到形参为String类型的函数,也就是第三个函数去执行。
后面的两个函数调用是类似的情况。
现在再回顾那句话:给对象一个激励,让对象自行决定应该做出何种响应。是不是有一定的体会呢?给p对象一个激励,可以理解为函数的传入实参吧,响应就相当于p对象根据传入的实参去决定执行哪个函数,实现函数体。
PS:函数重写中,虽然可以使两个函数的返回值不同,但是这不足以区分两个方法的重载关系,所以必须通过形参的类型、个数及顺序来构成重载关系。
也许很多人会问,为什么要把函数写成同名的呢,写成不同的名字就不需要这么麻烦了呢?
其实不然,Java中很多时候需要用很多同名的函数,最简单的比如要通过不同的方式来实例化对象,而构造函数名都是相同的,所以这就需要函数重载了。
其次:
//多态性之函数(方法)覆盖(重写)
package com.polymorphism;
class Father{
protected void dosomething(){
System.out.println("我是Father的dosomething方法");
}
}
class Son extends Father{
protected void dosomething(){
System.out.println("我是Son的dosomething方法");
}
}
class Daughter extends Father{
protected void dosomething(){
System.out.println("我是Daughter的dosomething方法");
}
}
public class TestPolymorphism{
public static void main(String[] args) {
Father f=new Father();
f.dosomething();//调用1
Father x=new Son();
x.dosomething();//调用4
Father y=new Daughter();
y.dosomething();//调用5
}
}
结果:
我是Father的dosomething方法
我是Son的dosomething方法
我是Daughter的dosomething方法
类的继承不多讲了,要注意的是子类不能继承父类private属性的变量和函数。
重写:多个子类都需要继承父类的某个方法,如果在每个子类中写一次这个方法,那么就会出现很多次重复的代码,那么会出现代码冗余的现象,所以把这个方法写在父类中,每个子类都继承这个方法,就避免了大量重复代码。但是一般在父类中的方法都是抽象方法,不能实现特定的功能,比如吃东西这个方法eat(),他只是实现吃东西这个动作,并不能实现吃什么东西。那么我要在子类中实现吃饭这个方法,就必须重写eat()方法来实现吃饭这个动作。再比如父类定义了一个画图形的方法draw(),子类要实现画三角形的功能,画三角形也是画图形,也就是说子类的draw()方法本来就有画图形的功能,你只要再写出指定画三角形的代码就行。。也就是说重写其实就是子类重新实现父类某个方法的功能,子类的重写的方法必须跟父类的方法一样(返回值类型、形参类型、形参个数、形参顺序等),但函数体应当不同。
理解了这个,重写就差不多了。
来看例子,很简单。Son和Daughter类都继承Father类并重写了其中的dosomething方法。
调用1都相信大家都能理解,每个对象直接调用自己的方法。
如果把父类引用指向子类对象,也就是x引用,再调用x对象里面的dosomething方法,那这个对象就实现子类的dosomething方法,也就是实现子类所重写的dosomething方法。而不是父类的dosomething方法。对于y引用是一样的。
这也是相当于给对象引用一个激励,让他自己决定去响应哪个激励。给对象引用(父类引用)的激励相当于不同的对象(父类对象或子类对象),响应相当于去执行哪个方法(父类还是子类的方法)。
现在应该能理解了多态性:给对象一个激励,让他自己决定去做出何种响应。
多态性的表现形式:
①同一个类之间:函数重载(overload)
②父类和子类之间:函数覆盖(override)
PS:本人第一次写博文,有什么缺陷或者写的有问题的地方欢迎指点,本人也是菜鸟一只,只是想跟大家共同进步,谢谢!