如果你认为你已经对java中的重载和重写了如指掌,那么我想你可能会通过下面的例子对生活产生怀疑。如果你能完整地回答下面的问题,那我觉得你真的非常非常棒。单一调度classParent{voidprint(Stringa){log.info("Parent-String");}voidprint(Objecta){log.info("Parent-Object");}}classChildextendsParent{voidprint(Stringa){log.info("Child-String");}voidprint(Objecta){log.info("Child-Object");}}下面会打印什么?Stringstring="";ObjectstringstringObject=string;//打印什么?Childchild=newChild();child.print(string);child.print(stringObject);Parentparent=newChild();parent.print(string);parent.print(stringObject);答案:child.print(string);//Print:"Child-String"child.print(stringObject);//打印:"Child-Object"parent.print(string);//打印:"Child-String"parent.print(stringObject);//打印:"Child-Object"print(string)和parent.print(string)是Java面向对象编程的教科书示例。被调用的方法取决于实际的实例类型,而不是声明的实例类型。比如你定义一个变量是Child还是Parent,因为实际的实例类型是Child,所以会调用Child::print。第二组比较复杂,因为它们都是同一个字符串。唯一的区别是string被声明为String而stringObject被声明为Object。在处理方法参数时,重要的是参数的声明类型,而不是其实际类型。即使实际参数类型是String,也会调用print(Object)隐式覆盖classParent{voidprint(Objecta){log.info("Parent-Object");}}classChildextendsParent{voidprint(Stringa){log.info("Child-String");}}要打印什么?Stringstring="";Parentparent=newChild();parent.print(string);答案:parent.print(string);//打印:"Parent-Object"的实际实例类型为Child,声明的参数类型为String,我们确实有一个为Child::print(String)定义的方法。事实上,这正是在上一个示例中调用parent.print(string)时选择的内容。但是,这不是此处调用的方法。Java似乎先选择调用哪个方法,然后再检查子类覆盖。在这种情况下,声明的实例类型是Parent,Parent中唯一匹配的方法是Parent::print(Object)。然后,当Java检查Parent::print(Object)的任何潜在覆盖时,它没有找到,所以这就是所做的。显式覆盖classParent{voidprint(Objecta){log.info("Parent-Object!");}voidprint(Stringa){thrownewRuntimeException();}}classChildextendsParent{voidprint(Stringa){log.info("Child-String!");}}打印什么?Stringstring="";Parentparent=newChild();parent.print(string);Answer:parent.print(string);//打印:"Child-String!"这个例子和前面的例子一样唯一的区别是我们添加了一个新的Parent::print(String)方法。这个方法从来没有真正执行过——如果执行了,它会抛出异常!但是,它的存在导致Java执行不同的方法。在评估Parent.print(String)时,运行时现在找到一个匹配的Parent::print(String)方法,然后看到该方法被Child::print(String)覆盖。模糊参数classFoo{voidprint(Cloneablea){log.info("Iamcloneable!");}voidprint(Mapa){log.info("IamMap!");}}下面打印的是什么?HashMapcloneableMap=newHashMap();Cloneablecloneable=cloneableMap;Mapmap=cloneableMap;//Whatgetsprinted?Foofoo=newFoo();foo.print(map);foo.print(cloneable);foo.print(cloneableMap);答案:foo.print(map);//print:"IamMap!"foo.print(cloneable);//print:"Iamcloneable!"foo.print(cloneableMap);//编译失败和singledispatch的例子类似,这里重要的是参数的声明类型,而不是实际类型。此外,如果多个方法对给定参数同样有效,Java会抛出一个编译错误并强制您指定应该调用哪个方法。多重继承-interfaceinterfaceFather{defaultvoidprint(){log.info("IamFather!");}}interfaceMother{defaultvoidprint(){log.info("IamMother!");}}classChildimplementsFather,Mother{}下面打印的是什么?newChild().print();与前面的示例类似,此示例也无法编译。具体来说,Child的类定义本身将无法编译,因为Father和Mother中的默认方法存在冲突。您需要修改Child类以指定Child::print的行为。多重继承-类和接口)。打印();答案:newChild().print();//打印:“Iamaclass!”如果类和接口之间存在继承冲突,则类方法优先。传递覆盖classParent{voidprint(){foo();}voidfoo(){log.info("IamParent!");}}classChildextendsParent{voidfoo(){log.info("IamChild!");}}打印什么?newChild().print();Answer:newChild().print();//print:"IamChild!"覆盖方法即使是传递的调用也会生效,看过Parent类的人可能会认为Parent::print会一直调用Parent::foo。但是如果该方法被覆盖,那么Parent::print将调用foo()的覆盖版本。privateoverrideclassParent{voidprint(){foo();}privatevoidfoo(){log.info("IamParent!");}}classChildextendsParent{voidfoo(){log.info("IamChild!");}}打印什么?newChild().print();答:newChild().print();//打印:“我是父母!”除了一处不同外,这与前面的示例完全相同。现在将Parent.foo()声明为私有的。因此,当Parent.print()调用foo()时,子类中是否有foo()的其他实现,或者调用print()的实例的实际类型无关紧要。静态重写classParent{staticvoidprint(){log.info("IamParent!");}}classChildextendsParent{staticvoidprint(){log.info("IamChild!");}}打印什么?Childchild=newChild();Parentparent=child;parent.print();child.print();Answer:parent.print();//打印:"IamParent!"child.print();//打印:“我是孩子!”Java不允许重写静态方法。如果在超类和子类中都定义了相同的静态方法,那么实例的实际类型根本无关紧要。只有声明的类型用于确定调用这两个方法中的哪一个。这是使用@override注释标记所有覆盖方法的另一个原因。在上面的例子中,当给Child::print添加注解时,你会得到一个编译错误,告诉你该方法不能被覆盖,因为它是静态的。静态链接classParent{voidprint(){staticMethod();instanceMethod();}staticvoidstaticMethod(){log.info("Parent::staticMethod");}voidinstanceMethod(){log.info("Parent::instanceMethod");}}classChildextendsParent{staticvoidstaticMethod(){log.info("Child::staticMethod");}voidinstanceMethod(){log.info("Child::instanceMethod");}}打印什么?Childchild=newChild();child。打印();答:Parent::staticMethodChild::instanceMethod这是我们之前讨论的一些不同概念的组合。例如,即使调用者在父方法中,覆盖也会生效。但是,对于静态方法,即使声明的变量类型是Child,Parent::staticMethod也会被调用,因为有一个中间的print()方法。总结如果有什么不同的话,那就是继承非常非常棘手并且容易出错。
