java多态什么意思,什么叫多态java
如何解决写爬虫IP受阻的问题?立即使用。
今天,我像往常一样来到公司。坐在自己的工作站上,打开电脑,‘又是一天搬砖’。想了想,我‘巧妙’的打开了思路,看了今天的要求,敲了敲代码。咦,这些代码是谁写的,怎么会出现在我的代码里,而且还处于提交状态?我记得我没有写它们,所以我饶有兴趣地看着它们:
这不是多态性吗?我不禁想知道是谁在我的电脑上写的测试。
你知道输出会是什么吗?
后面传来一个声音,因为我在想输出结果,没在意声音的来源。我继续看代码,得出结论:
调用()之前的多边形()
square.cal(),border=2
cal()后的多边形()
Square.square(),border=4复制代码,心里想:就这样?至少你是个Java开发工程师好吗?虽然平时搬砖,但还是有些基本功的。不禁有点小骄傲~
这是你的答案吗?你看起来也不太好。
那个声音突然又响起来了。这次我不淡定了,尼玛!我脑子里也在想答案好吗?谁能看到?而且我说的话让人这么想展示一套阿伟十八式。你是谁?带着一丝迷茫和愤怒转过头。为什么没有人?不要让我想,‘蔡晓,醒醒,你为什么在工作时睡着了?’?
上班,睡着了?我睁开眼睛,环顾四周。原来是一场梦。我松了一口气。一看到部门主管站在我面前,上班时间睡觉,你是身体不适还是怎么的?昨天写了一堆bug没改。今天提交了一些乱七八糟的东西。我认为你这个月的表现是不受欢迎的,基于你的表现,我要开始考虑这个部门了。
我没有,我没有,我也不知道为什么睡着了。让我解释一下!在我说出这句话之前,我想带你回家,带着我心中的花。在那个深夜的酒吧里,我不在乎是真是假。请摇摆,忘记他。你是最迷人的女孩。要知道,闹铃响的时候,我突然站了起来,后背微湿,额头微汗。我看了看手机。周六,八点半,原来是做梦!
奇怪,怎么会做这么奇怪的梦?太吓人了。然后我想到了梦里的那部分代码。我的结果错了吗?凭着记忆,我在电脑上又敲了一遍,运行结果如下:
/*
调用()之前的多边形()
square.cal(),border=0
cal()后的多边形()
square.square(),border=4
*/复制代码square.cal(),border的结果其实是0而不是2。我现在连多态性都不懂了吗?在电脑前,你不知道你是否得到了正确的答案!不管有没有,让我们用小菜一碟来复习多态性吧!
有些朋友可能对不止square.cal()感到困惑,border的结果是0。也有人质疑为什么square.square(),border=4先输出。那我们就从疑惑开始吧!
多态
在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。
多态性不仅可以改善代码的组织结构和可读性,还可以创建可扩展的程序。多态的作用是消除类型之间的耦合关系。
1. 向上转型
根据里氏替代原理:基类能出现的地方,子类当然也能出现。
一个对象可以用作它自己的类型或它的基类型。这种将对对象的引用视为对其基类型的引用的做法称为-向上转换。因为父类在子类之上,子类引用父类,所以叫向上转换。
公共类动物{ void eat() {
system . out . println( Animal eat());
}
}猴类延伸动物{ void eat() {
System.out.println(猴子吃());
}
} class test { public static void start(Animal Animal){
animal . eat();
} public static void main(String[]args){
猴猴=新猴();
开始(猴子);
}
}/*输出:
猴子吃()
*/复制代码。上面测试类中的start()方法接收一个动物的引用,自然也可以接收从Animal导出的类。调用eat()方法时,自然使用Monkey中定义的eat()方法,没有任何类型转换。因为从猴子向上过渡到动物只能减少接口数量,不会比动物少。
一个不恰当的类比:你父亲的财产会继承给你,你的财产还是你的。一般来说,你的财产不会比你父亲少。
忘记对象类型
在test.start()方法中,定义是在对Animal的引用中传递的,但它是传递给Monkey的。好像忘记了Monkey的对象类型,为什么不直接在测试类中定义方法为void start(Monkey monkey monkey)?看起来不是更直观吗?
直觉可能是它的优势,但也会带来其他问题:动物有不止一个输出类的猴子。这个时候有一只猪,那么是否有必要定义另一种方法为void start(猴子猴子猴子)?超载很滑,小伙子,但是太麻烦了。懒惰是开发者的天性。
因此,存在多态性。
2.显露优势
方法调用分为静态绑定和动态绑定。什么是绑定:将方法调用与方法体相关联称为绑定。
静态绑定:也称为前期绑定。它是在程序执行前绑定的。当我们通常听到“静态”时,我们不可避免地会想到静态关键字。由static关键字修改的变量成为一个静态变量,它在程序执行前被初始化。预绑定是面向过程语言中的默认绑定方法。比如C语言只有一个方法调用,那就是预绑定。引出思考:
公共静态空启动(动物动物){
animal . eat();
}复制的代码传入start()方法中Animal的对象引用。如果Animal有多个导出类,那么在执行eat()方法时,如何知道要调用哪个方法呢?前期绑定的话是无法实现的。所以就有了后期绑定。
动态绑定:也称为后期绑定。它是在程序运行时根据对象类型进行绑定的,所以也可以称为运行时绑定。而Java则是基于自己的后期绑定机制,这样就可以在运行时判断对象的类型,调用正确的方法。小结:
Java属于后期绑定,除了静态和final修饰的方法。
合理即正确
显然,通过动态绑定实现多态性是合理的。这样我们在开发接口的时候只需要传入基类的引用,这样这些代码就可以对基类所有导出的类正确运行。
猴子、猪和狗都是出口类动物。
Animal animal=new Monkey()好像分配不正确,但猴子是继承来的动物。如果我们调用animal.eat()方法,不了解多态性的朋友往往会误认为是animal的eat()方法,但最后却调用了Monkey自己的eat()方法。
Animal是基类,它的作用是为导出的类建立一个通用接口。从Animal继承的所有导出类都可以有自己独特的实现行为。
可扩展性
有了多态机制,我们可以根据需要向系统中添加尽可能多的新类型,而不会使void start(Animal animal)方法过载。
在一个设计良好的OOP程序中,大多数或所有的方法都会遵循start()方法的模型,并且只使用基类接口。这样的节目有可扩展性。我们可以通过从公共基类继承新的数据类型来添加一些功能,那些操纵基类接口的方法可以不加任何修改地应用到新类中。
失灵了?
让我们先来看看权限修饰符:
0 @ 166.com public:所有类可见protected:此类、此包及其子类可见default:此类和此包可见private:此类可见私有方法带来的失灵:
复习完,我们来看一组代码:
公共类PrivateScope { private void f() {
system . out . println( private scope f());
} public static void main(String[]args){
private scope p=new private override();
p . f();
}
}类PrivateOverride扩展PrivateScope { private void f() {
system . out . println( private override f());
}
}/*输出
专用示波器f()
*/复制代码是不是有点奇怪?为什么此时调用的f()是在基类中定义的,而不是像上面提到的那样通过动态绑定调用在导出类PrivateOverride中定义的f()。不知道大家有没有注意到,基类中f()方法的修饰是private。没错,就是这个问题。privateOverride中定义的f()方法是一个全新的方法。因为私有,所以对子类来说是不可见的,自然不能重载。
结论:
只有非私有的修改方法可以被重写。
当我们通过Idea写代码时,重写后的方法可以用@Override标注。如果它不是重写的方法,标记@Override注释将会给出一个错误:
这也可以很好地提醒我们,我们不是在重写方法,而是全新的方法。
域带来的失灵:
朋友看到这里,会开始认为一切(除了私改)都可以发生多态。然而现实不是这样的,只有普通的方法调用才可以是多态的。这就是这里对多态性的误解。
让我们再来看看下面这组代码:
class Super { public int field=0;public int getField(){ return field;
}
}类Son扩展Super { public int field=1;public int getField(){ return field;
} public int getSuperField(){ return super . field;
}
} class field test { 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 . getsupfield());
}
}/*输出
sup.field:0 sup.getField():1
son . field:1 son . getfield:1 son . getsupfield:0
*/复制代码。从上面的代码中我们可以看到,sup.field输出的值并不是在Son对象中定义的,而是由Super本身定义的。这和我们知道的多态性有点冲突。
否则,当超对象被转化为子引用时,任何域访问操作都会被编译器解析,所以不是多态的。在这个例子中,为Super.field和Son.field分配了不同的存储空间,并且Son类是从Super类派生的。因此,子实际上包含了两个称为field的域:它自己的+Super的。
虽然这种问题看起来很头疼,但是在我们的开发规范中,通常所有的域都设置为private,这样就不能直接访问,只能通过调用方法来访问。
static 带来的失灵:
在这里,朋友们应该对多态性有个大概的了解,但不要掉以轻心。还有一种情况会失败,那就是如果某个方法是静态的,那么它的行为就不具有多态性。。
像往常一样,我们来看看这组代码:
class static super { public static void static test(){
system . out . println( static super static test());
}
}类StaticSon扩展static super { public static void static test(){
system . out . println( static son static test());
}
} class static test { public static void main(String[]args){
static super sup=new static son();
sup . static test();
}
}/*输出
静态超级静态测试()
*/复制代码静态方法是与类相关联,而非与对象相关联
3.构造器与多态
首先,我们需要明白构造函数不是多态的,因为构造函数实际上是一个静态方法,只是静态的声明是隐式的。
让我们回到开头的神秘代码:
其中输出结果是:
/*
调用()之前的多边形()
square.cal(),border=0
cal()后的多边形()
square.square(),border=4
*/复制代码。我们可以看到第一个输出是基类polygon中构造函数的方法。
这是因为导出类的构造过程中总是调用基类的构造函数,而且是按照继承层次结构逐渐链接起来的,这样就可以调用每个基类的构造函数。
因为构造函数有一个特殊的任务:检查对象是否能被正确构造。导出类只能访问自己的成员,不能访问基类的成员(基类的成员通常是私有的)。只有基类的构造函数有权初始化自己的元素。所以必须调用所有的构造函数,否则不可能正确构造完整的对象。
步骤如下:
调用基类构造函数,这一步会递归继续。首先,构造这个层次结构的根,然后是下一级的导出类,直到最底层的导出类调用声明顺序中成员的初始化方法,并调用导出类构造其主体。比如不是特别合适:是不是一定要有你爸爸才出现,还是你爷爷才出现?这是逐步衔接的方式。
构造器内部的多态行为
你有没有想过,如果在构造函数内部调用正在构造的对象的某个动态绑定方法,会发生什么?
动态绑定的调用是在运行时决定的,因为对象无法知道自己是属于方法所在的类,还是属于那个类导出的类。如果您想在构造函数中调用动态绑定方法,您需要使用该方法的重写定义。但是,由于被覆盖的方法将在对象完全构造之前被调用,这可能会导致一些难以发现的隐藏错误。
问题线索:
动态绑定的方法调用将深入到继承层次结构中,它可以调动导出类中的方法。如果在构造函数内部这样做,可能会调用某个方法,这个方法操纵的成员可能还没有初始化,肯定会导致灾难。
你敏感的朋友有没有想到开头的代码:
输出是:
/*
调用()之前的多边形()
square.cal(),border=0
cal()后的多边形()
square.square(),border=4
*/复制代码。当我们初始化正方形对象时,我们将首先初始化多边形对象。多边形构造函数中有一个cal()方法。这时我们采用了动态绑定机制,调用了square的cal(),但是这次边界变量还没有初始化。int类型的默认值是0,所以我们得到square.cal()的输出,border=0。看到这里,朋友们有没有拨云见日的感觉!
代码初始化的实际过程是:
在其他事情发生之前,当通过将分配给对象的存储空间初始化为二进制0来调用基类构造函数时,将调用覆盖的cal()方法。由于步骤1,border的值为0。按照声明的顺序调用成员的初始化方法。调用导出类的构造函数体。不知道下次会做什么样的梦~
那是最后一年。如果你懂Java,你就得知道多态的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。