Verwandte Lernempfehlungen: Basic Java Tutorial
Heute habe ich das Unternehmen wie gewohnt besucht. Setzen Sie sich an Ihren Arbeitsplatz und schalten Sie den Computer ein „Es ist ein weiterer Tag voller Steine“
. Nachdem ich darüber nachgedacht hatte, öffnete ich Idea „kompetent“
, schaute mir die heutigen Anforderungen an und begann mit dem Codieren. Hey, wer hat diese Codes geschrieben, wie kommt es, dass sie in meinem Code auftauchen und immer noch darauf warten, eingereicht zu werden? Ich erinnere mich, dass ich sie noch nie geschrieben habe, also habe ich sie mir mit Interesse angeschaut: "又是搬砖的一天"
。想归想,还是"熟练"
的打开了 Idea,看了下今天的需求,便敲起了代码。咦,这些代码是谁写的,怎么出现在我的代码里面,而且还是待提交状态,我记得我没写过呀,饶有兴趣的看了看:
这不是多态吗,谁在我电脑写的测试,不禁一阵奇怪。
"你看看这会输出什么结果?"
一阵声音从身后传来,因为在思考输出结果,也没在意声音的来源,继续看了看代码,便得出结论:
polygon() before cal() square.cal(), border = 2 polygon() after cal() square.square(), border = 4复制代码
心里想:就这?起码也是名 Java 开发工程师好吗,虽然平时搬搬砖,一些基本功还是有的。不禁有点得意了~
"这就是你的答案吗?看来你也不咋的"
声音又突然响起,这次我不淡定了,尼玛!这答案我也是在心里想的好吗,谁能看得到啊,而且说得话让人那么想施展一套阿威十八式。"你是谁啊?"
带着丝微疑惑和愤怒转过了头。怎么没人?容不得我疑惑半分,"小菜,醒醒,你怎么上班时间就睡着了"
上班时间,睡着了?我睁开了眼,看了下周围环境,原来是梦啊,舒了一口气。望眼就看到部门主管站在我面前,上班时间睡觉,你是身体不舒服还是咋样?昨天写了一堆 bug 没改,今天又提交什么乱七八糟的东西上去,我看你这个月的绩效是不想要的,而且基于你的表现,我也要开始为部门考虑考虑了。
"我不是,我没有,我也不知道怎么就睡着了,你听我解释啊!"
这句话还没来得及说出口,心里的花我要带你回家,在那深夜酒吧哪管它是真是假,请你尽情摇摆忘记钟意的他,你是最迷人噶,你知道吗
,闹铃响了起来,我一下子立起身子,后背微湿,额顶微汗,看了下手机,周六,8点30分,原来那是梦啊!
奇怪,怎么会做那么奇怪的梦,也太吓人了。然后就想到了梦中的那部分代码,难道我的结果是错的吗?凭着记忆,在电脑上重新敲了出来,运行结果如下:
/* polygon() before cal() square.cal(), border = 0 polygon() after cal() square.square(), border = 4 */复制代码
square.cal(), border
的结果居然是 0,而不是2。难道我现在连多态都不会了吗?电脑手机前的你,不知道是否得出了正确答案了呢!不管有没有,接下来就跟小菜一起来复习一下多态吧!
有些小伙伴疑惑的点可能不止square.cal(), border
的结果是 0,也有为什么不是 square.square(), border = 4
先输出的疑惑。那么我们就带着疑惑,整起!
在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。
多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序。多态的作用就是消除类型之间的耦合关系
。
根据里氏代换原则
:任何基类可以出现的地方,子类一定可以出现。
对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。而这种吧对某个对象的引用视为对其基类型的引用的做法被称作为 - 向上转型
。因为父类在子类的上方,子类要引用父类,因此称为 向上转型
。
public class Animal { void eat() { System.out.println("Animal eat()"); } }class Monkey extends Animal { void eat() { System.out.println(" Monkey eat()"); } }class test { public static void start(Animal animal) { animal.eat(); } public static void main(String[] args) { Monkey monkey = new Monkey(); start(monkey); } }/* OUTPUT: Monkey eat() */复制代码
上述 test
类中的 start()
方法接收一个 Animal
的引用,自然也可以接收从 Animal
的导出类。调用eat()
方法的时候,自然而然的使用到 Monkey
中定义的eat()
方法,而不需要做任何的类型转换。因为从 Monkey
向上转型到 Animal
只能减少接口,而不会比Animal
„Sehen Sie, was ist die Ausgabe davon?“
public static void start(Animal animal) { animal.eat(); }复制代码🎜Ich denke mir: Das ist es? Zumindest sind Sie ein Java-Entwicklungsingenieur, okay? Obwohl Sie normalerweise viele Dinge tun, verfügen Sie dennoch über einige Grundkenntnisse. Ich konnte nicht anders, als ein wenig stolz zu sein~🎜🎜
„Ist das deine Antwort? Es scheint, dass du auch nicht gut bist“
🎜🎜Die Stimme ertönte plötzlich wieder, dieses Mal bin ich nicht mehr ruhig , Nima! Ich habe auch über diese Antwort nachgedacht, okay? Wer kann sie sehen? Außerdem weckt es bei den Leuten den Wunsch, eine Reihe von Aweis Achtzehn Stilen aufzuführen. „Wer bist du?“
drehte seinen Kopf mit einem Anflug von Zweifel und Wut. Warum ist niemand da? Ich konnte keine Zweifel ertragen, „Xiaocai, wach auf, warum bist du während der Arbeitszeit eingeschlafen?“
🎜🎜Bist du während der Arbeitszeit eingeschlafen? Ich öffnete meine Augen und betrachtete meine Umgebung. Es stellte sich heraus, dass es ein Traum war, und ich atmete erleichtert auf. Ich schaute mich um und sah den Abteilungsleiter vor mir stehen. Er schlief während der Arbeitszeit. Fühlen Sie sich unwohl oder so? Ich habe gestern eine Menge Fehler geschrieben, diese aber nicht korrigiert, und heute habe ich ein paar chaotische Sachen eingereicht. Ich denke, Ihre Leistung in diesem Monat ist nicht das, was ich will, und aufgrund Ihrer Leistung muss ich anfangen, für die Abteilung darüber nachzudenken . 🎜🎜„Das bin ich nicht, das habe ich nicht, ich weiß nicht, warum ich eingeschlafen bin, bitte hör dir meine Erklärung an!“
Bevor ich diesen Satz sagen konnte, Ich will das Blumen in meinem Herzen Bring dich nach Hause, egal ob es wahr ist oder nicht, spät in der Nacht in der Bar, bitte lass dich schwingen und vergiss ihn, du bist der charmanteste, weißt du?
Der Wecker klingelte, und ich stand sofort auf, mein Rücken war leicht nass und meine Stirn war leicht verschwitzt. Es war Samstag, 8:30 Uhr. 🎜🎜Es ist seltsam, wie konnte ich so einen seltsamen Traum haben? Es ist so beängstigend. Dann dachte ich an den Teil des Codes im Traum. Sind meine Ergebnisse falsch? Basierend auf dem Speicher habe ich es noch einmal auf dem Computer eingegeben und die Ergebnisse sind wie folgt: 🎜public class PrivateScope { private void f() { System.out.println("PrivateScope f()"); } public static void main(String[] args) { PrivateScope p = new PrivateOverride(); p.f(); } }class PrivateOverride extends PrivateScope { private void f() { System.out.println("PrivateOverride f()"); } }/* OUTPUT PrivateScope f() */复制代码🎜
square.cal(), border
Das Ergebnis ist tatsächlich 0, nicht 2. Bin ich jetzt nicht einmal gut im Polymorphismus? Vor Ihrem Computer und Ihrem Mobiltelefon wissen Sie nicht, ob Sie die richtige Antwort gefunden haben! Unabhängig davon, ob Sie es haben oder nicht, lassen Sie uns mit Xiao Cai einen Blick auf den Polymorphismus werfen! 🎜🎜Einige Freunde sind möglicherweise verwirrt, nicht nur, dass das Ergebnis von square.cal(), border
0 ist, sondern auch, warum square.square(), border = 4
ist Erste Zweifel werden nicht ausgegeben. Dann lasst uns mit Zweifeln durchstarten! 🎜Kopplungsbeziehung
zwischen Typen zu beseitigen. 🎜Richter-Substitutionsprinzip
: Überall dort, wo eine Basisklasse erscheinen kann, kann definitiv eine Unterklasse erscheinen. 🎜🎜Ein Objekt kann entweder als eigener Typ oder als Basistyp verwendet werden. Diese Methode, einen Verweis auf ein Objekt als Verweis auf seinen Basistyp zu behandeln, heißt - Upcast
. Da die übergeordnete Klasse über der untergeordneten Klasse liegt, muss die untergeordnete Klasse auf die übergeordnete Klasse verweisen. Daher wird dies als Aufwärtstransformation
bezeichnet. 🎜class Super { public int field = 0; public int getField() { return field; } }class Son extends Super { public int field = 1; public int getField() { return field; } public int getSuperField() { return super.field; } }class FieldTest { public static void main(String[] args) { Super sup = new Son(); System.out.println("sup.field:" + sup.field + " sup.getField():" + sup.getField()); Son son = new Son(); System.out.println("son.field:" + son.field + " son.getField:" + son.getField() + " son.getSupField:" + son.getSuperField()); } }/* OUTPUT sup.field:0 sup.getField():1 son.field:1 son.getField:1 son.getSupField:0 */复制代码🎜Die
start()
-Methode in der obigen test
-Klasse erhält eine Referenz auf Animal
und kann natürlich auch eine Referenz von Tier
Die exportierte Codeklasse>. Beim Aufruf der Methode eat()
wird natürlich die in Monkey
definierte Methode ohne Typkonvertierung verwendet. Denn ein Upcasting von Monkey
zu Animal
kann nur die Anzahl der Schnittstellen reduzieren, aber nicht weniger Schnittstellen als Animal
. 🎜🎜Eine nicht besonders passende Analogie: 🎜Der Besitz deines Vaters wird an dich vererbt, und dein Besitz gehört immer noch dir. Im Allgemeinen wird dein Besitz nicht geringer sein als der deines Vaters. 🎜🎜在 test.start()
方法中,定义传入的是 Animal
的引用,但是却传入Monkey
,这看起来似乎忘记了Monkey
的对象类型,那么为什么不直接把test
类中的方法定义为void start(Monkey monkey)
,这样看上去难道不会更直观吗。
直观也许是它的优点,但是就会带来其他问题:Animal
不止只有一个Monkey
的导出类,这个时候来了个pig
,那么是不是就要再定义个方法为void start(Monkey monkey)
,重载用得挺溜嘛小伙子,但是未免太麻烦了。懒惰才是开发人员的天性。
因此这样就有了多态
的产生
方法调用中分为 静态绑定
和动态绑定
。何为绑定:将一个方法调用同一个方法主体关联起来被称作绑定。
静态绑定
:又称为前期绑定。是在程序执行前进行把绑定。我们平时听到"静态"的时候,不难免想到static
关键字,被static
关键字修饰后的变量成为静态变量,这种变量就是在程序执行前初始化的。前期绑定
是面向过程语言中默认的绑定方式,例如 C 语言只有一种方法调用,那就是前期绑定。引出思考:
public static void start(Animal animal) { animal.eat(); }复制代码
在start()
方法中传入的是Animal
的对象引用,如果有多个Animal
的导出类,那么执行eat()
方法的时候如何知道调用哪个方法。如果通过前期绑定
那么是无法实现的。因此就有了后期绑定
。
动态绑定
:又称为后期绑定
。是在程序运行时根据对象类型进行绑定的,因此又可以称为运行时绑定
。而 Java 就是根据它自己的后期绑定机制,以便在运行时能够判断对象的类型,从而调用正确的方法。小结:
Java 中除了 static
和 final
修饰的方法之外,都是属于后期绑定
显然通过动态绑定
来实现多态
是合理的。这样子我们在开发接口的时候只需要传入 基类 的引用,从而这些代码对所有 基类 的 导出类 都可以正确的运行。
其中Monkey
、Pig
、Dog
皆是Animal
的导出类
Animal animal = new Monkey()
看上去不正确的赋值,但是上通过继承,Monkey
就是一种Animal
,如果我们调用animal.eat()
方法,不了解多态的小伙伴常常会误以为调用的是Animal
的eat()
方法,但是最终却是调用了Monkey
自己的eat()
方法。
Animal
作为基类,它的作用就是为导出类建立公用接口。所有从Animal
继承出去的导出类都可以有自己独特的实现行为。
有了多态机制,我们可以根据自己的需求对系统添加任意多的新类型,而不需要重载void start(Animal animal)
方法。
在一个设计良好的OOP程序中,大多数或者所有方法都会遵循start()
方法的模型,只与基类接口同行,这样的程序就是具有可扩展性的,我们可以通过从通用的基类继承出新的数据类型,从而添加一些功能,那些操纵基类接口的方法就不需要任何改动就可以应用于新类。
我们先来复习一下权限修饰符:
?? √geschützt | √ | √ | √ | |
---|---|---|---|---|
Standard | √ | √ | × | × |
privat | √ | × | × | × |
私有方法带来的失灵: 复习完我们再来看一组代码: public class PrivateScope { private void f() { System.out.println("PrivateScope f()"); } public static void main(String[] args) { PrivateScope p = new PrivateOverride(); p.f(); } }class PrivateOverride extends PrivateScope { private void f() { System.out.println("PrivateOverride f()"); } }/* OUTPUT PrivateScope f() */复制代码 是否感到有点奇怪,为什么这个时候调用的 结论: 只有非 我们通过 Idea 写代码的时候,重写的方法头上可以标注 这样也可以很好的提示我们非重写方法,而是全新的方法。 域带来的失灵: 当小伙伴看到这里,就会开始认为所有事物(除 让我们再看看下面这组代码: class Super { public int field = 0; public int getField() { return field; } }class Son extends Super { public int field = 1; public int getField() { return field; } public int getSuperField() { return super.field; } }class FieldTest { public static void main(String[] args) { Super sup = new Son(); System.out.println("sup.field:" + sup.field + " sup.getField():" + sup.getField()); Son son = new Son(); System.out.println("son.field:" + son.field + " son.getField:" + son.getField() + " son.getSupField:" + son.getSuperField()); } }/* OUTPUT sup.field:0 sup.getField():1 son.field:1 son.getField:1 son.getSupField:0 */复制代码 从上面代码中我们看到 其实不然,当 虽然这种问题看上去很令人头痛,但是我们开发规范中,通常会将所有的域都设置为 private,这样就不能直接访问它们,只能通过调用方法来访问。 static 带来的失灵: 看到这里,小伙伴们应该对多态有个大致的了解,但是不要掉以轻心哦,还有一种情况也是会出现失灵的,那就是如果某个方法是静态的,那么它的行为就不具有多态性。 老规矩,我们看下这组代码: class StaticSuper { public static void staticTest() { System.out.println("StaticSuper staticTest()"); } }class StaticSon extends StaticSuper{ public static void staticTest() { System.out.println("StaticSon staticTest()"); } }class StaticTest { public static void main(String[] args) { StaticSuper sup = new StaticSon(); sup.staticTest(); } }/* OUTPUT StaticSuper staticTest() */复制代码 静态方法是与类相关联,而非与对象相关联 3.构造器与多态首先我们需要明白的是构造器不具有多态性,因为构造器实际上是 我们先回到开头的那段神秘代码: 其中输出结果是: /* polygon() before cal() square.cal(), border = 0 polygon() after cal() square.square(), border = 4 */复制代码 我们可以看到先输出的是基类 这是因为基类的构造器总是在导出类的构造过程中被调用,而且是按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。 因为构造器有一项特殊的任务:检查对象是否能正确的被构造。导出类只能访问它自己的成员,不能访问基类的成员(基类成员通常是private类型)。只有基类的构造器才具有权限来对自己的元素进行初始化。因此,必须令所有构造器都得到调用,否则就不可能正确构造完整对象。 步骤如下:
打个不是特别恰当的比方:你的出现是否先要有你父亲,你父亲的出现是否先要有你的爷爷,这就是逐渐向上链接的方式 构造器内部的多态行为有没有想过如果在一个构造器的内调用正在构造的对象的某个动态绑定方法,那么会发生什么情况呢? 动态绑定的调用是在运行时才决定的,因为对象无法知道它是属于方法所在的那个类还是那个类的导出类。如果要调用构造器内部的一个动态绑定方法,就要用到那个方法的被覆盖后的定义。然而因为被覆盖的方法在对象被完全构造之前就会被调用,这可能就会导致一些难于发现的隐藏错误。 问题引索: 一个动态绑定的方法调用会向外深入到继承层次结构内部,它可以调动导出类里的方法,如果我们是在构造器内部这样做,那么就可能会调用某个方法,而这个方法做操纵的成员可能还未进行初始化,这肯定就会招致灾难的。 敏感的小伙伴是不是想到了开头的那段代码: 输出结果是: /* polygon() before cal() square.cal(), border = 0 polygon() after cal() square.square(), border = 4 */复制代码 我们在进行 这组代码初始化的实际过程为:
|
Das obige ist der detaillierte Inhalt vonWenn Sie heutzutage sagen, dass Sie Java kennen, müssen Sie sich mit Polymorphismus auskennen.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!