Java之多态(动态绑定)

封装通过合并特征和行为来创建新的数据类型,“实现隐藏”则通过细节“私有化”把接口和实现分离。
继承可以允许向上转型,将对象视为它自己本身的类型或其基类来加以处理,但是这必须满足“is a”关系,而多态则消除类型之间的耦合关系,允许一种类型表现出与其他相似类型之间的区别,只要他们都是从同一个基类导出而来。多态通过分离做什么和怎么做,从另一个角度讲接口和实现分离开。
但是继承的向上转型会缩小接口,使得派生类扩展的方法向上转型之后不能使用。
package jin.feng1;
 class Person{
  void print(){
  System.out.println();
 }
 //
}
public class Student extends Person
{
 public void sst(){}
 public static void main(String[] args)
 {
  Person p1=new Student();
  p1.sst(); //编译错误,不能访问
  p1.print();
 }
}

后期绑定(多态机制):派生类和基类都含有相同的方法(覆盖),调用时不知道该调用哪个方法,是调用基类的还是派生类的方法
动态绑定是对于向上转型的对象而言,如果不存在向上转型,则该调用哪个方法调用哪个方法。对于向上转型的对象调用方法,它调用的方法肯定是向上转型这个对象类型所提供的方法,即Person类型仅仅提供print()方法,所以它可选的方法是基类非private方法,它如果调用的方法是基类仅有(子类没有覆盖),则调用基类方法,如果调用的方法子类覆盖了则调用相应子类方法。
将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行之前进行绑定,叫做前期绑定。
后期绑定,是在运行时根据对象的类型进行绑定。后期绑定也叫做动态绑定或运行是绑定。以便在运行是能判断对象的类型,从而调用恰当的方法。也就是说编译器不知道到具体对象类型,但是方法调用机制能找到正确的方法体,并加以调用。
Java中除了static方法和final方法之外,其他所有的方法都是后期绑定的。它会自动发生。
 
class Person{
  void print(){System.out.println(" person");}
void sst(){}
}
 class Person1 extends Person{
  void print(){System.out.println(" person1");}
 }
 class Person2 extends Person{
  void print(){System.out.println(" person2");}
 }
public class Student
{
 static void tune(Person p1){
  p1.print();//子类覆盖,则根据具体类型调用具体方法
 }
 public static void main(String[] args)
 {
  Person p1=new Person1();//p1是基类Person类型,它可选的调用的子类没有覆盖方法sst()和子类覆盖的方法print()
p1.sst();//子类没有覆盖,则直接调用基类
Person p2=new Person2(); 

  tune(p1); tune(p2);//
 }
}

//output
person1
person2
虽然p1,p2是Person类型,但是它是由向上转型获得的,因此在运行期间会动态绑定到相应类型。并调用相应方法。
缺陷1:“私有方法的覆盖”
覆盖是针对非私有方法而言的,而程序中如果基类的方法是私有的,但是子类也有相应的方法名,此时的方法并不是覆盖,而是子类的新方法。
public class Student{
 private void print(){System.out.println(" Student");}
 public static void main(String[] args)
 {
  Student p1=new Student1();
  p1.print();//貌似是动态绑定,其实调用的是自己类中的private方法。
 }
}
 class Student1 extends Student
{
  void print(){System.out.println(" Student1");}
}
//output
Student
缺陷2:成员变量和静态方法
package jin.feng1;
class Super{
 int field=0;
 int getField(){return field;}
}
class Sub extends Super{
int field=1;
int getField(){return field;}
int getSuperField(){return super.field;}
}
public class Student{
 
 public static void main(String[] args)
 {
  Super super1=new Sub();
  System.out.println("成员变量访问:"+super1.field+"方法访问:"+super1.getField());
  Sub sub1=new Sub();
  System.out.println(sub1.getField()+"  "+sub1.getSuperField());
 }
}

//output
成员变量访问:0方法访问:1
1  0
补充:如果基类和派生类都定义一个相同的成员变量名,且不是私有的,则子类同时含有这两个变量,如果需要访问基类的同名变量则需要super.field,如果访问基类的同名方法则需要super.getField()
 向上转型: Super super1=new Sub();成员变量访问的时候是基类的field,而通过方法访问则动态绑定访问派生类的getField()方法,则调用派生类的field.
发现成员变量访问操作不会动态绑定,原因是什么呢?
任何成员变量访问操作都将由编译器解析,因此不是多态的。
这在平时是不会发生的,因为所有的成员变量一般都是private,不能直接访问他们。
缺陷3、静态方法
因为静态方法属于整个类,不会跟着具体的对象而进行变换类型。可以说它跟对象没有关系,所以没有动态绑定这一说。
package jin.feng1;
class Static1{
 static void print(){System.out.println("Static1 method");}
 void ss(){System.out.println("normal1 method");}
}
class Static2 extends Static1{
 static void print(){System.out.println("Static2 method");}
 void ss(){System.out.println("normal2 method");}
}
public class StaticDynmic
{
public static void main(String args[]){
 Static1 static1=new Static2();
 static1.print();
 static1.ss();
}
}

//output
Static1 method
normal2 method
构造器与多态
Java中构造器不同于其他的方法,构造器不具有多态性,因为它是static方法,不过是static声明是隐式的,如Construct c1=new Construct();就会调用构造方法,它不需对象名对其进行调用,所以它在对象创建之前就要进行调用,是类的方法。其他方法都是先创建对象,再通过对象对其进行调用。

package jin.feng1; 
class Constructor11{ 
 void draw(){System.out.println(" Constructor11 draw()");} 
  
 public Constructor11(){ 
  System.out.println("Constructor11:before draw()"); 
  draw(); 
  System.out.println("Constructor11:after draw()"); 
 } 
} 
class Constructor12 extends Constructor11{ 
 private int radius=2; 
 public Constructor12(){ 
  System.out.println("Constructor12:before draw()"); 
  draw(); 
  System.out.println("Constructor12:after draw()"); 
 } 
 void draw() 
 { 
  System.out.println("Constructor12+draw "+radius); 
 } 
} 
public class Constuctor22 
{ 
 public static void main(String[] args) 
 { 
  new Constructor12(); 
 } 
} 

//output
Constructor11:before draw()
Constructor12+draw 0
Constructor11:after draw()
Constructor12:before draw()
Constructor12+draw 2
Constructor12:after draw()
调用基类构造方法时:第一次调用draw时,radius是0,还未被初始化。
调用子类构造方法时:第二次调用draw时,radius是2,即类内初始化
首先类加载。分配给对象的存储空间初始化为0.接着初始化基类成员变量,调用基类构造器,初始化子类成员变量,调用子类构造器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值