请有人向我解释输出的最后 6 行是如何打印的。我知道由于静态装订,前三行被适当地打印。
我不知道为什么第 5 行给出输出,因为它是 Ipod 类型并且它没有任何歌曲方法,但它仍然打印输出。这是代码:
package myferrari;
import mycar.*;
class IPOD
{
void PlayMusic(Music m)
{
System.out.println("Music from Ipod");
}
}
class IpodPlayer extends IPOD
{
void PlayMusic(Music m)
{
System.out.println("Music from IpodPlayer");
}
void PlayMusic(Song m)
{
System.out.println("Song from IpodPlayer");
}
}
class Music{}
class Song extends Music{}
public class Main {
public static void main(String [] args)
{
IPOD ipod = new IPOD();
IpodPlayer iplayer = new IpodPlayer();
IPOD ipl = new IpodPlayer();
Music m = new Music();
Song s = new Song();
Music sm = new Song();
iplayer.PlayMusic(m);
iplayer.PlayMusic(s);
iplayer.PlayMusic(sm); // static binding refers to the reference type
ipod.PlayMusic(m);
ipod.PlayMusic(s);
ipod.PlayMusic(sm);
ipl.PlayMusic(m);
ipl.PlayMusic(s);
ipl.PlayMusic(sm);
}
}
输出在这里:
Music from IpodPlayer
Song from IpodPlayer
Music from IpodPlayer
Music from Ipod
Music from Ipod
Music from Ipod
Music from IpodPlayer
Music from IpodPlayer
Music from IpodPlayer
坚果壳:整个概念是关于方法重载和覆盖以及它们如何与动态和静态绑定链接。
现在,首先查看下面的代码片段,该代码片段@run简化您的案例并了解运行时图片。使用您选择的任何 IDE 调试代码,您将看到完全相同的结果。
主要要点:在运行时,您的引用将解析为实际对象,这称为动态绑定或多态性。 运行时表示对象。 因此,无论是IpodPlayer iplayer = new IpodPlayer();
还是IPOD ipl = new IpodPlayer();
,在运行时,您都将获得IpodPlayer
的实例,并调用其方法。
与方法重写链接的动态绑定。因此,在运行时,通过检查对象类型来决定调用哪个重写的方法。因此,您可以看到,无论引用类型是什么,在运行时都会调用实际对象的PlayMusic
方法。
这总结了动态绑定和方法重写。
new IpodPlayer().PlayMusic(new Music()); //Music from IpodPlayer
new IpodPlayer().PlayMusic(new Song()); //Song from IpodPlayer
new IpodPlayer().PlayMusic(new Song()); //Music from IpodPlayer
new IPOD().PlayMusic(new Music()); //Music from Ipod
new IPOD().PlayMusic(new Song()); //Music from Ipod
new IPOD().PlayMusic(new Song()); //Music from Ipod
new IpodPlayer().PlayMusic(new Music()); //Music from IpodPlayer
new IpodPlayer().PlayMusic(new Song()); //Music from IpodPlayer
new IpodPlayer().PlayMusic(new Song()); //Music from IpodPlayer
进入静态绑定和方法重载。
静态绑定清除了有关 java 中重载方法在编译期间如何使用类型信息绑定的内容。
现在,您的IPOD ipod = new IPOD();
情况非常清楚,因为您只有方法而没有重载方法,因此您的结果 4、5 和 6 是不言自明的。
真正的混乱是在IpodPlayer iplayer = new IpodPlayer();
和IPOD ipl = new IpodPlayer();
的情况下.
在编译时,根据对象类型(PlayMusic(Music m)
或PlayMusic(Song m)
)决定调用哪个重载方法。请注意,我只谈论方法,而不是哪个类或对象。
现在,在您的案例 1、2 和 3 中,由于对象 of new IpodPlayer()
并且您有两个重载方法,因此根据对象类型调用适当的PlayMusic
方法PlayMusic
最后的情况 - 您的案例 7、8 和 9,这是主要的挠头案例,最重要的是理解,因为它结合了动态绑定和静态绑定的概念。
同样在编译时,要调用的重载方法是根据对象类型决定的,但由于在编译时它是根据IPOD ipl
而不是= new IpodPlayer();
决定的,所以编译器决定并生成PlayMusic(Music m)
将在运行时调用的字节代码(哪个对象的方法,但PlayMusic(Music m)
方法),因为IPOD
没有任何其他方法。最后,在运行时,动态绑定将进入图片,并且由于= new IpodPlayer();
<</p>
IpodPlayer
方法> 执行打印的类有一个方法,该方法将对象作为 Music 类,而不是 Song 的子类。
为了做你想做的事情,你必须执行以下操作:
void PlayMusic(Music m){
if(m instance of Song){
System.out.println("Song from Ipod");
} else
System.out.println("Music from Ipod");
}
由于您将 IPL 声明为超类 IPOD 并将其实例化为 IPodPlayer,因此除非您检查其类型并适当地强制转换它,否则它的行为与 IPOD 一样。
IpodPlayer player2 = null;
if(ipl instance of IpodPlayer)
player2 = (IpodPlayer)ipl;
但这没有意义,所以只需将其用作您希望它表现的类即可。
这篇文章比我更好地解释了它。Java 原因是
:起初,java 使用静态绑定从一组重载方法中进行选择。然后动态绑定从一组重写的方法中进行选择。http://beginnersbook.com/2013/04/java-static-dynamic-binding/
class IpodPlayer extends IPOD
{
//that is overriding
void PlayMusic(Music m)
{
System.out.println("Music from IpodPlayer");
}
//that is overloading
void PlayMusic(Song m)
{
System.out.println("Song from IpodPlayer");
}
}
您理解为什么打印前三行,所以我不会解释其背后的原因。
所以可以这样想。每当你"扩展"到一个类时,你实际上是在创建该类的新副本并向其中添加内容。如果你把一个类想象成一个包含房间(方法)和电器(变量)的房子的蓝图,那么一个扩展另一个类的类就是一个基于另一个蓝图的房子的蓝图,它有一些可能的修改/添加。创建扩展另一个蓝图的蓝图时,您添加的任何与原始蓝图中的内容具有相同签名(相同名称/类型/参数)的房间或设备都将覆盖原始蓝图中的该内容。
解释为什么每行都像它们一样打印出来:
第 4 行:您创建了一个 IPOD 类,该类只有一个方法并打印相同的内容"来自 iPod 的音乐"。
第 5 行:您创建了一个 IPOD 类,该类只有一个方法并打印相同的内容"来自 iPod 的音乐"。是的,该方法仅接受音乐类型,但歌曲扩展了音乐。回到例子:这意味着音乐屋的房间和电器都在歌屋内。因此,由于该方法不是寻找歌曲屋,因此它会删除不属于音乐屋的所有内容并将其丢弃。然后将音乐屋交给IPOD类内部的方法。
第 6 行:您创建了一个 IPOD 类,该类只有一个方法并打印相同的内容"来自 iPod 的音乐"。SM 是一个音乐对象,因此很容易传递给方法。
第 8 行:所以第 8 行是用 main 中的这些代码行组成的:
IPOD ipl = new IpodPlayer();
Song s = new Song();
ipl.PlayMusic(s);
IPL是IPOD类,而不是IpodPlayer类。IPOD 类中没有对象歌曲的方法。 当你写"IPOD ipl = new IpodPlayer();"时,你不是在创建IpodPlayer对象。您正在创建一个 IPOD 对象。当你在这个上下文中说"new IpodPlayer"时,你实际上是在说使用IpodPlayer蓝图来构建IPOD对象。IPOD 对象是一个具有一种方法的类,"void PlayMusic(Music m)"。所以 ipl 将只有一种方法(音乐方法)。之所以说"来自 IpodPlayer 的音乐",而不是"来自 iPod 的音乐",是因为在使用 IpodPlayer 蓝图生成 IPOD 对象时,您使用具有相同符号的 IpodPlayer 方法覆盖了 IPOD 方法(void PlayMusic(Music m))。这之所以有效,是因为 IpodPlayer 正在扩展 IPOD 类。
第 7 行和第 9 行:IPL 是一个 IPOD 类,但是在声明期间,您可以使用 IpodPlayer 类中的相同命名方法覆盖名为"PlayMusic"的方法。这就是为什么m,s和sm都打印出"来自IpodPlayer的音乐"