JAVA中多态研究笔记

一 多态分类
1.1 编译时多态
编译器编译成字节码文件前就已经可以确认的需要调用哪个方法或者变量,这里既包括实例成员也包括类成员static
1.2 运行时多态
编译器无法提前预知需要调用哪个实例方法,需要程序运行到该实例方法的主调方法的入口时才能知道.只有实例方法才有真正意义的运行时多态.下面解释

二 编译时多态
2.1 方法重载
这是最常见的编译时多态,根据重载的解析规则,程序员或者编译器可以很容易的判断出需要调用哪个实例方法/static方法/构造方法
TIPS:static方法和实例方法可以互相重载

三 运行时多态
3.1 通过向上/向下造型CAST
举例:

//--------文件名Forefather.java,程序编号4.29-------
public class Forefather {
	public void normal(){
	     System.out.println("这是父类的普通方法");
	}
}
     接下来定义一个子类如下:
//--------文件名Inheritor.java,程序编号4.30--------
public class Inheritor extends Forefather {
	public void normal(){ //这里覆盖了父类的同名方法
	     System.out.println("这是子类的普通方法");
	}
}	
     再写一个用于测试的类:
//--------文件名ShowSomething.java,程序编号4.32--------
public class ShowSomething {
     public static void main(String args[]){
          Forefather pfather;//定义一个父类变量
          Inheritor psun;//定义一个子类变量
          pfather = new Inheritor();//创建子类对象,向上造型
          pfather.normal();//此处是调用父类还是子类的方法呢?
          psun = new Inheritor();//创建子类对象
          psun.normal();//调用子类的方法
     }
}

pfather虽然定义成一个父类变量,但在创建对象时,使用的是子类的构造方法,这在Java中是允许的。Java还允许子类对象为父类变量赋值,比如:

 pfather = psun;

反之,不能用父类对象为子类变量赋值。因此,在调用nornal方法时,就存在一个问题,由于pfather有两个含义:既是父类变量,又是一个子类对象,那么pfather.normal()到底是调用了父类还是子类中的normal()方法?Java中处理这类问题采用动态绑定的方法。

即在调用实例方法时,该变量是什么对象,就调用该对象所属类的方法,与变量声明时所属类无关。

程序4.32的输出结果如下:
在这里插入图片描述
由于pfather在调用normail()方法时是子类对象,所以调用的是子类方法。上面的输出结果也验证了这一点。读者可能对该例觉得困惑:既然要调用子类方法,那么就应该在声明变量时使用子类类型,而不应该使用父类类型。确实,对于本例可以使用这种方式。但对于另外一些情况就未必如此。而且对于这个简单的例子,编译器可以在编译时就确定了调用哪个类的方法.
教科书上已经称这种调用方法为运行时多态了,我认为这个还是编译时多态,因为编译器只观察class ShowSomething就可以判断出要调用哪个实例方法了,因为主调对象的定义和主调对象调用方法在一个class中.
但是如果遇到下面这种就不行了,

3.2 对象做方法的形参,且通过形参调用了实例方法

void doSomething(Forefather p){
     ……..
     p.normal();
}

调用时的实参既可以是Forefather对象,也可以是它的子类对象,而在方法doSomething()中,是无法预先知道运行时的实际情况的,编译器更无法在编译时决定调用哪个类的方法。只有在运行到方法调用语句的时候,系统才能确定实际的对象。

3.3 如何判断使用子类还是父类的同名方法?

  • 父类有方法,子类有覆盖方法:编译通过,执行子类方法。

  • 父类有方法,子类没覆盖方法:编译通过,执行父类方法(子类继承)。

  • 3、父类没方法,子类有方法:编译失败,无法执行。

四 这些情况不是运行时多态
4.1 所有的变量都没有运行时多态
根据对象声明时用的类型来调用的,即使对象指向了一个子类对象,依然只能调用父类定义的变量

4.2 静态方法么有运行时多态
表面原因,静态方法覆盖不能修改返回值类型,不能修改静态修饰符,不能修改参数列表,所以通过子类对象调用子类静态方法,实际调用的是该子类对象定义时的类型的静态方法.
深层原因,运行时多态总时和对象捆绑,但是静态方法不属于任何对象所以它没有运行时多态,实际是调用的显式对象的定义时类型的静态方法.

五 关于方法覆盖和变量隐藏,我的一些理解
很多书本对于覆盖和隐藏的区别,在于覆盖后的父类方法不能通过子类对象来调用;隐藏后的父类变量可以通过子类变量来调用
当时我实测发现:
super.变量名->调用父类的变量
super.方法名([参数列表])->调用父类的方法
super([参数列表])->写在子类构造方法里的第一行,显式调用父类的构造方法
所以,我认为覆盖和隐藏可以混用…

六 关于向下造型,需要进行一定的逻辑判断
如例使用if()可以增强代码健壮性
如果实参使用new Dog()传入可以用if()来过滤掉运行时的错误

public static void method(Animal a){//Animal a = new Dog();
	a.eat();
	if(a instanceof Cat){
		Cat c = (Cat)a;
		c.catchMouse();
	}
}

注意!!!运行时多态讨论的是catchMouse()不是method(),这里用static method()不影响运行时多态

instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。
这里说明下:
类的实例包含本身的实例,以及所有直接或间接子类的实例
instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误

  • 左边的对象实例不能是基础数据类型
  • 左边的对象实例和右边的类不在同一个继承树上 ,编译错误
  • null用instanceof跟任何类型比较时都是false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值