Java学习笔记——多态(polymorphic)

多态

概念:方法或对象具有多种形态,这是面向对象的重要特征,多态是建立在封装和继承基础之上的。
多态具体体现在以下两个方面:

  1. 方法的多态:方法的重写和重载(详细内容可以看这篇博客:overload(重载)和override(重写)的区别)
  2. 对象的多态(核心):一个对象的编译类型和运行类型可以不一样

对象的多态详解

对象的多态体现在:一个对象的编译类型和运行类型可以不一样(其实也就是向上转型)。用代码来说可以写成:Base b = new Sub();如果没有特殊说明,多态一般就是指对象的多态。
编译类型由声明该变量时使用的类型决定,即 = 左边的类型;运行类型由实际赋给该变量的对象决定,即 = 右边的类型。
对于对象的多态,有几点很关键:

  1. 编译类型在定义对象是就确定了,不能改变
  2. 运行类型是可以变化的
  3. 多态的前提是类之间存在继承关系

多态的向上转型

向上转型的本质:父类的引用指向子类对象父类类型 引用名 = new 子类类型();
向上转型的调用规则:

  1. 向上转型中,可以调用父类的所有成员(需要遵守访问权限),但是不能调用子类的特有成员(方法及变量),因为在编译阶段,能调用哪些成员是由编译类型决定的。(不是有动态绑定机制吗,为什么不行?因为调用的时候还没有过编译,编译过了才能运行,才有运行类型,才有动态绑定。即动态绑定机制是编译后,运行时产生的,这里如果调用了子类特有的成员,过不了编译。)
  2. 方法的最终运行效果看子类的具体实现,即运行时从子类(运行类型)开始逐级往上找成员方法,规则和前面讲的(子类访问成员变量或方法的规则)调用规则一致子类访问方法从运行类型开始查找
  3. 变量的值则直接看编译类型中对应的变量值(子类访问属性从编译类型开始查找

代码示例(重点)

public class PolyTest {
    public static void main(String[] args) {
        Sub s =new Sub();
        System.out.println(s.count);//20
        System.out.println(s.num);//22
        s.display();//20
        //==========================重点==========================
        Base b = s;//允许,向上转型
        System.out.println((b == s));//true,两个引用都指向同一个对象(在堆中地址一样,内容一样)
        //报错,向上转型时,不能访问子类中特有的成员变量及方法,因为在编译阶段,能调用哪些成员是由编译类型决定的。
        //System.out.println(b.num);
        System.out.println(b.count);//向上转型时,子类访问属性,从编译类型开始向上找,输出Base的count值10
        System.out.println(b.s);    //从编译类型Base开始向上找,输出Super的s值111
        b.display();                //向上转型时,子类访问方法,从运行类型Sub开始向上找,调用Sub中的方法,输出20
        //总结:访问属性时,向上转型的情况要从编译类型开始向上找。其他时候基本都是从子类开始想上找(存疑,暂时这么总结)
    }
}

class Super{
    int s = 111;
}
class Base extends Super{
    int count = 10;
    public void display(){
        System.out.println(this.count);
    }
}
class Sub extends Base{
    int count = 20;
    int num = 22;
    int s = 222;
    public void display(){
        System.out.println(this.count);
    }
}

总结:访问属性时,向上转型的情况要从编译类型开始向上找。其他时候基本都是从子类开始想上找(存疑,暂时这么总结)

多态的向下转型(强制类型转换)

向下转型后,我们就可以调用子类类型中的所有成员(方法和变量),其语法为:
子类类型 引用名 = (子类类型) 父类引用;
对于向下转型有以下注意事项:

  1. 只能强转父类的引用,不能强转父类的对象
  2. 要求转型前的父类引用必须指向的是当前目标类型的对象

代码示例(重点)

 		Sub c = (Sub) b;//向下转型
        System.out.println(c.num);//向下转型后,我们就可以调用子类类型中的所有成员
        Base bb = new Base();
        //Sub cc = (Sub) bb;//运行后报错,只能强转父类的引用,不能强转父类的对象
        Super ss = new Sub();
        //==============================重点===========================================
        //转型前的父类引用必须指向的是当前目标类型的对象
        Base bbb = (Base) ss;//不报错,转型前的父类引用指向sub的对象,
                            // 而sub对象也是目标类型Base类的对象,即ss instanceof Base为true,
                            // 相当于向下转了一级
        bbb.display();//运行类型还是Sub,执行Sub里的函数
        System.out.println(bbb.count);//编译类型为Base,输出Base里的count

instanceof 操作符

语法:对象 instance 类型
用于判断对象的运行类型是否为某类型或其子类,返回true/false

代码示例

class AA {} //父类
class BB extends AA {}//子类
public class Poly {
	public static void main(String[] args) {
		BB bb = new BB();
		System.out.println(bb instanceof BB);// true
		System.out.println(bb instanceof AA);// true
		//aa 编译类型 AA, 运行类型是 BB
		AA aa = new BB();
		System.out.println(aa instanceof AA);//true
		System.out.println(aa instanceof BB);//true,运行类型为AA子类
		Object obj = new Object();
		System.out.println(obj instanceof AA);//false
		String str = "hello";
		//System.out.println(str instanceof AA);//报错,字符串和AA无继承关系
		System.out.println(str instanceof Object);//true
	}
}

你可能感兴趣的:(java,学习,笔记)