黑马程序员 java_多态

                ----------- 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学习型技术博客、期待与您交流! ------------

你可能感兴趣的:(java)