美文网首页
Java多态

Java多态

作者: Cool_Pomelo | 来源:发表于2020-01-14 19:56 被阅读0次

Java多态

编译时类型和运行时类型

理解编译时类型和运行时类型是理解多态的关键

上最直白的定义:

Java的引用变量有两个类型,一个是编译时类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定

这边搬出大佬的解释:

Person p=new Women()(Women类继承自Person类)那么,假如p的属性修饰符为public 访问属性时得到的是Person类的属性还是Women类的属性,方法调用又是哪个类?

答案:会得到Person类的属性,调用Women类的方法。为什么会这样呢?这里就需要知道什么是编译时类型和运行时类型,

Java程序状态会分为编译和运行这两种状态

编译时,JVM会在栈中静态创建基本数据变量,和引用数据变量的引用,回到刚刚那句代码,显然,p这个引用就是在编译时创建的,那么,p的编译时类型就是Person了,

当运行这句java代码时,JVM在堆中为p新建一块内存,对应new Women()这句代码,所以p的运行时类型就是Women

在栈中存放的是一些引用和一些变量,而堆内存中存放的是对象

所以:编译时期可以理解为堆内存中还没有为该对象创建内存,只是在栈中创建了一些基本类型的变量和引用,所以编译时类型就是指在new该对象之前被加载到栈中的属性或方法。而运行时类型,是指new了一个对象出来,在栈中为该对象分配了内存,此时的运行时类型也就是栈中对象的属性或方法了

Person person = new student();
这里就可以很好地理解编译时类型和运行时类型了。
编译时类型是Person,当new了一个对象之后,堆内存中产生了一个Student对象,此时的类型就是运行时类型,也就是Student类型了
class Person {
    public String name;

    public Person() {
        name = "person";
    }

    public void show() {
        System.out.println("person show");
    }
}

class Woman extends Person {
    public String name;

    public Woman() {
        name = "woman";
    }

    public void show() {
        System.out.println("woman show");
    }

    public void myshow(){
        System.out.println("++++++++++++++++++");
    }
}

public class TestDemo {

//    当使用该对象引用进行调用的时候,有这么一条规则,对象调用编译时类型的属性和运行时类型的方法

    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);
        person.show();

        System.out.println();

        Person person1 = new Woman();
        System.out.println(person1.name);
        person1.show();
//        person1.myshow();报错

        System.out.println();

        Woman woman = new Woman();
        System.out.println(woman.name);
        woman.show();
        woman.myshow();
    }

}

多态

理解了上面之后,多态的产生也很简单

编译时类型和运行时类型不一致就可能产生多态

例子:


class BaseClass
{
    public int book = 6;
    public void base()
    {
        System.out.println("父类的普通方法");
    }
    public void test()
    {
        System.out.println("父类的被覆盖的方法");
    }
}
public class SubClass extends BaseClass
{
    //重新定义一个book实例变量隐藏父类的book实例变量
    public String book = "book";
    public void test()
    {
        System.out.println("子类的覆盖父类的方法");
    }
    public void sub()
    {
        System.out.println("子类的普通方法");
    }
    public static void main(String[] args)
    {
        // 下面编译时类型和运行时类型完全一样,因此不存在多态
        BaseClass bc = new BaseClass();
        // 输出 6
        System.out.println(bc.book);
        // 下面两次调用将执行BaseClass的方法
        bc.base();
        bc.test();

        System.out.println("++++++++++++++++++++++++++++++++++++++++++");

        // 下面编译时类型和运行时类型完全一样,因此不存在多态
        SubClass sc = new SubClass();
        // 输出"book"
        System.out.println(sc.book);
        // 下面调用将执行从父类继承到的base()方法
        sc.base();
        // 下面调用将执行从当前类的test()方法
        sc.test();

        System.out.println("++++++++++++++++++++++++++++++++++++++++++");

        // 下面编译时类型和运行时类型不一样,多态发生
        BaseClass ploymophicBc = new SubClass();
        // 输出6 —— 表明访问的是父类对象的实例变量
        System.out.println(ploymophicBc.book);
        // 下面调用将执行从父类继承到的base()方法
        ploymophicBc.base();
        // 下面调用将执行从当前类的test()方法
        ploymophicBc.test();



        // 因为ploymophicBc的编译类型是BaseClass,
        // BaseClass类没有提供sub方法,所以下面代码编译时会出现错误。
        // ploymophicBc.sub();
    }
}

子类其实是一种特殊的父类,Java允许把一个子类对象直接赋给一个父亲引用变量,无需任何类型转换,也就是向上转型

在上面的BaseClass ploymophicBc = new SubClass();中:

ploymophicBc引用变量的编译时类型是BaseClass,运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量,调用同一个方法时呈现出多种不同的行为特征,也就是多态

与方法不同,实例变量不具备多态性,比如上面的ploymophicBc引用变量,程序中输出它的book实例变量时,并不是输出SubClass中定义的实例变量,而是BaseClass里面的

instanof运算符

instanof运算符的前一个操作数通常是一个引用类型变量,后一个操作数通常是一个类(也可以是接口),它用于判断前面的对象是否是后面的类,或者其子类,实现类的实例,是就返回true

注意:

instanof运算符前面操作数的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系


public class InstanceofTest {

    public static void main(String[] args) {

        // 声明hello时使用Object类,则hello的编译类型是Object,
        // Object是所有类的父类, 但hello变量的实际类型是String
        Object hello = "Hello";
        // String与Object类存在继承关系,可以进行instanceof运算。返回true。
        System.out.println("字符串是否是Object类的实例:"
                + (hello instanceof Object));
        System.out.println("字符串是否是String类的实例:"
                + (hello instanceof String)); // 返回true。
        // Math与Object类存在继承关系,可以进行instanceof运算。返回false。
        System.out.println("字符串是否是Math类的实例:"
                + (hello instanceof Math));
        // String实现了Comparable接口,所以返回true。
        System.out.println("字符串是否是Comparable接口的实例:"
                + (hello instanceof Comparable));
        String a = "Hello";
//      // String类与Math类没有继承关系,所以下面代码编译无法通过
//      System.out.println("字符串是否是Math类的实例:"
//          + (a instanceof Math));

    }
}


最终总结起来:

多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象

使用继承的注意点

继承带来便利的同时也破坏了父类的封装性,因为子类可以直接访问父类的成员变量和方法

所以设计父类时应该注意:

  • 尽量隐藏父类内部数据,设置private访问权限

  • 除了需要被外部类调用的方法外,其余应该用private修饰,如果不想子类修改暴露的方法,可以用final修饰,如果希望某个方法被子类重写,但又不想被其他类访问,那么使用protected修饰

  • 尽量不要在父类构造函数中调用将要被子类重写的方法

class Base
{
    public Base()
    {
        test();
    }
    public void test()           // ①号test()方法
    {
        System.out.println("将被子类重写的方法");
    }
}
public class Sub extends Base
{
    private String name;
    public void test()         // ②号test()方法
    {
        System.out.println("子类重写父类的方法,"
                + "其name字符串长度" + name.length());
    }
    public static void main(String[] args)
    {
        // 下面代码会引发空指针异常
        Sub s = new Sub();
    }
}

继承与组合

先看继承:

class Animal
{
    private void beat()
    {
        System.out.println("心脏跳动...");
    }
    public void breath()
    {
        beat();
        System.out.println("吸气,吐气,呼吸中...");
    }
}
// 继承Animal,直接复用父类的breath()方法
class Bird extends Animal
{
    public void fly()
    {
        System.out.println("天空飞翔...");
    }
}
// 继承Animal,直接复用父类的breath()方法
class Wolf extends Animal
{
    public void run()
    {
        System.out.println("陆地奔跑...");
    }
}
public class InheritTest {
    public static void main(String[] args) {
        Bird b = new Bird();
        b.breath();
        b.fly();
        Wolf w = new Wolf();
        w.breath();
        w.run();
    }
}


使用组合

class Animal
{
    private void beat()
    {
        System.out.println("心脏跳动...");
    }
    public void breath()
    {
        beat();
        System.out.println("吸气,吐气,呼吸中...");
    }
}
class Bird
{
    // 将原来的父类组合到原来的子类,作为子类的一个组合成分
    private Animal a;
    public Bird(Animal a)
    {
        this.a = a;
    }
    // 重新定义一个自己的breath()方法
    public void breath()
    {
        // 直接复用Animal提供的breath()方法来实现Bird的breath()方法。
        a.breath();
    }
    public void fly()
    {
        System.out.println("天空飞翔...");
    }
}
class Wolf
{
    // 将原来的父类组合到原来的子类,作为子类的一个组合成分
    private Animal a;
    public Wolf(Animal a)
    {
        this.a = a;
    }
    // 重新定义一个自己的breath()方法
    public void breath()
    {
        // 直接复用Animal提供的breath()方法来实现Wolf的breath()方法。
        a.breath();
    }
    public void run()
    {
        System.out.println("陆地奔跑...");
    }
}
public class CompositeTest
{
    public static void main(String[] args)
    {
        // 此时需要显式创建被组合的对象
        Animal a1 = new Animal();
        Bird b = new Bird(a1);
        b.breath();
        b.fly();
        // 此时需要显式创建被组合的对象
        Animal a2 = new Animal();
        Wolf w = new Wolf(a2);
        w.breath();
        w.run();
    }
}



参考

https://blog.csdn.net/snow_7/article/details/51579278
https://blog.csdn.net/qq_29513537/article/details/60765552
https://blog.csdn.net/qq_23419401/article/details/52064871#java

相关文章

  • java多态面试题

    java多态性 多态分两种: (1) 编译时多态(设计时多态):方法重载。 (2) 运行时多态:JAVA运...

  • java多态面试题

    java多态性 多态分两种: (1) 编译时多态(设计时多态):方法重载。 (2) 运行时多态:JAVA运行时...

  • 2018-01-25

    多态机制 java语言,实现多态...

  • java多态之简述

    Java的第三大特性------>多态 一、什么是多态 多态根据其字面意思就是多种形态的意思,那么在Java中的多...

  • 学习JavaScript设计模式——面向对象(五)

    面向对象(五) 多态 我仔细看了一下,这里说的JavaScript 的多态好像和Java的多态不一样, Java ...

  • Java基础之面向对象

    1.多态,继承,封装 Java实现多态有哪些必要条件?具体怎么实现?多态的实现原理?多态的作用? 答:多态的优点 ...

  • Java 多态

    1、Java 多态分类 1)编译时多态:方法重载。2)运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决...

  • Java 多态之方法调用顺序

    本文例子取于<深入理解java多态性>和,分析 Ja...

  • 详解Java多态

    详解Java多态 多态定义 多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态...

  • Java笔记

    Java基础 Java面试通关要点 1.面向对象的特征 封装 继承 多态 封装继承多态 2.final,final...

网友评论

      本文标题:Java多态

      本文链接:https://www.haomeiwen.com/subject/cdnaactx.html