一.相关定义
1-1 多态
多态是同一个行为具有多个不同表现形式或形态的能力。同一个形参类型为基类的接口,使用不同的子类的实例可以执行不同操作。
1-2 绑定
- 绑定:将一个方法调用和一个方法体关联起来被称作绑定;
- 前期绑定:若在程序执行前进行绑定(如果有的话,由编译器和连接程序实现);
- 后期绑定:在运行时根据对象的类型绑定,也叫做动态绑定或运行时绑定。
后期绑定的实现通常是在对象中安置某种”类型信息”,以便方法调用机制能知道对象是哪种类型,从而找到对应的方法体加以调用。
Java中除了static方法和final方法(private方法也属于final方法)之外,其他所有方法都是后期绑定。
1-3 总是调用最派生的方法
Java在使用基类引用调用方法时,总是调用最派生的方法。选取最派生方法的范围是:基类中的方法和基类引用指向的对象所在类中覆写的同名方法。即,如果基类引用所指向对象所在的类中未覆写基类中的方法,那么通过基类引用调用的将会是基类中的方法,否则调用的就是基类引用所指向对象所在类中覆写的方法。
package com.hutao.test.page155;
import static com.hutao.util.Print.print;
class BaseClass {
public void method1(){
print("Base class's method1 call method2");
method2();
}
public void method2(){
print("Base class's method2 was called");
}
}
class DerivedClass extends BaseClass{
@Override
public void method2() {
print("Derived class's method2 was called");
}
}
public class Test{
public static void main(String[] args) {
BaseClass baseClass = new DerivedClass();
//使用基类引用调用方法
baseClass.method1();
}
}
运行结果为:
Base class's method1 call method2
Derived class's method2 was called
Process finished with exit code 0
如上代码中的派生关系为:
使用基类引用调用的方法是method1(),从BaseClass baseClass = new DerivedClass()可知选取最派生方法的范围应该是BaseClass类和DerivedClass类,但DerivedClass中未覆写BaseClass中的method1(),所以最派生的method1()方法就是BaseClass中的method1()。BaseClass中的method1()又调用了method2(),这里实际上也可理解为是通过基类引用baseClass去调用的method2(),此时,因为DerivedClass中覆写了method2(),所以选取最派生的方法就是DerivedClass类中的method2()。
二.final方法、静态方法和域的多态
结论:final方法(包括private方法)、静态方法和域没有多态特性。
2-1 final方法
final方法不可以被覆写,所以同一个行为就不能有多种表现形式,所以没有多态。注意:private方法是默认的final方法。
package com.hutao.page.page156;
import static com.hutao.util.Print.*;
public class PrivateOverride {
private void f(){
print("private f()");
}
public static void main(String[] args) {
PrivateOverride privateOverride = new Derived();
privateOverride.f();
}
}
class Derived extends PrivateOverride{
public void f(){
print("public f()");
}
}
运行结果为:
private f()
Process finished with exit code 0
这里还是可以根据1-3中的方式分析,从PrivateOverride privateOverride = new Derived()中可知,选取最派生f()方法的范围为PrivateOverrie类和Derived类,但是Derived类中未覆写f()方法(虽然有同名方法,但因为private方法不可能被覆写,所以Derived类中的f()只能算是另一个新方法),所以选取的范围就只剩下来基类中的f()方法,因此实际调用的方法就是基类中的f()方法。
2-2 静态方法
静态方法对应的行为不具有多态性,静态方法是与类而并非单个对象关联的。
package com.hutao.page.page157;
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类相关联
StaticSuper staticSuper = new StaticSub();
System.out.println(staticSuper.staticGet());
System.out.println(staticSuper.dynamicGet());
}
}
运行结果为:
Base staticGet()
Derived dynamicGet()
Process finished with exit code 0
2-3 域
如果直接访问某个域,这个访问在编译期就进行了解析(并非是运行时绑定),所以域不具有多态性。
package com.hutao.page.page156;
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 getSupperField(){
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();//upcast
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.getSupperField());
}
}
运行结果为:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
Process finished with exit code 0
三.构造器和多态
3-1 构造器的多态
构造器不具有多态性,因为构造器实际上是隐式声明的static方法,由2-2可知,静态方法不具有多态性。
- 基类的构造器总是在导出类的构造过程中被调用;
- 无论是基类还是导出类,在构造器被调用时,所有成员变量 都已经完成了初始化。
package com.hutao.page.page158;
import static com.hutao.util.Print.*;
class Meal{
Meal(){
print("Meal()");
}
}
class Bread{
Bread(){
print("Bread()");
}
}
class Cheese{
Cheese(){
print("Cheese()");
}
}
class Lettuce{
Lettuce(){
print("Lettuce()");
}
}
class Lunch extends Meal{
Lunch(){
print("Lunch()");
}
}
class PortableLunch extends Lunch{
//此成员变量用来验证是先初始化成员再调用构造器的。
private Bread bread = new Bread();
PortableLunch(){
print("PortableLunch()");
}
}
public class Sandwich extends PortableLunch{
private Bread bread = new Bread();
private Cheese cheese = new Cheese();
private Lettuce lettuce = new Lettuce();
public Sandwich(){
print("Sandwich()");
}
public static void main(String[] args) {
new Sandwich();
}
}
运行结果为:
Meal()
Lunch()
Bread()//表明成员被初始化在构造器被调用之前
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
Process finished with exit code 0
调用构造器的顺序为:
1)调用基类构造器。这个步骤会不断反复递归下去,首先是构造这个层次结构的根(根基类),然后是下一层的导出类,直到最低层的导出类(构造器执行前需保证类中的所有成员按声明顺序完成了初始化);
2)按声明顺序调用最低层导出类的初始化方法;
3)调用最低层导出类构造器的主体。
3-2 基类构造器中调用具有多态特性的方法
如果要在基类构造器中调用一个可被动态绑定的方法,实际上调用时绑定的方法体可能会是导出类中覆盖此方法的方法体。这个调用结果可能会引发错误,因为在调用基类构造器时,导出类对象还未被完全构造。(在构造导出类对象时候,会先调用基类的构造器。)
package com.hutao.page.page163;
import static com.hutao.util.Print.*;
class Glyph{
void draw(){
print("Glyph.draw()");
}
Glyph(){
print("Glyph() before draw()");
draw();
print("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(){
this.radius = radius;
print("RoundGlyph.RoundGlyph(), radius = "+radius);
}
@Override
void draw() {
print("RoundGlyph.draw(), radius = "+radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph();
}
}
运行结果为:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 1
Process finished with exit code 0
Glyph.draw()方法设计为将要被覆盖,这种覆盖是在RoundGlyph中发生的。但是Glyph构造器会调用这个方法,结果导致了对RoundGlyph.draw()的调用。但是此时RoundGlyph中的radius还未被初始化(还是二进制0)。
初始化的具体过程:(->)https://www.cnblogs.com/certainTao/p/14657469.html
编写构造器的准则:用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法。
在构造器中唯一能够安全调用的方法是本类中的final方法(private方法默认是final方法),这类方法不能被覆盖(其实调用静态方法也是可以的,但这里要表明的重点不是这个)。
Original: https://www.cnblogs.com/certainTao/p/14798451.html
Author: certainTao
Title: 多态
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585279/
转载文章受原作者版权保护。转载请注明原作者出处!