关于多态的理解

文章目录

一、概述

二、多态的对象转换

三、instanceof关键字

四、多态中成员特点



一、概述

  • 面对对象程序设计的三大支柱是封装继承多态
  • 多态性是对象多种表现形式的体现。
  • 多态类型:
    • 静态多态性
      • 包括变量的隐藏、方法的重载(指同一个类中,方法名相同但方法的参数类型、个数、次序不同,本质上是多个不同的方法)
    • 动态多态性(运行时多态)
      • 指子类在继承父类(或实现接口)时,重写了父类(或接口)的方法,程序中用父类(或接口)引用去指向子类的具体实例,从代码形式上看时父类(或接口)引用去调用父类(接口)的方法
      • 但是在实际运行时,JVM能够根据父类(或接口)引用所指的具体子类,去调用对应子类的方法,从而表现为不同子类对象有多种不同的形态。
      • 不过,程序代码在编译时还不能确定调用的哪一个类的方法,只有在运行时才能确定,故又称为运行时的多态性。
  • 多态实现的前提条件
    • 继承

    • 重写

      • 以下例子中,首先定义父类Shape及其方法draw(),子类Circle,继承并重写了父类中的draw()方法。

        class Shape{
        		void draw(){}
        }
        
        class Circle extends Shape{
        		void draw(){
        				System.out.println("Circle.draw()");
        		}
        }
        
    • 父类引用指向子类对象Parent p = new Child();

二、多态的对象转换

多态的对象转换分为向上转换(向上转型)向下转换(向下转型)

  • 向上转型:将一个子类的实例转换为一个父类的变量

    • 语法

      Father f = new Son();
      
    • 注意

      • 上转型对象是由子类创建的,但上转型对象会失去子类的一些属性和方法

      • 上转型对象调用方法时,就是调用子类继承和重写过的方法。而不会是新增的方法,也不是父类原有的方法。

      • 上转型对象可以操纵父类原有的属性和功能,无论这些方法是否被重写。

      • 上转型对象可以再强制转换到一个子类对象,强制转换过的对象具有子类所有属性和功能。

        public class Person{  // 父类
        		int age = 10;
        		void getAge(){ // 父类原有的方法
        				System.out.println(age);
        		}
            public static void main(String[] args){
        				Person p = new Male();
        				p.getAge(); // 向上转型对象调用子类重写方法
        				p.getName(); // 编译报错,向上转型对象失去子类的属性和方法
        		}
        }
        class Male extends Person{ // Male子类继承Person父类
        		void getAge(){ // 子类重写父类方法
        				System.out.println(age+1);
        		}
        		void getName(){ // 子类自己的方法
        				System.out.println("Li wei");
        		}
        }
        
  • 向下转型:将一个父类的实例转换为它的子类变量(强制类型转换)

    • 必须使用转换标记 ”(子类名)” 进行显示转换

    • 必须确保要转换的对象时子类的一个实例。如果父类对象不是子类的一个实例,出现运行时异常ClassCastException

    • 语法

      Father f = new Son();
      Son s = (Father)f;
      
      // 错误示范(编译不报错,但运行报错),
      Father f = new Son();
      Son s = Son(f);
      
      // 上述错误示范可以用instanceOF判断(类型防护)
      Person f = new Person();
      if (f instanceof Son){  // 因为f不是Son的实例,所以不执行if内部代码
      		Son s = (Male)f;
          s.getName();
      }
      
    • 注意

      • 向下转型必须先向上转型,否则会发生异常
      • 下转型对象可以引用子类和父类的属性和方法
  • 实例

    public class Human {
        public void sleep() {
            System.out.println("Human sleep..");
        }
        public static void main(String[] args) {
        // (1)向上转型
    		    Male m1 = new Male();
            Human h1 = m1;      
            h1.sleep();
            //h1.speak();  // 此时需要向下转型,否则不能调用speak方法。
            
        // (2)向下转型
            Male m2 = new Male();
            Human h2 = m2;  
            m2 = (Male) h2;         
            m2.speak(); 
        }
    }
    class Male extends Human {
        @Override
        public void sleep() {
            System.out.println("Male sleep..");
        }
        public void speak() {
            System.out.println("I am Male");
        }
    }
    

三、instanceof关键字

  • 作用:用于判断一个对象是否为一个类的实例

  • 语法

    boolean result = obj instanceof Class
    
    • 如果obj为Class的对象,或者是其直接或间接子类,或者是其接口的实现类,则结果result返回true,否则返回false
    Person p1 = new Person();
    Person p2 = new Man();
    Man m1 = new Man();
    System.out.println("p1 instanceof Man);  // false
    System.out.println("p2 instanceof Man);  // true
    System.out.println("m1 instanceof Man);  // true
    
    
  • 注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。

四、多态中成员特点

  • 成员变量编译运行看父类

            由下述例子可以看出,在父类Human中定义的成员变量 i 被赋值为10,在子类中定义的成员变量 i 被赋值为5,程序运行结果为10,而当父类中没有定义成员变量 i 时,出现编译报错,说明多态中成员变量的编译运行看父类

    package multistate;
    
    public class Human {
    	int i = 10;  // 向上转型后输出值为10,只能取到父类的值
        public void sleep() {
            System.out.println("Human sleep..");    
            
    	}
        public void main(String[] args) {
        	Human n = new Male();  // 向上转型
            System.out.println(n.i);
        }
    }
    class Male extends Human {   
    	int i = 5;  // 若父类中未定义i,则编译错误
    	public void sleep() {
    		 System.out.println("Male sleep.."); 
        }
    }
    
  • 成员方法编译看父类,运行看子类

    • 由下述例子可以看出,父类Human中调用的是子类Male中的sleep()方法,而当父类中删除sleep()方法后,出现编译错误,说明多态中成员方法的编译看父类,运行看子类。即:
    • 首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
    • 如果子类覆盖了该方法,就调用子类的方法,否则调用父类方法。
    package multistate;
    public Human {
        public void sleep() {
            System.out.println("Human sleep..");    
    		}
        public static void main(String[] args) {
            Male m = new Male();
            m.sleep();
    
            Human h = new Male(); //(1)向上转型
            h.sleep();            //(2)动态绑定,调用的是子类中的sleep方法
        }
    }
    class Male extends Human {
        @Override    
    		public void sleep() {
            System.out.println("Male sleep..");
        }
    }
    
上一篇:Java基础知识(9)- 面向对象(一)


下一篇:【无标题】