只有非private的方法才能被覆盖,但是推荐在导出类当中对于基类中private的方法最好采用不同的名字。并且基类中private的方法子类中不可见,也不能被重载。
只有普通方法的调用是多态的。域是没有多态性的(事实上一般被声明为private)。静态的方法也不具备多态性。
class Super { public int field = 0; public int getField() { return field; } } class Sub extends Super { public int field = 1; public int getField() { return field; } public int getSuperField() { return super.field; } } public class FiedlAccess { public static void main(String[] args) { Super sup = new Sub(); //向上转型 System.out.println("sup.field=" + sup.field + ", sup.getField()=" + sup.getField()); Sub sub = new Sub(); System.out.println("sub.field=" + sub.field + ", sub.getField()=" + sub.getField() + ", sub.getSuperField()=" + sub.getSuperField()); } }输出:
sup.field=0, sup.getField()=1 sub.field=1, sub.getField()=1, sub.getSuperField()=0由此可见 将子类对象向上转型为基类的应用变量,变量的域是基类的域,但是方法都是按照子类的方法运行,同时子类的方法。Sub对象转型为Super引用时,域访问操作都会被编译器解析,不是多态。Super.field与Sub.field都分配了不同的存储空间。
如果规范是静态的就不会有多态性:
class StaticSuper { public static String staticGet() { return "Base staticGet()"; } public String dynamicGet() { return "Base dynamicGet()"; } } class StaticSub extends StaticSuper { public static String staticGet() { return "Derived staticGet()"; } public String dynamicGet() { return "Derived dynamicGet()"; } } public class StaticPolymorphism { public static void main(String[] args) { StaticSuper sup = new StaticSub(); System.out.println(sup.staticGet()); System.out.println(sup.dynamicGet()); } }输出:
Base staticGet() Derived dynamicGet()输出:
Meal() Lunch() PortableLunch() Bread() Cheese() Lettuce() Sandwich()调用构造器的顺序需要满足如下顺序:
1)调用基类构造器。这个过程会不断的反复递归下去。
2)按照声明顺序调用成员的初始化方法。
3)调用导出类构造器的主体。
层次结构中每个类Characteristic 与Description两种类型的成员对象,并且必须被销毁。为了避免某个子对象依赖于其他对象,所以销毁的顺序应该与初始化顺序相反。故字段意味着与声明的顺序相反。先对其导出类进行清理,然后是基类。因为导出类的清理可能会调用基类中的某些方法。
class Glyph { void draw() { System.out.println("Glyph.draw()"); } Glyph() { System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); } } class RoundGlyph extends Glyph { private int radius = 1; RoundGlyph(int r) { radius = r; System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius); } void draw() { System.out.println("RoundGlyph.draw(), radius = " + radius); } } public class PolyConstructors { public static void main(String[] args) { new RoundGlyph(5); } }输出
Glyph() before draw() RoundGlyph.draw(), radius = 0 Glyph() after draw() RoundGlyph.RoundGlyph(), radius = 5这里Glyph.draw()已经由RoundGlyph.draw()覆盖。由输出可以看出radius不是默认的初始值1而是0.所以这里必须要说明初始化的实际过程:
1)在其他任何事情发生之前,将分配给对象的存储空间初始化为二进制的零
2)如前所述调用基类构造器。此时调用覆盖后的draw方法(要在调用RoundGlyph构造器之前)由于步骤1的缘故,此时发现radius的值为0 故输出0.
3)按照声明的顺序调用成员的初始化方法。
4)调用导出类的构造器主体
这样做有点就是所有东西至少都初始化为0,而不仅仅留作垃圾。
输出:
HappyActor SadActor此处Stage包含一个对Actor的引用,而Actor被初始化为HappyActor对象,意味着performPlay()会产生某种特殊行为。
向上转型后,导出类中接口的扩展部分不能被基类访问。但是一样的部分可以被重载。
向上转型(在继承层次中向上移动)会丢失具体的类型信息。所以我们想通过向下转型(继承层次中向下移动)。向上转型是安全的,因为基类不会有大于其导出类的忌口,因此向上转型是安全的通过基类接口发送消息保证都能被接收。但是向下转型就存在不安全性。必须要有某种方法确保向下转型的正确性。
Java中所有转型都会得到检查,即使是进行一次普通加括号的类型转化,进入运行期时仍然会对其进行检查,保证是希望的类型,如果不是就会返回一个ClassCastException(类型转型异常)。
class Useful { public void f() { System.out.println("Useful f()"); } public void g() { System.out.println("Useful g()"); } } class MoreUseful extends Useful { public void f() { System.out.println("MoreUseful f()"); } public void g() { System.out.println("MoreUseful g()"); } public void u() {} public void v() {} public void w() {} } public class RTTI { public static void main(String[] args) { Useful[] x = { new Useful(), new MoreUseful() }; x[0].f(); x[1].f(); x[0].g(); x[1].g(); // x[1].u(); 报错 向上转型后 额外的方法无法被调用 ((MoreUseful)x[1]).u(); // Downcast/RTTI 向下转型 ((MoreUseful)x[0]).u(); //会抛出异常 } } Useful f() MoreUseful f() Useful g() MoreUseful g() Exception in thread "main" java.lang.ClassCastException: Useful cannot be cast to MoreUseful at RTTI.main(RTTI.java:32)MoreUseful接口扩展了Useful接口但是其为继承来的,所以可以向上转型到Useful,在main方法中对数组x进行初始化可以看到这种情况,因为两个new的对象都属于Useful类型的 故可以调用f()与g()方法,并且MoreUseful的方法对基类中的f() g()进行了重载,调用的时候调用的是子类的重载函数。但是如果调用u()则会报错,因为进行了向上转型,无法控制超出基类函数的方法。
如果想要访问MoreUseful的对象的借口,就可以尝试进行向下转型,如果所转型的类型是正确的,那么转型成功。否则就会返回一个ClassCastException异常。
多态意味着”不同的形式“。在面向对象设计过程中,我们持有从基类基础来的相同接口,以及使用该接口的不同形式:不同版本的动态绑定方法。
欢迎加微信公众号进一步交流!