[Java][华为云Java编程创造营][学习笔记][第二阶段][03_Java面向对象之多态]

举报
John2021 发表于 2021/11/20 21:18:59 2021/11/20
【摘要】 1,方法重写 1.1,子类和父类同名方法 1.1.1,子类和父类同名方法子类和父类同名方法,方法重写前提:需要有继承关系方法重写表现:(方法名必须相同;参数列表必须相同;返回值类型必须相同;修饰符:范围可以扩大或相同,但是不能缩小public>protected>default) 1.1.2,方法重写class Animal{ public void eat() { ...

1,方法重写

1.1,子类和父类同名方法

1.1.1,子类和父类同名方法

  • 子类和父类同名方法,方法重写
  • 前提:需要有继承关系
  • 方法重写表现:(方法名必须相同;参数列表必须相同;返回值类型必须相同;修饰符:范围可以扩大或相同,但是不能缩小public>protected>default)

1.1.2,方法重写

class Animal
{
    public void eat()
    {
        System.out.println("动物在吃东西");
    }
}

class Cat extends Animal
{
    @Override
    public void eat()
    {
        System.out.println("猫吃鱼");
    }
}

class Dog extends Animal
{
    @Override
    public void eat()
    {
        System.out.println("狗吃肉");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        /*
         * 输出结果
         * 动物在吃东西
           猫吃鱼
           狗吃肉
         * */
        Animal animal = new Animal();
        animal.eat(); //输出动物类eat()方法
        Animal cat = new Cat();
        cat.eat(); //输出猫类eat()方法
        Animal dog = new Dog();
        dog.eat(); //输出狗类eat()方法
    }
}
  • 不能重写父类的private方法,如果定义的话只是定义了一个新方法,不是方法重写
class Animal
{
    private void talk()
    {
        System.out.println("动物在说");
    }
}

class Cat extends Animal
{
    void talk()
    {
        System.out.println("猫在喵喵叫");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Animal animal = new Animal();
        //方法不可见
        //animal.talk(); //报错:'talk()' has private access in 'com.huawei.demo2.Animal'

        Cat cat = new Cat();
        //父类指向子类,向上转型
        Animal animal1 = cat;

        //方法不可见,说明talk()方法没有被子类重写
        //animal1.talk(); //报错:'talk()' has private access in 'com.huawei.demo2.Animal'

        cat.talk(); //猫在喵喵叫`
    }
}

1.1.3,运行时多态

后期绑定

  • 如果被调用的方法在编译期无法被确定下来,
  • 只能够在程序运行期间根据实际的类型绑定相关的方法,这种绑定方法也被称为后期绑定。

运行时多态

  • 方法重写是根据实际的类型决定调用哪个重写的方法,发生在运行期间,也叫做运行时多态。
class Animal
{
    public void eat()
    {
        System.out.println("动物吃东西");
    }
}

class Cat extends Animal
{
    @Override
    public void eat()
    {
        System.out.println("猫吃鱼");
    }
}

class Dog extends Animal
{
    @Override
    public void eat()
    {
        System.out.println("狗吃肉");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Animal animal = new Cat();
        animal.eat(); //猫吃鱼
    }
}

1.2,static修饰同名方法

  • 子类和父类static修饰的同名方法
  • static修饰的方法是静态方法,也叫做类方法
  • 使用private或static或final修饰的变量或方法,是早期绑定
class Animal
{
    static void eat()
    {
        System.out.println("动物吃东西");
    }
}

class Cat extends Animal
{
    static void eat()
    {
        System.out.println("猫吃鱼");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Animal animal = new Cat();
        //animal.eat(); //报错:Static member 'com.huawei.demo4.Animal.eat()' accessed via instance reference
    }
}

1.3,解耦合

1.3.1,动态绑定和解耦合简介

动态绑定

  • 在运动时根据具体对象的类型进行绑定,也就是后期绑定。

解耦合简介

  • 解耦合,字面意思就是解除耦合关系。

设计的核心思想:

  • 尽可能减少代码耦合,如果发现代码耦合,就要采取解耦技术。
  • 数据模型,业务逻辑和视图显示三层之间彼此降低耦合。

1.4,同名变量

class Father
{
    int a = 1;
    static int b = 1;

    Father()
    {
        a = 10;
        b = 10;
    }
}

public class Son extends Father
{
    int a = 2;
    static int b = 2;

    Son()
    {
        a = 20;
        b = 20;
    }

    public static void main(String[] args)
    {
        Son son = new Son();
        Father father = son;
        System.out.println("father.a=" + father.a + ",father.b=" + father.b);  //father.a=10,father.b=10
        System.out.println("son.a" + son.a + ",son.b=" + son.b);  //son.a20,son.b=20
    }
}

1.5,方法重载和方法重写区别

  • 多态的具体表现
名称 方法重载 方法重写
一个类 继承关系
方法名 相同 相同
方法参数 参数个数不同、参数类型不同、参数顺序不同 参数列表相同
返回值类型 可以不同 必须相同
调用方式 参数决定 创建的实际对象决定
static修饰 是方法重载 不是方法重写

2,抽象类

2.1,抽象类的定义

为什么需要抽象类?

  • 动物Animal都有自己的行为,小鸟和老虎继承了动物的行为,但小鸟和老虎的行动方式不一样。在动物类中能给出行动的具体实现吗?

什么是抽象类?

  • 使用abstract关键字修饰的方法叫做抽象方法,抽象方法没有方法体。当一个类中包含了抽象方法,那么该类也必须使用abstract关键字来修饰,这种使用abstract关键字修饰的类就是抽象类。
  • 抽象类及抽象方法定义的语法格式
/**
 * [修饰符] abstract class 类名
 * {
 *     //定义抽象方法
 *     [修饰符] abstract 方法返回值类型 方法名([参数列表]);
 *     //其他方法或属性
 * }
 * */

2.2,抽象类的作用

  • 抽象类的作用类似于"模板",其目的是方便开发人员根据抽象类的格式来修改和创建新类。
  • 抽象类主要用于继承,有利于程序的扩展。
abstract class Book
{
    abstract String getAuthor();
}

class ComputerBook extends Book
{
    String getAuthor()
    {
        return "Java之父詹姆斯高斯林 James Gosling";
    }
}

class EnglishBook extends Book
{
    String getAuthor()
    {
        return "Tom";
    }
}

抽象类的特点:

  • 1,抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非抽象子类的对象。
  • 2,抽象类中,可以有构造器,是供子类创建对象时,初始化父类成员使用的。
  • 3,抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 4,抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,不然编译无法通过而报错。
  • 5,抽象类中的抽象方法不能用private,final,static修饰。
  • 6,抽象类存在的意义是为了被子类继承,抽象类体现的是模板思想。

2.3,抽象类设计和场景

  • 我们现在使用抽象类设计一个模板模式的应用
  • 场景:
  • 新司机:开门,点火,双手紧握方向盘,刹车,熄火
  • 老司机:开门,点火,右手握方向盘左手抽烟,刹车,熄火
abstract class Driver
{
    void go()
    {
        System.out.println("开门");
        System.out.println("点火");
        //将开车姿势设置为抽象类以提供模板
        posture();
        System.out.println("刹车");
        System.out.println("熄火");
    }

    abstract void posture();
}

class NewDriver extends Driver
{
    @Override
    void posture()
    {
        System.out.println("新司机双手紧握方向盘");
    }
}

class OldDriver extends Driver
{
    @Override
    void posture()
    {
        System.out.println("老司机右手握方向盘左手点烟");
    }
}

public class DriverTest
{
    public static void main(String[] args)
    {
        NewDriver newDriver = new NewDriver();
        newDriver.go();
        /*开门
          点火
          新司机双手紧握方向盘
          刹车
          熄火
        */
        OldDriver oldDriver = new OldDriver();
        oldDriver.go();
        /*开门
          点火
          老司机右手握方向盘左手点烟
          刹车
          熄火
        */
    }
}

3,接口

3.1,接口的定义

  • 接口就是某个事物对外提供的一些功能的声明。
  • 可以利用接口实现多态,同时接口也弥补了Java单一继承的弱点
  • 使用interface关键字定义接口

3.2,接口的特点和作用

JDK1.8之前接口的特征

  • 接口允许多继承。
  • 接口没有构造方法。
  • 接口中的属性默认是用public static final修饰的。
  • 接口中的方法默认使用public abstract修饰的。
  • 接口继承接口用extends,不能implement。
public interface MyInterface
{
    //设计接口和设计类有什么区别
    //属性
    public static final String STR = "my interface"; //接口中的属性默认就是final

    //接口中的属性一般设置为 public static final 修饰的常量
    //方法
    //接口中不能有构造方法
    //MyInterface(){} //报错:Interface abstract methods cannot have body
    public void test();

    public void test2();
    //在JDK1.8之前接口中的方法都是抽象方法
}

JDK1.8之后接口的语法和特征

  • 在接口内部可以定义多个常量和抽象方法,定义常量时必须进行初始化赋值,定义默认方法和静态方法时,可以有方法体。
  • 在接口中定义常量时,可以省略"public static final"修饰符,接口会默认为常量添加"public static final"修饰符。与此类似,在接口中定义抽象方法时,也可以省略"public abstract"
    修饰符,定义default默认方法和static静态方法时,可以省略"public"修饰符,这些修饰符系统都会默认进行添加。
/**
 * [修饰符] interface 接口名 [extends 父接口1,父接口2,...]
 * {
 *     [public] [static] [final] 常量类型 常量名 = 常量值;
 *     [public] [abstract] 方法返回值类型 方法名([参数列表]);
 *     [public] default 方法返回值类型 方法名([参数列表])
 *     {
 *         //默认方法的方法体
 *     }
 *     [public] static 方法返回值类型 方法名([参数列表])
 *     {
 *         //类方法的方法体
 *     }
 * }
 * */

public interface MyInterface
{
    //JDK1.8之后的新特征
    //接口中可以定义默认方法(可以有多个),有方法体
    default String showStr()
    {
        System.out.println("MyInterface 中的默认方法:showStr");
        return STR;
    }

    default String showStr2()
    {
        System.out.println("MyInterface 中的默认方法:showStr2");
        return STR;
    }

    //接口中可以定义静态方法,有方法体
    static void showInfo()
    {
        System.out.println("MyInterface 中的静态方法:showInfo");
    }

    static void showInfo2()
    {
        System.out.println("MyInterface 中的静态方法:showInfo2");
    }
}

接口的作用

  • 接口代表一种能力,例如:“做这项工作需要一个程序员”。(程序员是一种能力,不关心具体是谁)
  • 接口是一种能力(体现在接口的方法上)。
  • 面向接口编程(关心实现类有何能力,而不关心实现细节;面向接口的约定而不考虑接口的具体实现)

小结

  • 1,从JDK8开始,接口中的方法除了包含抽象方法外,还包含默认方法和静态方法,默认方法和静态方法都可以有方法体,并且静态方法可以通过"接口.方法名"来调用。
  • 2,当一个类实现接口时,如果这个类是抽象类,只需实现接口中的部分抽象方法即可,否则需要实现接口中的所有抽象方法。
  • 3,一个类可以通过implements关键字同时实现多个接口,被实现的多个接口之间要用英文逗号隔开。
  • 4,接口之间可以通过extends关键字实现继承,并且一个接口可以同时继承多个接口,接口之间用英文逗号隔开。
  • 5,一个类在继承一个类的同时还可以实现接口,此时,extends关键字必须位于implements关键字之前。

3.3,接口的设计和使用场景

  • 面向接口编程例子
  • 需求:开发打印机
  • 墨盒:黑白,彩色
  • 纸张类型:A4,B5
  • 墨盒和纸张都不是打印机厂商提供的
  • 打印机厂商要兼容市场上的墨盒和纸张
  • 输出结果:使用黑白墨盒在A4纸打印;使用彩色墨盒在B5纸打印;使用彩色墨盒在A4纸打印
//墨盒
public interface ColorBox
{
    void useColor();
}

public class BlackWhileBox implements ColorBox
{
    @Override
    public void useColor()
    {
        System.out.print("使用黑白墨盒");
    }
}

public class ColorfulBox implements ColorBox
{
    @Override
    public void useColor()
    {
        System.out.print("使用彩色墨盒");
    }
}

//纸张
public interface PaperSize
{
    void usePaperSize();
}

public class PaperA4 implements PaperSize
{
    @Override
    public void usePaperSize()
    {
        System.out.print("在A4纸上打印");
    }
}

public class PaperB5 implements PaperSize
{
    @Override
    public void usePaperSize()
    {
        System.out.print("在B5纸上打印");
    }
}


//主方法
public class TestPrinter
{
    public static void main(String[] args)
    {
        BlackWhileBox blackWhileBox = new BlackWhileBox();
        ColorfulBox colorfulBox = new ColorfulBox();
        PaperA4 paperA4 = new PaperA4();
        PaperB5 paperB5 = new PaperB5();

        blackWhileBox.useColor();
        paperA4.usePaperSize();
        System.out.println();  // 使用黑白墨盒在A4纸上打印
        colorfulBox.useColor();
        paperB5.usePaperSize();
        System.out.println(); // 使用彩色墨盒在B5纸上打印
        colorfulBox.useColor();
        paperA4.usePaperSize();
        System.out.println();  // 使用彩色墨盒在A4纸上打印
    }
}

4,接口和抽象类的区别

4.1,抽象类is-a

抽象类的特点

  • 1,抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非抽象子类的对象。
  • 2,抽象类中,可以有构造器,是供子类创建对象时,初始化父类成员使用的。
  • 3,抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 4,抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。
  • 5,抽象类中的抽象方法不能用private,final,static修饰。
  • 6,抽象类存在的意义是为了被子类继承,抽象类体现的是模板思想。

何时使用抽象类?

  • 继承与真实世界类似,只要说"猫是哺乳动物",猫的很多属性和行为就不言自明了。
  • 符合 is-a 关系的设计使用继承
  • 继承是代码重用的一种方式,将子类共有的属性和行为放到父类中,子类与父类是is-a关系。

4.2,接口has-a

  • 下面以一个防盗门的例子来说明 is-a 与 has-a。
//Door
public abstract class Door
{
    public abstract void open();  //开门

    public abstract void close();  //关门
}

//Lock
public interface Lock
{
    void lockUp();  //锁

    void openLock();  //开锁
}

//TheftProofDoor
public class TheftProofDoor extends Door implements Lock
{

    //is-a:防盗门是门
    //has-a:防盗门有开锁和关锁的功能
    @Override
    public void open()
    {
        System.out.println("门打开了");
    }

    @Override
    public void close()
    {
        System.out.println("门关上了");
    }

    @Override
    public void lockUp()
    {
        System.out.println("把门锁上了");
    }

    @Override
    public void openLock()
    {
        System.out.println("把门锁打开了");
    }
}

//DoorTest
/*
 * 实现防盗门功能(防盗门是一个门)
 * 防盗门有一个锁(上锁,开锁)
 * is-a的关系
 * has-a的关系
 *
 * */
public class DoorTest
{
    public static void main(String[] args)
    {
        //创建具体防盗门对象
        TheftProofDoor theftProofDoor = new TheftProofDoor();
        theftProofDoor.close();//关门
        theftProofDoor.lockUp();//锁门
        theftProofDoor.openLock();//开锁
        theftProofDoor.open();//开门
        /*
         * 输出结果:
         * 门关上了
         * 把门锁上了
         * 把门锁打开了
         * 门打开了
         * */
    }
}

4.3,猜数字游戏实战

import java.util.Scanner;

/*
 * 需求:编写程序随机生成一个1-10之间的随机数。程序提示用户输入一个数字,不停猜测,
 * 直到猜对为止,然后输出猜测的数字,和猜测的次数。
 * 并且如果没有猜中要提示用户输入的值大了还是小了。
 * */
public class GuessNumberDemo
{
    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个正数,范围1-10之间:");
        int guessNum = (int) (Math.random() * 10 + 1);//[0.0,1.0)

        int i = 0;
        while (true) //不知道要猜多少次,要死循环,满足条件结束循环
        {
            System.out.print("猜第" + ++i + "次:");
            int result = scanner.nextInt(); //接收用户每次从键盘输入的正数
            if (result < guessNum)
            {
                System.out.println("小了");
            } else if (result > guessNum)
            {
                System.out.println("大了");
            } else
            {
                System.out.println("猜对了");
                break;
            }
        }
    }
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。