多态是继封装和继承之后的面向对象程序设计语言的第三者基本特征。
封装通过合并特征和行为来创建新的数据类型。继承允许将多种类型(从同一基类导出的)视为同一类型来处理
多态也称作动态绑定、后期绑定、运行时绑定,它的作用就是消除类型之间的耦合关系。
对象既可以作为它自己本身的类型使用,也可以作为它的基类型使用。
把对某个对象的引用视为对其基类型的引用,称为向上转型。
例如:Animal cat=new Cat();
将一个方法调用同一个方法主体关联起来,被称作绑定。
前期绑定在程序执行前,由编译器进行绑定,C语言中方法调用就是前期绑定。
后期绑定在运行时根据对象的类型进行绑定,不是参数的类型。因此可以编写只与基类打交道的程序代码,并且这些代码对所有的导出类都可以准确运行。因为这些导出类的类型都是一样的。
public class Wind extends Instrument {
public void play(Wind wind){
System.out.println("Wind.play(wind)");
}
public void play(Instrument ins){
System.out.println("wind.play(Ins)");
}
public static void main(String[] args) {
Instrument ins=new Wind();
Wind wind=new Wind();
//根据对象的类型进行后期绑定
ins.play(ins);
wind.play(wind);
ins.play(wind);
wind.play(ins);
}
}
class Instrument{
public void play(Instrument ins){
System.out.println("Instrument.play()");
}
}
//由于子类wind覆盖了基类Ins的play(Instrument ins)方法,所以ins只能调用wind的play(Instrument ins)方法。如果没有被覆盖,它会调用自己的Play方法
wind.play(Ins)
//wind调用自己的play方法
Wind.play(wind)
//ins本来是要调用自己的play方法,但是被覆盖了,只能调用wind的相同的方法签名
wind.play(Ins)
wind.play(Ins)
java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。
public class Shapes {
//该方法是一种工厂,可以随机产生一个对象
public Shape newShape(){
int j=new Random(83).nextInt(2);
switch (j){
//default写在前面,
default:
//向上转型,返回Shape类型
case 0:return new Circle();
case 1:return new Square();
}
}
public static void main(String[] args) {
Shapes sh=new Shapes();
Shape[] shapes=new Shape[9];
for(int i=0;i<shapes.length;i++){
//只能获得一个通用的shape引用,而不知具体类型
shapes[i]=sh.newShape();
}
for(Shape shp:shapes){
//对draw方法进行动态绑定,
shp.draw();
}
}
}
class Shape{
public void draw(){};
}
class Circle extends Shape{
public void draw(){
System.out.println("Circle draw");
}
}
class Square extends Shape{
public void draw(){
System.out.println("Square Draw");
}
}
只有非private方法才可以被覆盖,在导出类中,对于基类中的private方法,最好采用不同的名字。
public class OverridePrivate {
//私有方法被自动认为是final方法,对导出类屏蔽,
private void f(){
System.out.println("private f()");
}
public static void main(String[] args) {
//引用op的实际类型是OverridePrivate
OverridePrivate op=new Derived();
Derived de=new Derived();
op.f();
de.f();
}
}
class Derived extends OverridePrivate{
//对于基类中的private最好在导出类中取不同的名字
//并不会覆盖基类中的方法,它是一个全新的方法
public void f(){
System.out.println("public f()");
}
}
//结果:
private f()
public f()
只有普通的方法调用可以是多态的。任何域访问操作都将由编译器解析,并不是多态的。
public class FiledAccess {
public static void main(String[] args) {
Super sup=new Sub();
Sub sub=new Sub();
System.out.println("sup.field="+sup.field+
",sup.getField="+sup.getField());
System.out.println("sub.field="+sub.field+
",sub.getField="+sub.getField()+
",sub.getSuperField="+sub.getSuperField());
}
}
class Super{
//域field分配不同的存储空间
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;}
}
结果:
//注意,对sup.getField访问域,并不是返回sup中的域值0,实际返回真实对象new Sub()的域值。
//要返回该值,必须调用super.field才行
sup.field=0,sup.getField=1
sub.field=1,sub.getField=1,sub.getSuperField=0
可以简单总结为:例如 Animal cat =new Cat();
普通方法调用 如果没有被覆盖,那与实际类型Animal有关;如果被覆盖,那只能调用子类的方法。但是访问域cat.field与新对象new Cat()的field有关。如果要调用基类的域,使用super.field。
静态方法也是不具有多态性的,它是与类相关联,而不是与单个对象相关联:
public class StaticPoly {
public static void main(String[] args) {
StaticSuper sup=new StaticSub();
StaticSub sub=new StaticSub();
sup.staticGet();
sup.dynamicGet(1);
sup.dynamicGet();
sub.staticGet();
sub.dynamicGet();
}
}
class StaticSuper{
public static void staticGet(){
System.out.println("Base staticGet");
}
public void dynamicGet(int i){
System.out.println("Base staticGet i="+i);
}
public void dynamicGet(){
System.out.println("Base staticGet");
}
}
class StaticSub extends StaticSuper{
public static void staticGet(){
System.out.println("Derived staticGet");
}
public void dynamicGet(){
System.out.println("Derived staticGet");
}
}
结果:
//静态方法与类有关,所以分别调用实际的类的静态方法
Base staticGet
//调用实际的类的普通方法
Base staticGet i=1
//被子类覆盖了,所以只能调用子类的相同方法签名
Derived staticGet
//静态方法与类有关,
Derived staticGet
//调用实际类的普通方法
Derived staticGet
构造器和多态
构造器是static方法,并不具有多态性。
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。
因为:导出类只能访问自己的成员,不能访问基类中的成员(基类成员通常是private类型)。只有基类的构造器才能对自己的元素进行初始化。因此必须令所有构造器都得到调用。
在导出类的构造器主体中,如果没有明确指定调用某个基类构造器,会默默调用默认构造器(无参构造器)。
public class Sandwich extends Lunch{
private String sd=opop("Sandwich non-static");
private static String bb=opop("Sandwich static");
Sandwich(){
System.out.println("Sandwich Constructor");
}
public static void main(String[] args) {
System.out.println("进入main方法");
opop("this is a test");
Bread bread=new Bread();
new Sandwich();
}
public static String opop(String sss){
System.out.println(sss);
return "";
}
}
class Meal{
private static String str=print11Static("Meal static 变量");
private String a=print11Static("Meal non-static");
Meal(){
System.out.println("Meal Constructor");
}
public static String print11Static(String s){
System.out.println(s);
return null;
}
}
class Lunch extends Meal{
Lunch(){
System.out.println("Lunch Constructor");
}
}
class Bread{
private static int i=ppp("Bread Static");
private int j=ppp("Bread non-static ");
Bread(){
System.out.println("Bread Constructor");
}
public static int ppp(String s){
System.out.println(s);
return 22;
}
}
//结果
//先初始化基类中的静态变量
Meal static 变量
//初始化本类的静态变量
Sandwich static
//静态变量初始化完成后,进入到main方法,此时基类、本类中的非静态变量并没有初始化
进入main方法
//按程序中的定义顺序执行
this is a test
//定义一个对象
Bread Static
Bread non-static
Bread Constructor
//新建本类对象之前,先初始化基类非静态变量,调用基类的构造器
Meal non-static
Meal Constructor
Lunch Constructor
//初始化本类的非静态变量
Sandwich non-static
//最后调用本类的构造器
Sandwich Constructor