首页 > 其他 > 详细

方法覆盖和多态

时间:2021-05-30 20:23:27      阅读:24      评论:0      收藏:0      [点我收藏+]

方法覆盖和多态

方法覆盖

当子类继承父类之后,发现继承过来的方法无法满足子类的业务需求,这个时候子类有权利对继承过来的方法进行重新编写(即方法覆盖,又称为方法重写,英语单词:Override、Overwrite)。

public class OverrideTest01 {
    public static void main(String[] args) {
        //创建鸟对象
        Bird bird=new Bird();
        bird.move();
        //c创建猫对象
        Cat cat=new Cat();
        cat.move();
    }
}
?
class Animal{
    public void move(){
        System.out.println("动物正在移动!");
    }
    
    public void sing(int i){
        System.out.println("Animal sing.........");
    }
}
?
class Bird extends Animal{
    //没有进行方法重时,输出动物正在移动,我们需要的是鸟儿在飞翔,父类继承过来的方法不符合子类的业务需求,这个时候我们就需要进行方法重写了。
    //以下代码即方法重写,就是将父类中的方法拿过来,然后更改方法体中的内容,用来满足子类的业务需求。
    public void move(){
        System.out.println("鸟儿在飞翔!");
    }
    
    //父类sing(int i)和子类sing()没有构成方法覆盖,因为参数列表不同,构成了方法重载
    public void sing(){
        System.out.println("brid sing...........");
    }
?
}
class Cat extends Animal{
    //方法重写
    public void move(){
        System.out.println("猫在走猫步!");
    }
}

 

在代码级别上构成方法覆盖的条件

  1. 两个类之间必须要有继承关系

  2. 重写之后的方法和之前的方法具有:相同的返回值类型、相同的方法名、相同的参数列表

  3. 子类重写的方法访问权限不能比父类更低,但是可以比父类只能更高。

    class Animal{
        public void move{
        }
    }
    class Bird extends Animal{
        //这样是不可行的
        protected void move{   
        }
    }
    ---------------------------------------------------
    class Animal{
        protected void move{
        }
    }
    class Bird extends Animal{
        //这样是可行的
        public void move{
        }
    }

     

  4. 重写之后的方法不能比父类的方法抛出更多的异常,只能更少

覆盖方法的注意事项

  • 注意1:方法覆盖只是针对于方法,和属性无关。

  • 注意2:私有方法无法覆盖。

  • 注意3:构造方法不能被继承,所以构造方法也不能被覆盖。

  • 注意4:方法覆盖只是针对于“实例方法”,“静态方法”覆盖没有意义。

经典案例

public class OverrideTest02 {
    public static void main(String[] args) {
        ChinesePeople cp=new ChinesePeople();
        cp.setName("张三");
        cp.say();
        AmericaPeople ap=new AmericaPeople();
        ap.setName("jack");
        ap.say();
    }
}
?
class People{
    private String name;
?
    public void say(){
        System.out.println(this.getName()+"正在说话!");
    }
?
    public String getName() {
        return name;
    }
?
    public void setName(String name) {
        this.name = name;
    }
}
?
class ChinesePeople extends People{
    public void say(){
        System.out.println(this.getName()+"正在说汉语!");
    }
}
?
class AmericaPeople extends People{
    public void say(){
        System.out.println(this.getName()+" speak English!");
    }
}

 

关于Object类中的toString()方法

toString()方法的作用

将“Java对象”转换成“字符串形式”。

toString()方法的使用

当我们在调用类时候想将类的对象进行字符串转换时不满足,Object类中默认的toString()时,可在在所需类中对toString()方法进行重写。

public class OverrideTest03 {
    public static void main(String[] args) {
        Time time=new Time(1999,2,3);
        System.out.println(time.toString());
?
    }
}
?
class Time{
    //属性
    private int year;
    private int month;
    private int day;
?
    //构造方法
    public Time(){
?
    }
?
    public Time(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
?
    //setter and getter 方法
?
    public int getYear() {
        return year;
    }
?
    public void setYear(int year) {
        this.year = year;
    }
?
    public int getMonth() {
        return month;
    }
?
    public void setMonth(int month) {
        this.month = month;
    }
?
    public int getDay() {
        return day;
    }
?
    public void setDay(int day) {
        this.day = day;
    }
    //没重写前调用toString()方法,输出样式:Time@1b6d3586
    //重写toString(),想要它以这种方式输出Time{year=1999, month=2, day=3}
    @Override
    public String toString() {
        return "Time{" +"year=" + year +", month=" + month +", day=" + day +‘}‘;
    }
}

多态

/*
    为了方便所以写在一个类,一般最好分开写
 */
public class AnimalTest {
    public static void main(String[] args) {
        Animal a=new Animal();
        Cat c=new Cat();
        Bird b=new Bird();
?
        //父类型的引用可以指向子类型的对象
        Animal aCat=new Cat();
        aCat.move();//猫在走猫步!
        Animal aBird=new Bird();
        aBird.move();//鸟儿在飞翔!
    }
}
class Animal {
    public void move(){
        System.out.println("动物正在移动!");
    }
}
?
class Bird extends Animal {
    public void move(){
        System.out.println("鸟儿在飞翔!");
    }
}
?
class Cat extends Animal {
    public void move(){
        System.out.println("猫在走猫步!");
    }
    //猫除了move之外,应该有自己的特定行为,比如抓老鼠。
    //子类中特有的方法
    public void catchMouse(){
        System.out.println("猫正在抓老鼠!");
    }
}

学习多态语法之前普及两个概念

向上转型(upcasting)

子------->父,

Animal acat=new Cat();
Animal abird=new Bird();

向下转型(downcasting)

父------->子

Cat cat=(Cat)aCat;

注意

Java中允许向上转型也允许向下转型。 !!!!!不过无论是向上转型还是向下转型两种之间都必须有继承关系,没有继承关系编译器报错。

什么是多态

  多种形态,多种状态。

  分析:aCat.move() ; java程序分为编译阶段和运行阶段。 先来分析编译阶段: 对于编译器来说,编译器只知道a2的类型是Animal,所以编译器在检查语法的时候,会去Animal.class字节码文件中找move()方法,找到了,绑定上move()方法,编译通过,静态绑定成功。(编译阶段属于静态绑定。)再来分析运行阶段: 运行阶段的时候,实际上在堆内存中创建的java对象是Cat对象,所以move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行cat对象的move ()方法。这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定。)

多态表示多种形态: 编译的时候一种形态。运行的时候另一种形态。

多态的基础语法

  1. 多态指的是

    父类型的引用可以指向子类型的对象

    包括编译阶段和运行阶段。

    编译阶段:静态绑定父类的方法;

    运行阶段:动态绑定子类型对象的方法。

    多种形态

    Animal aCat=new Cat();
    /*
        分析下面这段代码可以编译和运行吗
        分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定。
        只有编译阶段通过了才能运行
        答:不能编译通过,因为在编译阶段编译器只知道aCat的类型是Animal,去Animla.class文件中去赵catMouse()方法,
        结果找不多,静态绑定失败,编译进行了报错,无法运行。
    */
    aCat.catchMouse();
  1. 什么时候必须“向下转型”

    当你需要访问的是子类对象中“特有”的方法,这时候需要“向下转型”。

    //假设这个时候我就想要调用catchMouse()方法,,那么这个时候就需要用到“向下转型”了 (强制类型转换)
    Cat cat=(Cat)aCat;
    cat.catchMouse();//猫正在抓老鼠!
    ?
    Bird bird=(Bird)aBird;

    向下转型会有风险

    //分析以下程序是否会报错,编译报错还是运行报错?
    ?
    Cat cat1=(Cat)aBird;
    cat1.catchMouse();
    ?
    /*
        编译器检测到cat1这个引用是Animal类型,而Animal和Cat之间具有继承关系,所以可以向下转型,编译不报错。
        当进入运行的时候,堆内存中创建的是Bird对象,在实际下运行时,将Brid对象转换成Cat对象是不可行的,因为Bird与Cat
        之间没有继承关系,此时运行会报错。
        运行时出现异常,这个异常和空指针异常一样重要,也非常经典。
        java.lang.ClassCastException:类型转换异常。
        com.liu.duotai.Bird cannot be cast to com.liu.duotai.Cat(Bird不能转换成Cat)
    */

instanceof运算符(避免ClassCastException异常)

/*
    怎么避免ClassCastException异常的发生?
    新的内容,运算符:
        instanceof
    1.instanceof可以在运行阶段动态判断引用指向的对象和类型。
    2.instanceof的语法:
        (引用 instanceof 类型)
    3.instanceof运算符的运算结果只能时True或False
    4.c是一个引用,c变量保存了一个内存地址指向了堆中的对象
        假设(c instanceof Cat)为True表示:
            c引用指向的堆内存中的Java对象是一个Cat
        假设(c instanceof Cat)为False表示:
            c引用指向的堆内存中的Java对象不是一个Cat
        
*/
if (aBird instanceof Cat){//如果aBird是一个Cat,再进行强制类型转换
            Cat cat1=(Cat)aBird;
            cat1.catchMouse();
        }

!!!程序员要养成一个好习惯: 任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof运算符进行判断。(Java规范中要求的。)

public class AnimalTest02 {
    /*
        当我们的肉眼可以看到底层到底是new Cat()还是 new Brid()时候
        为什么还要使用instanceof进行判断呢?
        原因是有时候我们肉眼是看不到的
    */
    public static void main(String[] args) {
        Animal a1=new Cat();
        Animal a2=new Bird();
        if (a1 instanceof Cat){
            Cat c=(Cat) a1;
            c.catchMouse();
        }else if (a1 instanceof Bird){
            Bird b=(Bird) a1;
            b.sing();
        }
        if (a2 instanceof Cat){
            Cat c=(Cat) a2;
            c.catchMouse();
        }else if (a2 instanceof Bird){
            Bird b=(Bird) a2;
            b.sing();
        }
    }
    
}

多态在开发中的作用(非常重要)

软件在扩展过程中修改的越少越好,修改的越多,你的系统稳定性越差,未知风险越多。这里就涉及到一个软件的开发原则: 软件开发原则有七大原则(不属于Java,这个开发原则属于整个软件业):其中有一条最基本的原则:ocp(开闭原则)。 什么是开闭原则? 对扩展开放(你可以额外增加),对修改关闭(最好很少修改现有程序)。在软件的扩展过程中,修改越少越好。

项目的开发不仅为了实现客户的需求,同时也需要考虑软件的扩展性。在编程的时候最好面向父类型编程面向更加抽象进行编程,不建议面向具体进行编程。因为面向具体编程会让软件扩展性很差。

测试类

/*
    编写程序模拟…主人喂养宠物"的场景:提示1:R
    主人类:Master宠物类:Pet
    宠物类子类:Dog、cat、 Yingwu提示2:
    主人应该有喂养的方法:feed ()宠物应该有吃的方法:eat()
    只要主人喂宠物,裾物就吃。
    要求:主人类中只提供一个喂养方法feed(),要求达到可以喂养各种类型的宠物。
    编写测试程序:
    创建主人对象
    创建各-种龙物对象
    调用主人的喂养方法feed(),喂养不同的宠物,观察执行结果。
    通过该案例,理解多态在开发中的作用。
    重要提示:feed方法是否需要一个参数,参数选什么类型!! !
?
 */
public class Test {
    public static void main(String[] args) {
        Master master=new Master();
        master.feed(new Dog());
        master.feed(new Cat());
        master.feed(new YingWu());
?
    }
}

主人类

public class Master {
    /*
        假设主人起初的时候只是喜欢养宠物狗狗1/喂养宠物狗狗
    public void feed (Dog d){
        d.eat() ;
    }
        由于新的需求产生,导致我们"不得不"去修改Master这个类的代码
    public void feed (cat ){
        c.eat() ;
    }
?
        能不能让Master主人这个类以后不再修改了。
        即使主人又喜欢养其它宠物了,Master也不需要修改。
        这个时候就需要使用:多态机制。
        最好不要写具体的宠物类型,这样会影响程序的扩展性
    */
    //这个时候就可以把feed()方法中的参数列表换成Pet这个父类型,
        //喂养的方法
    public void feed(Pet pet){
?
        pet.eat();
    }
}

宠物类

public class Pet {
    public void eat(){
        //这个方法可以不用给具体实现
        System.out.println("宠物正在吃东西");
    }
?
}

狗类

public class Dog extends Pet{
    public void eat(){
        System.out.println("狗正在吃狗粮");
    }
}

猫类

public class Cat extends Pet{
    public void eat(){
        System.out.println("猫正在吃猫粮");
    }
}

鹦鹉类

public class YingWu extends Pet{
    public void eat(){
        System.out.println("鹦鹉正在吃豆子");
    }
}

多态在开发中的作用

降低程序的耦合度,提高程序的扩展力。 ? public class Master{ ? public void feed (Dog d){} ? public void feed (Cat c ){ } ? 以上的代码中表示:Master和Dog以及cat的关系很紧密(耦合度高)。导致扩展力很差。

public class Master{ ? public void feed ( Pet pet) { }} ? 以上的代表中表示:Master和Dog以及cat的关系就脱离了,Master关注的是Pet类这样Master和Dog以及cat的耦合度就降低了,提高 了软件的扩展性。

面向对象的三大特征: 封装、继承、多态真的是一环扣一坏。 有了封装,有了这种整体的概念之后。对象和对象之间产生了继承。 有了继承之后,才有了方法的覆盖和多态。

七大原则最基本原则

OCP(对扩展开放,对修改关闭),目的是:降低程序耦合度,提高程序扩展力。 ? 面向抽像编程,不建议面向具体编程

私有方法无法覆盖

//经过测试,你记住就行。
// 私有方法不能覆盖。
public class OverrideTest05{
?
    //私有方法,只能在本类中访问,不能在别的类中访问。
    private void dosome () {
        System.out.println ( "OverrideTest06‘s private method dosome execute! ");
    }
    //入口
    public static void main (String [] args){
        //多态
        OverrideTest05 ot = new T();
        ot.dosome( ); //overrideTest06‘s private method dosome execute!
    }
//子类
?
}
class T extends OverrideTest05 {
    //尝试重写父类中的dosome ()方法
    // 访问权限不能更低,可以更高。
    public void dosome() {
        System.out.println("T‘s public dosome method execute! ");
    }
}

方法覆盖只是针对于“实例方法”,“静态方法”覆盖没有意义。

/*
    方法覆盖只是针对于“实例方法”,“静态方法”覆盖没有意义。
    1、方法覆盖需要和多态机制联合起来使用才有意义。
        Animal a =new Cat() ;
        a . move() ;
        要的是什么效果?
        编译的时候move()方法是Animal的。
        运行的时候自动调用到子类重写move()方法上。
        假设没有多态机制,只有方法覆盖机制,你觉得有意义吗?
        没有多态机制的话,方法覆盖可有可无。
        没有多态机制,方法覆盖也可以没有,如果父类的方法无法满足子类业务需求的时候,子类完全可以定义一个全新的方法。
    2、静态方法存在方法覆盖吗?
        多态自然就和对象有关系了.而静态方法的执行不需要对象。
        所以,一般情况下,我们会说静态方法"不存在"方法覆盖。
?
?
 */
public class OverrideTest04 {
    public static void main(String[] args) {
        //静态方法可以使用“引用.”来调用吗?
        //答:可以的,虽然使用“引用.”来调用,但和对象是没有关系的。
        Animals a=new Cats();//多态
        //静态对象和多态无关
        //实际运行还是Animals.doSome()
        a.dosome();//Animals 的dosome折行了
?
    }
}
?
class Animals{
    //父类中的静态方法
    public static void dosome(){
        System.out.println("Animals 的dosome折行了");
    }
}
?
class Cats extends Animals{
    //尝试在子类中对父类的静态方法进行重写
    public static void dosome() {
        System.out.println("Cat 执行了");
    }
}

 

方法覆盖和多态

原文:https://www.cnblogs.com/shiwangmmp/p/14828231.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!