JavaSE—多态的定义及其应用

前情提要:

        书接上回,上篇博客详细介绍了继承,了解到了继承在Java中的作用以及如何去具体应用到实际情景。本篇博客将对多态展开详细介绍。


何为多态:

        概念:同一个行为,在创建不同对象去进行的时候会产生不同的效果。

例如:

        打印机有黑白和彩色两种,都能进行打印这个操作,但是执行操作之后的效果却是不同的

        

多态的实现条件:

        1、必须在继承体系下

        2、子类必须重写父类中的方法进行重写

        3、通过父类的引用调用重写的方法 

 多态的体现:在代码运行的时候,不同类对象会调用对应类的方法

多态的优点和缺点:

        优点:1、降低了代码的复杂度;2、代码扩展性强

        缺点:代码的效率比较低(父类引用不能直接访问子类特有的成员变量和构造方法)

认识重写:

        概念:子类的方法保留方法名字、返回值类型、返回值、方法形参与父类一致,然后方法中的核心代码进行重新编写

重写的规则:

        1、重写的方法在形参、名字、返回值类型上必须与父类中的方法一致

        2、子类中重写的方法其访问权限不能比父类的方法低(例如父类中方法的修饰符是public,那么子类重写方法的时候就不能写成protected)

        3、父类中被private、static修饰的方法不能被重写

        4、重写的方法可以用@Override显示注释,可以帮助检测代码正确性

在IDEA中可以用快捷键alt+insert进行方法重写:

1、先按下alt+insert,点击Override Methods

选择要重写的类,然后点击ok

然后再对方法核心代码进行重写

代码实例:

创建一个父类people,在父类中创建构造方法

public class People {
    String name ;
    int age;
    public People(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public void sleep()
    {
        System.out.println(name+"正在呼呼大睡....");
    }
    public void eat()
    {
        System.out.println(name+"正在吃大餐....");
    }
}

接着创建两个子类进行继承,然后在子类中对父类的构造方法进行重写

public class Dad extends People{

    public Dad(String name ,int age)
    {
        super(name,age);
    }

    @Override
    public void sleep() {
        super.sleep();
    }
}
public class Son extends People {
    public Son(String name,int age)
    {
        super(name, age);
    }

    @Override
    public void sleep() {
        super.sleep();
    }

}

对子类进行实例化测试

public class Test {
    public static void main(String[] args) {
        Son s1 = new Son("大头儿子",5);
        Dad d1 =  new Dad("小头爸爸",40);
        s1.sleep();
        d1.sleep();
    }
}

 结果如下:


区别重写和重载:

二者区别一图解:

        方法重载的概念以及用法详情移步博客:http://t.csdnimg.cn/P0ZOR

        此处不做过多赘述。


 转型:

        重头戏!!!!这是多态最为重要的内容,也是实用性最强的!!

向上转型:

概念:创建一个子类对象,把该子类对象当做一个父类对象使用。

格式:父类 对象名 = new 子类();

优点:让代码实现更加灵活

缺点:不能调用子类中特有的方法和成员变量

代码实例:

父类:

public class People {
    String name ;
    int age;
    public People(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    public void sleep()
    {
        System.out.println(name+"正在呼呼大睡....");
    }
    public void eat()
    {
        System.out.println(name+"正在吃大餐....");
    }
}

子类:

public class Son extends People {
    double weight;                                    //weight为子类中特有的成员变量
    public Son(String name,int age)
    {
        super(name, age);
    }

    @Override
    public void sleep() {
        super.sleep();
    }
    public void play()                                //play()方法为子类中特有的方法
    {
        System.out.println(name+"正在玩游戏....");
    }
}

 实例化对象

public class Test {
    public static void main(String[] args) {
        People p1 = new Son("张三",20);
        p1.sleep();
    }
}

当我们在实例化对象之后,发现无法通过访问子类中特有的方法和成员变量 


向下转型:

 概念:将父类引用转化为子类对象就是向下转型。

        在创建子类实例化对象的时候,因为该对象是一个父类引用对象此时的实例化对象是无法访问子类中特有的成员变量和构造方法的。

        为了能够访问子类中特有的成员变量和构造方法,此时就需要将实例化对象进行向下转型。

下面用代码实例直观感受向下转型:

先写一个父类Aniaml

public class Animal {
    String name ;
    String color;
    public Animal(String name,String color)
    {
        this.color = color;
        this.name = name;
        System.out.println(name+"的颜色是"+color);
    }
        public void eat()
        {
            System.out.println(name + "正在吃饭....");
        }
}

再写一个子类Dog继承自父类Animal,在子类中写一个子类特有的构造方法

public class Dog extends Animal{
    public Dog(String name ,String color)
    {
        super(name,color);
    }
    public void bark ()                            //子类中特有的方法
    {
        System.out.println(name+"正在叫....");
    }

}

再实例化一个父类引用的子类实例化对象:

public class Test {
    public static void main(String[] args) {
        Animal dog  = new Dog("哈士奇","黑白");  //此处为向上转型
    }
}

然后通过实例化对象访问子类Dog中特有的构造方法,发现子类特有的构造方法bark()访问不了

进行向下转型,再次尝试访问子类中特有的构造方法bark(),这次发现可以访问了

 注意:子类实例化对象进行父类引用之后,想要进行向下转型最好是转型为原有的子类

例如:

        上述例子的子类是Dog,实例化对象的父类引用是Animal,那么在进行转型的时候最好是将实例化对象转化为Dog,不要转化为其他的子类,否则会产生编译报错

用代码来感受这一点:

        在上一个例子中再写一个Cat类继承父类Animal

public class Cat extends Animal{
    public Cat(String name ,String color)
    {
        super(name,color);
    }
    public void play()                          //子类Cat中特有的构造方法
    {
        System.out.println(name+"正在玩耍....");
    }
}

然后再将上一个例子的实例化对象照搬过来,对其进行向下转型,此时转型的子类Cat

public class Test {
    public static void main(String[] args) {
        Animal dog  = new Dog("哈士奇","黑白");  //此处为向上转型
        dog = (Cat)dog;
        
    }
}

然后我们通过对象去访问对象中的构造方法,发现确实可以访问,但是编译器在编译的时候却抛出了异常

         之所以抛出异常,是因为进行向上转型的子类与进行向下转型子类不同,代码不安全所以抛出了异常(本来是只狗,你向上转型成一个动物,然后进行向下转型之后又变成了一只猫,编译器肯定不干)


避免在实例化对象中调用重写的构造方法:

依旧是通过代码来直观感受:

先写一个父类A,重写父类的成员方法并且在成员方法中调用父类中的构造方法

public class A {
    public A()
    {
        func();
    }
    public void func()
    {
        System.out.println("类A中的构造方法func()");
    }
}

再写一个子类B,重写父类中的构造方法func(); 定义一个访问权限为private的成员变量num并且进行初始化,然后在子类的成员方法中调用已经重写过的func()

public class B extends A{
    private int num = 10;
    @Override
    public void func() {
        System.out.println("类B中重写过的方法func():"+num);
    }
}

进行实例化引用

public class Test {
    public static void main(String[] args) {
            B b = new B();
    }
}

附上运行结果

通过上例,我们可以分析:

1、构造对象b的时候会调用类A的构造方法

2、在实例化b的过程中,A中的成员方法调用的是在B中重写过的构造方法 

3、B在进行实例化过程中,成员变量num处于未初始化的状态,如果成员变量num具有多态性,那么在B中重写构造方法的num就是10

总结:避免在实例化对象中调用重写过的构造方法,很容易出错


本篇博客到此就结束啦!看完请不要吝啬您的三连哦!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值