[Java][华为云Java编程创造营][学习笔记][第二阶段][03_Java面向对象之多态]
【摘要】 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)