----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
如果说继承,抽象类,接口可以让我们组建一个能够实现复杂功能多层次的继承体系,那么多态就是对这个体系的应用。多态实现了对事物定义和实现的分离,就是说将做什么和怎么做分开。多态降低了类与类直接的耦合性,从而提高了扩展性。
继承体系中的对象,既可以被做为它本身的类型,也可以被当做它的基类。这也是是实现多态的前提。
多态
多态也是面向对象的特征之一。
多态:可以理解为事物存在的多种体现形态
人:男人,女人
动物:猫,狗。
猫 x = new 猫();
动物 x = new 猫();
多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
多态的前提
必须是类与类之间有关系。要么继承,要么实现。
通常还有一个前提:存在覆盖。
多态的好处
预先定义的程序可以运行后期程序的内容。
多态的出现大大的提高程序的扩展性。
多态的弊端
虽然可以预先使用,但是只能访问父类中已有的功能,运行的是后期子类的功能内容。
不能预先使用子类中定义的特有功能。
多态的应用
package itcast.heima;
//为了提高主板功能的扩展性。
//定义了规则。让后期的出现的功能板块,
//只要覆盖该规则,就可以被这个主板使用。
interface PCI
{
public void open();
public void close();
}
class MainBoard{
public void run(){
System.out.println("mainboard run");
}
public void usePCI(PCI p){
//判断p是否指向实体对象
if(p!=null){
p.open();
p.close();
}
}
}
class NetCard implements PCI{
public void open(){
System.out.println("NetCard open");
}
public void close(){
System.out.println("NetCard close");
}
}
class SoundCard implements PCI{
public void open(){
System.out.println("SoundCard open");
}
public void close(){
System.out.println("SoundCard close");
}
}
public class DuoTaiDemo5 {
public static void main(String[] args) {
MainBoard mb = new MainBoard();
mb.run();
mb.usePCI(null);
mb.usePCI(new NetCard());
mb.usePCI(new SoundCard());
}
}
NetCard和SoundCard都实现了PCI接口,所以可以被当做PCI对象传入usrPCI(PCI p)方法中,即使在后期
我们
在多态中成员函数的特点
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员 函数在多态调用时,编译看左边,运行看右边。
对待静态方法编译和运行都看左边,因为静态方法先于对象已经存在。
package itcast.heima;
class Fu
{
int num = 5;
void method1()
{
System.out.println("fu method_1");
}
void method2()
{
System.out.println("fu method_2");
}
static void method4()
{
System.out.println("fu method_4");
}
}
class Zi extends Fu
{
int num = 8;
void method1()
{
System.out.println("zi method_1");
}
void method3()
{
System.out.println("zi method_3");
}
static void method4()
{
System.out.println("zi method_4");
}
}
class DuoTaiDemo4
{
public static void main(String[] args)
{
Fu f = new Zi();
System.out.println(f.num);
f.method4();
Zi z = new Zi();
z.method4();
}
}
打印结果
f.method4()和z.method4()都是调用的的子类的方法。
如果父类中没有此方法,子类中有此方法,将编译失败。
反之将运行失败。
在多态中,成员变量的特点
无论编译和运行,都参考左边(引用型变量所属的类)。
例子来源于thinking in java
package itcast.heima;
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 getSuperField(){
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
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.getSuperField());
}
}
打印结果
可以看出成员变量是跟着引用走的,哪个对象去访问field,就返回哪个对象的field。thing in
java中认为,任何域的访问操作都是有编译器解析的,因此不是多态。在本例中field有两个Super.field和Sub.field。Super只包含Super.field。而Sub两个都包含,但默认默认域Sub.field。
因此才会有以上结果。当去掉Sub.field域后,Sub的中就只有Super.field。这是sub.field和field.getField()都将访问父类的field=0;
涉及到构造函数的多态
package itcast.heima;
abstract class Glyph {
abstract void draw();
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println(
"RoundGlyph.RoundGlyph(), radius = "
+ radius);
}
void draw() {
System.out.println(
"RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
这里首先涉及到了继承中的对象初始化,创建一个子类是,会先去调用父类的构造函数,而后运行自己的构造器。但是在调用父类构造函数之前,子类对象会首先给对象赋予默认初始化值。所以radius在被父类构造函数运行之前会被赋值0,父类构造函数运行完后,会给radius显
示初始化radius=5,最后执行子类构造函数。
在这段代码中运行父类构造函数时,它调用了draw()方法,而draw()方法又被子类覆盖,所以父类构造函数运行的是RoundGlyph.draw(),正是这个方法可以访问子类中尚未初始化的数据(显示初
始化和构函数初始化)这会带来意想不到的麻烦,且相当隐蔽,所以尽量不要在父类构造器中访问被子类覆盖的方法。
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------