多态 后期绑定
java中,除了static和final 方法,其他所有方法都是后期绑定的,先举一个多态的简单例子
class Instrument{
public void play(){
System.out.println("play instrument");
}
}
class Piano extends Instrument{
public void play(){
System.out.println("play piano");
}
}
class Volin extends Instrument{
public void play(){
System.out.println("play Volin");
}
}
class Play_music{
public static void paly_m(Instrument m){
m.play();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Instrument V = new Volin();
Instrument P = new Piano();
Play_music.paly_m(V);
Play_music.paly_m(P);
}
}
执行结果:
要实现多态,子类中要重写基类中的方法,并且这个方法是public的,如果是private则不能实现
class Derived extends Main{
public void f(){
System.out.println("public f()");
}
}
public class Main {
private void f(){
System.out.println("private f()");
}
public static void main(String[] args){
Main p = new Derived();
p.f();
}
}
运行结果:
可以发现,并没有实现多态。基类和子类中两个f()是两个不相关的,其实基类中的private方法,默认就是final,不能被重写。
基类的public方法都能被多态吗?
class StaticSuper{
public static String staticGet(){
return "Base staticGet";
}
public String dynamicGet(){
return "Base dynamicGet";
}
}
class StaticSub extends StaticSuper{
public static String staticGet(){
return "Sub staticGet";
}
public String dynamicGet(){
return "sub dynamicGet";
}
}
public class Main {
public static void main(String[] args){
StaticSuper sup = new StaticSub();
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}
运行结果:
静态方法属于类,而并非与单个对象相关联,所以static是不能多态的
方法能多态,那类中的域能多态吗?
class Super{
public int filed = 0;
public int getFiled(){
return filed;
}
}
class Sub extends Super{
public int filed = 1;
public int getFiled(){
return filed;
}
public int getSuperField(){
return super.filed;
}
}
public class Main {
public static void main(String[] args){
Super sup = new Sub();
System.out.println("sup.field=" + sup.filed +
",sup.getFiled()=" + sup.getFiled());
Sub sub = new Sub();
System.out.println("sup.field=" + sub.filed +
",sup.getFiled()=" + sub.getFiled() +
",sup.getSuperField()=" + sub.getSuperField() );
}
}
运行结果:
这里为了演示,将域定义成了public,一般不这样做。可以看出,域并不能像方法那样能实现多态。
我们知道如果要new一个子类对象,会先初始化基类中的变量和构造函数,如果基类中的构造函数调用了,多态中子类和基类都有的方法(即实现了多态),那会如何?
class Glyph{
public void draw(){
System.out.println("Glyph.draw()");
}
public Glyph(){
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph{
private int radius = 1;
public RoundGlyph(int r){
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius=" + radius);
}
public void draw(){
System.out.println("RoundGlyph.RoundGlyph(), radius=" + radius);
}
}
public class Main {
public static void main(String[] args){
new RoundGlyph(5);
}
}
运行结果:
可以发现,基类中的构造方法竟然也执行了子类中的draw()方法,并且因为子类还未初始化,所以radius=0。要尽量避免这种情况发生,一旦出错,很难发现。
在继承中,子类可以添加属于自己的方法,子类是基类的超集,如果通过向上转型来实现多态,要想调用子类中的自己新的方法,需要向下转型
class Userful{
public void f(){};
public void g(){};
}
class MoreUsefule extends Userful{
public void f(){};
public void g(){};
public void u(){};
}
public class Main {
public static void main(String[] args){
Userful[] x = { new Userful(), new MoreUsefule()};
x[0].f();
x[1].g();
//如果要调用子类中新方法,向下转型
((MoreUsefule)x[0]).u(); //出错啦 本身为Userful,没有u()方法
((MoreUsefule)x[1]).u();
}
}
运行结果:
出错了,Userful对象没有u()方法。当我使用的IDE编写时,并没有提醒出错,我也试了一下用javac 去编译文件,也没有报错,都是运行才出错,所以要多加小心。
最后,说一下协变返回类型:子类中的覆盖方法可以返回基类方法的返回类型的某种子类类型:
光看这个解释不太好懂,看个例子就会豁然开朗
class Grain{
public String toString() {
return "Grain";
}
}
class Wheat extends Grain{
public String toString(){
return "Wheat";
}
}
class Mill{
Grain process(){
return new Grain();
}
}
class WheatWill extends Mill{
Wheat process(){
return new Wheat();
}
}
public class Main {
public static void main(String[] args){
Mill m = new Mill();
Grain g = m.process();
System.out.println(g);
m = new WheatWill();
g = m.process();
System.out.println(g);
}
}
运行结果:
相信聪明的你已经理解了。
总结:
1.私有方法不能被多态,所以要想实现多态,子类应该重写基类的public方法
2.域不能像方法那样多态
3.static方法也不能实现多态
4.尽量避免在基类构造方法中调用多态方法,如果调用了,多态会起作用,会调用子类中此方法,而此时子类还未初始化。