先写一个简单的水果之间继承的各个类及关系,方便测试
class GoldenDelicious extends Apple{
public GoldenDelicious(){}
public void makeAppleCider(){System.out.println("GoldenDelicious");}
}
class Macintosh extends Apple{
public Macintosh(){}
}
class Apple extends Fruit{
public Apple(){}
}
class Orange extends Fruit{
public Orange(){}
}
class Fruit{
public Fruit(){}
}
在main中
<span style="font-family:SimSun;">Fruit fruit = new GoldenDelicious();
fruit.makeAppleCider();
</span>
刚写完就报错了,提示我们在Fruit中找不到makeAppleCider()方法。这是因为我们用的Fruit fruit = new,相当于这三句
<span style="font-family:SimSun;">Fruit fruit ;
GoldenDelicious g = new GoldenDelicious();
fruit = g; //g向上转型,fruit称为g的上转型对象</span>
Java对上转对象有规定,其特点为:
(1)上转对象不能操作子类新增的成员变量;不能使用子类新增的方法。
(2)上转对象可以操作子类继承或隐藏成员变量,也可以使用子类继承的或重写的方法。
(3)如果子类重写了父类的某个方法后,对象的上转型对象调用这个方法时,一定是调用了这个重写的方法。
(4)可以将对象的上转型对象再强制转化为一个子类对象,这时,该子类对象又具备子类的所有属性和功能。
对于第一个我们已经证实,子类GoldenDelicious虽然有方法makeAppleCider()方法,但是Fruit类中无该方法,所以访问不到。接下来我们在Fruit类加入方法
class GoldenDelicious extends Apple{
public GoldenDelicious(){}
public void makeAppleCider(){System.out.println("GoldenDelicious");}
}
class Macintosh extends Apple{
public Macintosh(){}
}
class Apple extends Fruit{
public Apple(){}
}
class Orange extends Fruit{
public Orange(){}
}
class Fruit{
public Fruit(){}
public void makeAppleCider(){System.out.println("fruit");
}
}
main.java:
Fruit fruit = new GoldenDelicious();
fruit.makeAppleCider();
结果:
GoldenDelicious
这里可以证实上面的结论②,调用的是GoldenDelicious的方法makeAppleCider();
其中多态运行顺序为:
1、当该方法无参数时,那么只看实例化该对象的类是什么,从实例类的开始从下到上找起,一直找到Object类,那一层先有那个无参方法就调用哪一个方法。
2、当方法有参数时,也是从实例类的开始从下到上找起,不过找的时候会比较参数,看参数的引用是谁,去匹配类方法中参数的类型定位到哪一层的的方法。
这里对应第一种情况就是先从GoldenDelicious类找起,里面有makeAppleCider(),就用GoldenDelicious的方法makeAppleCider(),不再往上找。
假如我们去掉GoldenDelicious类方法makeAppleCider(),而在Apple类中加入方法makeAppleCider(),那么按这个顺序,结果应该是apple才对。
class GoldenDelicious extends Apple{
public GoldenDelicious(){}
//public void makeAppleCider(){System.out.println("GoldenDelicious");}
}
class Macintosh extends Apple{
public Macintosh(){}
}
class Apple extends Fruit{
public Apple(){}
public void makeAppleCider(){System.out.println("apple");}
}
class Orange extends Fruit{
public Orange(){}
}
class Fruit{
public Fruit(){}
public void makeAppleCider(){System.out.println("fruit");}
}
结果:apple
第二种情况解释:
首先我们在继承类中加入一些带参数的方法,分别在三个类中加入带参方法makeAppleCider(),例如:
<span style="font-family:SimSun;">class GoldenDelicious extends Apple{
public GoldenDelicious(){}
public void makeAppleCider(){System.out.println("GoldenDelicious");}
public void makeAppleCider(GoldenDelicious obj){System.out.println("GoldenDeliciousTwo");} //加
}
class Macintosh extends Apple{
public Macintosh(){}
}
class Apple extends Fruit{
public Apple(){}
public void makeAppleCider(){System.out.println("apple");}
public void makeAppleCider(Apple obj){System.out.println("appleTwo");} // 加
}
class Orange extends Fruit{
public Orange(){}
}
class Fruit{
public Fruit(){}
public void makeAppleCider(){System.out.println("fruit");}
public void makeAppleCider(Fruit obj){System.out.println("fruitTwo");} //加
}</span>
现在我们在main()中这样若这样调用:
Fruit fruit = new GoldenDelicious();
Fruit fruit1 = new GoldenDelicious();
fruit.makeAppleCider(fruit1);
结果:
fruitTwo
分析:
这里调用函数时的参数fruit1的引用类型是Fruit,这里先从GoldenDelicious类由下往上找的时候会看里面有没有带有形参类型为Fruit的方法makeAppleCider()。
而GoldenDelicious类的makeAppleCider()方法的形参类型为GoldenDelicious,不匹配;所以再向上找到Apple类,里面makeAppleCider()方法的形参类型为Apple,不匹配;
所以再向上找到Fruit类,里面makeAppleCider()方法的形参类型为Fruit,匹配,所以输出fruitTwo.
再例如我们将Apple类中的形参类型变一下,变成Fruit,即:
class GoldenDelicious extends Apple{
public GoldenDelicious(){}
public void makeAppleCider(){System.out.println("GoldenDelicious");}
public void makeAppleCider(GoldenDelicious obj){System.out.println("GoldenDeliciousTwo");}
}
class Macintosh extends Apple{
public Macintosh(){}
}
class Apple extends Fruit{
public Apple(){}
public void makeAppleCider(){System.out.println("apple");}
public void makeAppleCider(Fruit obj){System.out.println("appleTwo");} //变参数类型
}
class Orange extends Fruit{
public Orange(){}
}
class Fruit{
public Fruit(){}
public void makeAppleCider(){System.out.println("fruit");}
public void makeAppleCider(Fruit obj){System.out.println("fruitTwo");}
}
按上面的分析,那么结果应该是 appleTwo
结果:
appleTwo
3.上转型对象的前三个特点我们都已经证实,最后一个可以将对象的上转型对象再强制转化为一个子类对象,这时,该子类对象又具备子类的所有属性和功能。
这样再转化为子类的话,goldendeliciouus又完全具备GoldenDelicious类的所有属性和功能。
Fruit fruit = new GoldenDelicious();
GoldenDelicious goldendelicious = (GoldenDelicious)fruit;
class GoldenDelicious extends Apple{
public GoldenDelicious(){}
public void makeAppleCider(){System.out.println("GoldenDelicious");}
}
class Macintosh extends Apple{
public Macintosh(){}
}
class Apple extends Fruit{
public Apple(){}
}
class Orange extends Fruit{
public Orange(){}
}
class Fruit{
public Fruit(){}
}
</span>
结果为:
GoldenDelicious
说明:这时的goldendeliciouus对象完全不依赖父类,可以实现GoldenDelicious类的所有属性和功能,包括新增的。
最后,总结多态的实验过程:
(1)子类重写父类的方法。
(2)编译方法时,调用父类定义的方法。
(3)运行时,根据实际创建的对象类型动态决定使用哪个方法。