多态
如何构成多态?
多态存在的必要条件:
1.有继承关系
2.有方法重写
3.父类引用指向子类对象
多态特点:
1.成员变量: 编译时期观察左边类型,运行时期看左边类型
2.静态方法:编译时期观察左边类型,运行时期看左边类型
3.构造方法:帮助子类初始化父类继承的成员
4.成员方法: 编译时期观察左边类型,运行时期看右边类型(运行时指向的是子类的成员方法)
5.简化了代码,提高了维护下和扩展性
public class PolymorphismDemo01 {
public static void main(String[] args) {
// 3.父类引用指向子类对象 (注:该种引用看不到子类自己独立拥有的成员方法,若要使用需要强制转换)
CutterMan c = new Hairdresser();
// 使用多态
c.cut();//此时无法输出子类特有的成员方法“唱歌”
c = new Doctor();
c.cut();
c = new Director();
c.cut();//此时无法输出子类特有的成员方法“拍电影”
//4.强转转换:格式:<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
Hairdresser h =(Hairdresser) c;
h.sing();//输出:唱歌
Director d = (Director) c;
d.makeMovie();//输出:拍电影
}
}
class CutterMan {
public void cut() {
System.out.println("爱怎么cut怎么cut");
}
}
class Hairdresser extends CutterMan {
public void cut() {
System.out.println("剪头发");
}
public void sing() {
System.out.println("唱歌");
}
}
class Doctor extends CutterMan {
@Override
public void cut() {
System.out.println("动手术");
}
}
class Director extends CutterMan {
@Override
public void cut() {
System.out.println("暂停");
}
public void makeMovie(){
System.out.println("拍电影");
}
}
多态的缺点:通过父类引用无法访问子类所特有的方法。
若想要访问子类特有的方法,则需要通过向下转型来强制转换。
转型格式:<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
在向下转型的过程中可能会遇到如下异常情况:
java.lang.ClassCastException
异常名称: 类型转换异常
产生原因: 在向下转型的过程中没有转换成真实的类型
解决类型转换异常的办法: 在向下转型的过程中对父类的所有子类做类型检查
使用 instanceof 关键字检查类型;
格式: 左边对象 instanceof 右边类: 判断左边对象是否属于右边类的实例,返回boolean类型
例题::
实现主人与宠物玩耍功能 play
和狗狗玩接飞盘游戏。
和企鹅玩游泳游戏。
编写测试类测试
分析:
给Dog添加接飞盘方法catchingFlyDisc( )
给Penguin添加游泳方法swimming( )
给主人添加play(Pet pet)方法
思考:如果还需要再主人类中添加赠送宠物的方法呢?
public class Dedo01 {
public static void main(String[] args) {
// Hoster h = new Hoster();
Hoster h = new Hoster("小明", 3, 3);
h.show();
h.sendOut("dog", 3);
h.playWithPet(new Dog());
}
}
class Hoster {
private String name;
private int dogCount;
private int penguinCount;
public Hoster() {
}
public Hoster(String name, int dogCount, int penguinCount) {
super();
this.name = name;
this.dogCount = dogCount;
this.penguinCount = penguinCount;
}
public void show() {
System.out.println(name + "有狗 " + dogCount + "只,有企鹅" + penguinCount + "只");
}
public void sendOut(String petName, int num) {
System.out.print("送出" + petName + num + "只,");
if (petName.equals("dog")) {
this.dogCount = dogCount - num;
System.out.println("剩下" + petName + dogCount + "只");
} else if (petName.equals("penguin")) {
this.penguinCount = penguinCount - num;
System.out.println("剩下" + petName + penguinCount + "只");
}
}
//Pet p =new Dog();
public void playWithPet(Pet p) {
// 实际上访问的是子类重写的方法,多态访问成员方法的特点
p.play();
if (p instanceof Dog && dogCount != 0) {
//强制转换,访问的是子类成员特有的方法
Dog dog = (Dog) p;
dog.catchingFlyDisc();
} else if (p instanceof Penguin && penguinCount != 0) {
Penguin penguin = (Penguin) p;
penguin.swimming();
} else {
System.out.println("该宠物已送光,没得玩了,呜呜~~");
}
}
public String getPetName() {
return name;
}
public void setPetName(String petName) {
this.name = petName;
}
public int getDogCount() {
return dogCount;
}
public void setDogCount(int dogCount) {
this.dogCount = dogCount;
}
public int getPenguinCount() {
return penguinCount;
}
public void setPenguinCount(int penguinCount) {
this.penguinCount = penguinCount;
}
}
class Pet {
public void play() {
System.out.println("和某某玩");
}
}
class Dog extends Pet {
public void play() {
System.out.println("和狗玩");
}
public void catchingFlyDisc() {
System.out.println("狗接飞盘");
}
}
class Penguin extends Pet {
public void play() {
System.out.println("和企鹅玩");
}
public void swimming() {
System.out.println("企鹅游泳");
}
}
抽象类
抽象方法的概念: 一个仅仅给出方法声明,没有方法体的方法叫做抽象方法
抽象类的概念: 有一个抽象方法类必须是抽象类
抽象类的特点: 【精通】
1.抽象方法和抽象类使用 abstract 修饰
2.有一个抽象方法的类升级为抽象类
3.抽象类中一定有抽象方法吗? --> 不一定
4.没有抽象方法的类有意义吗? --> 有意义,防止外界创建对象
防止外界创建对象的方式:
a.构造方法私有
b.抽象类
c.接口
d.内部类
5.Cannot instantiate the type Animal(抽象类不能够实例化)
6.如果你想要 “实例化”,可以使用多态 【抽象类 强制使用者 利用多态】
7.抽象类的子类特点:
a.如果子类不想要实现父类的所有抽象方法,那么子类也是升级为抽象类
b.如果子类想要实现父类的抽象方法,那么就必须实现父类所有的抽象方法
8.抽象类和普通类的区别:
抽象类和普通类一模一样,只不过多了一个抽象方法
9.抽象类中的 成员变量,成员方法,构造方法,常量,静态方法的存在有什么意义呢?
成员变量: 给子类使用
成员方法: 给子类使用
构造方法: 帮助子类初始化父类继承下来的成员
抽象方法: 用于给子类重写,方便构成多态
常量: 直接通过类名即可方法,不仅给子类,给所有的类用
静态方法: 直接通过类名即可方法,不仅给子类,给所有的类用
总结: 抽象类就是一个彻头彻尾的 服务类,服务于子类,燃烧自己,照亮孩子
补充:抽象方法它能够和 native private final static 共存吗?
native: 本地方法,C实现的,有方法体,abstract没有方法体,不能共存
private: 私有修饰的方法不能够给子类继承,更不可能重写了,abstract就是用来给子类重写的 (冲突)
final: final修饰方法不能够给子类重写,abstract就是用来给子类重写的 (冲突)
static: static修饰方法方便别人调用,别人调用的方法肯定是希望实现好的,abstract没有方法体,没有实现的,所以没有意义
抽象方法到底应该使用那些关键字修饰? --> public、 protected
public class AbstractDemo02 {
public static void main(String[] args) {
// Animal an = new Animal();
Dog dog = new Dog(); // 面向具体编程
Animal an = new Dog(); // 面向抽象编程
// 面向接口编程
}
}
//抽象类
abstract class Animal {
String name;
int age;
public static final int NUM = 100;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
//抽象方法
public /*static*/ /*final*/ /*private*/ /*native*/ abstract void bark();
public abstract void show();
public void method() {
System.out.println("Animal.method()");
}
}
class Dog extends Animal {
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void bark() {
System.out.println("狗叫");
}
@Override
public void show() {
System.out.println("Dog show");
}
}
2、例题:
编写交通工具类,具有前进run()功能,子类有自行车、小轿车、地铁,重写父类方法,主人有属性name,age属性,方法回家goHome(交通工具),需要使用交通工具,使用抽象类优化程序。
public class Demo02 {
public static void main(String[] args) {
Host h = new Host("小明", 30);
Conveyance c = new Car();
h.goHome(c);
h.goHome(new Subway());
h.goHome(new Bike());
}
}
class Host {
private String name;
private int age;
public Host() {
}
public Host(String name, int age) {
this.name = name;
this.age = age;
}
public void goHome(Conveyance c) {
c.run();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
abstract class Conveyance {
public abstract void run();
}
class Bike extends Conveyance {
@Override
public void run() {
System.out.println("骑自行车回家");
}
}
class Car extends Conveyance {
public void run() {
System.out.println("开轿车回家");
}
}
class Subway extends Conveyance {
@Override
public void run() {
System.out.println("坐地铁回家");
}
}
接口
接口里面全都是抽象方法,接口里面就是一个声明而已,没有实现。格式:
interface 接口名 {
常量;
抽象方法;
// 在JDK1.8之后还可以有默认方法和静态方法
接口的实现类格式:
class 接口的实现类 implements 接口 {
}
理解: 接口和抽象类的区别 接口和继承的关系
接口的特点: 【精通】
1.接口使用 interface 修饰,是一种特殊的类,是一种比抽象类还要抽象的类
2.接口是常量和抽象方法的集合,在JDK1.8之后还可以有默认方法和静态方法
3.常量默认省略了public static final
方法默认省略了 public abstract
4.接口不能够实例化
5.如果我想要"实例化",利用多态 【父类引用(接口) 指向子类(接口的实现类) 对象】
6.接口的实现类特点:
a.如果一个类想要实现一个接口,就必须实现该接口当中所有的抽象方法
b.如果一个类不想实现接口当中的方法,那么该类必须升级为接口
7.接口是一种完全抽象,但是在JDK1.8之后可以使用默认方法和静态方法
8.接口是一种标准,是一种规范
9.接口是用来扩展功能
10.接口是灵活的
11.接口是规范,实现接口的功能还是属于第三方来实现的
12.接口 和 类之间的关系
类和类 单继承 不可以实现
类和接口 不可以继承 多实现
接口和接口 多继承 不可以实现
13.接口只是一个规范,而抽象类是模板,部分规范, 普通类是完全模板
接口习题:
-
1、要求如下:
(1) 所有的可以拨号的设备都应该有拨号功能 (Dailup)
(2) 所有的播放设备都可以有播放功能(Play)。
(3) 所有的照相设备都有拍照功能(takePhoto)。
(4) 定义一个电话类 Telephone,有拨号功能。
(5) 定义一个Dvd类有播放功能。
(6) 定义一个照相机类 Camera, 有照相功能。
(7) 定义一个手机类 Mobile, 有拨号,拍照,播放功能。
(8) 定义一个人类 Person, 有如下方法:
<1> 使用拨号设备 use (拨号设备)
<2> 使用拍照设备 use(拍照设备)
<3> 使用播放设备 use(播放设备)
<4> 使用拨号 播放 拍照设备 use(拨号播放拍照设备)
(9) 编写测试类Test 分别创建人,电话,Dvd,照相机,手机对象,让人使用这些对象 -
public class Demo03 { public static void main(String[] args) { // 利用多态来创建主人对象 AbsPerson p = new Master("小宁"); // 利用多态来创建电话 IDailup dailup = new Telephone(); p.use(dailup); p.use(new Dvd()); p.use(new Camera()); p.use(new Mobile()); } } abstract class AbsPerson { protected String name; public AbsPerson() { } public AbsPerson(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract void use(IDailup dailup); public abstract void use(IPlay play); public abstract void use(ITakePhoto takePhoto); public abstract void use(IDailupPlayTakephoto dailupPlayTakephoto); } class Master extends AbsPerson { public Master() { } public Master(String name) { super(name); } @Override public void use(IDailup dailup) { dailup.dailup(); } @Override public void use(IPlay play) { play.play(); } @Override public void use(ITakePhoto takePhoto) { takePhoto.takePhoto(); } @Override public void use(IDailupPlayTakephoto dailupPlayTakephoto) { dailupPlayTakephoto.dailup(); dailupPlayTakephoto.takePhoto(); dailupPlayTakephoto.play(); } } interface IDailup { void dailup(); } interface IPlay { void play(); } interface ITakePhoto { void takePhoto(); } interface IDailupPlayTakephoto { void dailup(); void play(); void takePhoto(); } class Telephone implements IDailup { public void dailup() { System.out.println("电话拨号"); } } class Dvd implements IPlay { @Override public void play() { System.out.println("DVD播放"); } } class Camera implements ITakePhoto { @Override public void takePhoto() { System.out.println("照相机照相"); } } class Mobile implements IDailupPlayTakephoto { @Override public void dailup() { System.out.println("手机拨号"); } @Override public void play() { System.out.println("手机播放"); } @Override public void takePhoto() { System.out.println("手机拍照"); } }