Java_多态&抽象类&接口
1. 多态
即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多。
1.1 多态的概述
什么是多态:
同一个对象,在不同时刻表现出来的不同形态
举例:猫
我们可以说猫是猫:猫 cat = new 猫();
我们也可以说猫是动物:动物 animal = new 猫();
这里猫在不同的时刻表现出来了不同的形态,这就是多态
多态的前提:
要有继承或实现关系
要有方法的重写
要有父类引用指向子类对象
public class Animal { public void eat() { System.out.println("动物吃东西"); } }
//有继承关系 public class Cat extends Animal{ //有方法的重写 @Override public void eat() { System.out.println("猫吃鱼"); } }
public class AnimalDemo { public static void main(String[] args) { //有父类引用指向子类对象 Animal a = new Cat(); } }
1.2 多态中的成员访问特点
成员变量:
编译看父类,运行看父类
成员方法:
编译看父类,运行看子类
public class Animal { public int age = 40; public void eat() { System.out.println("动物吃东西"); } }
public class Cat extends Animal { public int age = 20; public int weight = 10; @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
public class AnimalDemo { public static void main(String[] args) { //有父类引用指向子类对象 Animal a = new Cat(); System.out.println(a.age); //40 (成员变量:编译看父类,执行看父类) // System.out.println(a.weight); 报错(成员变量:编译看父类,执行看父类) a.eat(); //猫吃鱼 (成员方法:编译看父类,执行看子类) // a.playGame(); 报错(成员方法:编译看父类,执行看子类) } }
为什么成员变量和成员方法的访问不一样呢?
因为成员方法有重写,而成员变量没有
1.3 多态的好处和弊端
好处:
提高程序的扩展性。定义方法时候,使用父类型作为参数;在使用的时候,使用具体的子类型参与操作
弊端:
不能使用子类的特有成员
public class Animal { public void eat() { System.out.println("动物吃东西"); } }
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }
public class Dog extends Animal { @Override public void eat() { System.out.println("狗吃骨头"); } public void lookDoor() { System.out.println("狗看门"); } }
public class Pig extends Animal { @Override public void eat() { System.out.println("猪吃白菜"); } }
/* 动物操作类 */ public class AnimalOperator { /* public void useAnimal(Cat c) { //Cat c = new Cat(); c.eat(); } public void useAnimal(Dog d) { //Dog d = new Dog(); d.eat(); } */ public void useAnimal(Animal a) { //Animal a = new Cat(); //Animal a = new Dog(); a.eat(); // a.lookDoor(); 错误:不能使用子类的特有成员 } }
/* 测试类 */ public class AnimalDemo { public static void main(String[] args) { //创建动物操作类的对象,调用方法 AnimalOperator ao = new AnimalOperator(); Cat c = new Cat(); ao.useAnimal(c); Dog d = new Dog(); ao.useAnimal(d); Pig p = new Pig(); ao.useAnimal(p); } }
1.4 多态中的转型
向上转型:
父类引用指向子类对象就是向上转型
向下转型:
格式:子类型 对象名 = (子类型)父类引用;
public class Animal { public void eat() { System.out.println("动物吃东西"); } }
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
public class Dog extends Animal { @Override public void eat() { System.out.println("狗吃骨头"); } public void lookDoor() { System.out.println("狗看门"); } }
public class AnimalDemo { public static void main(String[] args) { //向上转型 Animal a = new Cat(); //父类引用指向子类对象 a.eat(); // a.playGame(); 错误(编译看父类) /* //创建Cat类的对象 Cat c = new Cat(); //内存中有两个Cat类的对象,为了使用playGame方法造两个对象,没必要 c.eat(); c.playGame(); */ //向下转型 Cat c = (Cat) a; ////格式:子类型 对象名 = (子类型)父类引用; c.eat(); c.playGame(); //向上转型 a = new Dog(); a.eat(); //向下转型 //ClassCastException:类型转换异常 Cat cc = (Cat) a; //此时内存中a是Dog,狗不能强转为猫 cc.eat(); cc.playGame(); } }
1.5 多态的案例
需求:请采用多态的思想实现猫和狗的案例,并在测试类中进行测试
思路:
1. 定义动物类(Animal)
成员变量:姓名,年龄
构造方法:无参,带参
成员方法:get/set方法,吃饭()
2. 定义猫类(Cat),继承动物类
构造方法:无参,带参
成员方法:重写吃饭()
3. 定义狗类(Dog),继承动物类
构造方法:无参,带参
成员方法:重写吃饭()
4. 定义测试类(AnimalDemo),写代码测试
public class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } 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; } public void eat() { System.out.println("动物吃东西"); } }
public class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } }
public class Dog extends Animal { public Dog() { } public Dog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("狗吃骨头"); } }
public class AnimalDemo { public static void main(String[] args) { //创建猫类对象进行测试 Animal a = new Cat(); a.setName("Tom"); a.setAge(2); System.out.println(a.getName() + "," + a.getAge()); a.eat(); a = new Cat("Tom", 5); System.out.println(a.getName() + "," + a.getAge()); a.eat(); //创建狗类对象进行测试 Animal a1 = new Dog(); a1.setName("Wang"); a1.setAge(3); System.out.println(a1.getName() + "," + a1.getAge()); a1.eat(); a1 = new Dog("Wang", 3); System.out.println(a1.getName() + "," + a1.getAge()); a1.eat(); } }
2. 抽象类
2.1 抽象类的概述
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!
2.2 抽象类的特点
抽象类和抽象方法必须使用 abstract 关键字修饰
//抽象类的定义 public abstract class 类名 {} //抽象方法的定义 public abstract void 方法名();
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能直接实例化
抽象类如何实例化:参照多态的方式,通过子类对象实例化,这叫抽象类多态
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
/* 抽象类 */ //抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类 public abstract class Animal { //抽象方法 public abstract void eat(); public void sleep() { System.out.println("睡觉"); } }
public class Cat extends Animal { //重写抽象类中的所有抽象方法 @Override public void eat() { System.out.println("猫吃鱼"); } }
//是抽象类 public abstract class Dog extends Animal { }
/* 测试类 */ public class AnimalDemo { public static void main(String[] args) { // Animal a = new Animal(); 抽象类不能直接实例化 //抽象类如何实例化:参照多态的方式,通过子类对象实例化,这叫抽象类多态 Animal a = new Cat(); a.eat(); a.sleep(); } }
2.3 抽象类的成员特点
成员变量:
既可以是变量;也可以是常量
构造方法:
空参构造;有参构造
(构造方法不能实例化,作用:用于子类访问父类数据的初始化)
成员方法:
抽象方法(限制子类必须完成某些动作:即子类要么重写父类中的抽象方法;要么子类也为抽象类)
普通方法(提高代码的复用性)
/* 抽象类 */ public abstract class Animal { private int age = 20; private final String city = "北京"; public Animal() { } public Animal(int age) { this.age = age; } public void show() { age = 40; System.out.println(age); // city = "上海"; 错误,city被final修饰,为常量,不可再赋值 System.out.println(city); } //父类中的抽象方法限制子类必须完成某些动作 //即子类要么重写父类中的抽象方法 //要么子类也为抽象类 public abstract void eat(); }
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } }
public class AnimalDemo { public static void main(String[] args) { Animal a = new Cat(); a.eat(); a.show(); } }
2.4 抽象类的案例
案例需求:
请采用抽象类的思想实现猫和狗的案例,并在测试类中进行测试
思路:
定义动物类(Animal)
成员变量:姓名,年龄
构造方法:无参,带参
成员方法:get/set方法,吃饭();
定义猫类(Cat),继承动物类
构造方法:无参,带参
成员方法:重写吃饭(){…}
定义狗类(Dog),继承动物类
构造方法:无参,带参
成员方法:重写吃饭(){…}
定义测试类(AnimalDemo),写代码测试
代码实现:
public abstract class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } 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; } public abstract void eat(); }
public class Cat extends Animal { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } }
public class Dog extends Animal { public Dog() { } public Dog(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("狗吃骨头"); } }
public class AnimalDemo { public static void main(String[] args) { //创建对象,按照多态的方式 Animal a = new Cat(); a.setName("Tom"); a.setAge(2); System.out.println(a.getName() + "," + a.getAge()); a.eat(); a = new Cat("Tom", 2); System.out.println(a.getName() + "," + a.getAge()); a.eat(); Animal a2 = new Dog(); a2.setName("Wang"); a2.setAge(3); System.out.println(a2.getName() + "," + a2.getAge()); a2.eat(); a2 = new Dog("Wang", 3); System.out.println(a2.getName() + "," + a2.getAge()); a2.eat(); } }
3. 接口
3.1 接口的概述
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
Java中的接口更多的体现在对行为的抽象!
3.2 接口的特点
接口用关键字 interface 修饰
public interface 接口名 {}
类实现接口用 implements 表示
public class 类名 implements 接口名 {}
接口不能直接实例化
接口如何实例化:参照多态的方式,通过实现类对象实例化,这叫接口多态。
多态的形式:具体类多态,抽象类多态,接口多态。
接口的子类:
要么重写接口中的所有抽象方法
要么子类也是抽象类
public class JumppingDemo { public static void main(String[] args) { // Jumpping j = new Jumpping(); 接口不能直接实例化 //接口如何实例化:参照多态的方式,通过实现类对象实例化,这叫接口多态。 Jumpping j = new Cat(); j.jump(); } }
/* 定义了一个接口 */ //接口用关键字 interface 修饰 public interface Jumpping { public abstract void jump(); }
//类实现接口用 implements 表示 public class Cat implements Jumpping { //要么重写接口中的所有抽象方法 @Override public void jump() { System.out.println("猫可以跳高了"); } }
//要么子类也是抽象类 public abstract class Dog implements Jumpping { }
3.3 接口的成员特点
成员变量
只能是常量,默认修饰符:public static final (接口中的成员变量默认被final修饰)
构造方法
没有,因为接口主要是扩展功能的(对行为进行抽象),而没有具体存在
一个类如果没有父类,默认继承自 Object 类
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
public interface Inter { public int num = 10; public final int num2 = 20; // public static final int num3 = 30; //等价于int num3 = 30; int num3 = 30; // public Inter() {} 错误,接口没有构造方法 public abstract void method(); //接口中的成员方法只能是抽象方法 void show(); //接口中的方法默认带有修饰符:public abstract }
//public class InterImpl implements Inter { 等价于 public class InterImpl extends Object implements Inter { public InterImpl() { super(); //用的是Object类里面的构造方法 } @Override public void method() { System.out.println("method"); } @Override public void show() { System.out.println("show"); } }
public class InterfaceDemo { public static void main(String[] args) { Inter i = new InterImpl(); // i.num = 20; 错误,接口中的成员变量默认被final修饰 System.out.println(i.num); // i.num2 = 40; 错误,num2被final修饰,为常量,不可重新赋值 System.out.println(i.num2); System.out.println(Inter.num); //可以通过接口名直接访问,说明num默认被静态修饰 System.out.println(Inter.num3); } }
3.4 接口的案例
案例需求:
对猫和狗进行训练,他们就可以跳高了,这里加入跳高功能。
请采用抽象类和接口来实现猫狗案例,并在测试类中进行测试。
思路:
定义接口(Jumpping)
成员方法:跳高();
定义抽象动物类(Animal)
成员变量:姓名,年龄;构造方法:无参,带参;成员方法:get/set方法,吃饭();
定义具体猫类(Cat),继承动物类,实现跳高接口
构造方法:无参,带参;成员方法:重写吃饭(){…},重写跳高方法(){…}
定义狗类(Dog) ,继承动物类,实现跳高接口
构造方法:无参,带参;成员方法:成员方法:重写吃饭(){…},重写跳高方法(){…}
定义测试类(AnimalDemo),写代码测试
public interface Jumping { public abstract void jump(); }
public abstract class Animal { private String name; private int age; public Animal() { } public Animal(String name, int age) { this.name = name; this.age = age; } 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; } public abstract void eat(); }
public class Cat extends Animal implements Jumping { public Cat() { } public Cat(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("猫吃鱼"); } @Override public void jump() { System.out.println("猫跳高了"); } }
狗类同理,所以省略
public class AnimalDemo { public static void main(String[] args) { //创建对象,调用方法 Jumping j = new Cat(); j.jump(); System.out.println("------"); Animal a = new Cat(); a.setName("Tom"); a.setAge(2); System.out.println(a.getName() + "," + a.getAge()); a.eat(); // a.jump; 错误,因为Animal类中无jump方法,除非强转:((Cat) a).jump(); a = new Cat("Tom", 2); System.out.println(a.getName() + "," + a.getAge()); a.eat(); System.out.println("------"); Cat c = new Cat(); //Cat类继承类Animal类,并且实现了Jumping接口,所以方法最多 c.setName("加菲"); c.setAge(3); System.out.println(c.getName() + "," + c.getAge()); c.eat(); c.jump(); } }
3.5 类和接口的关系
类与类的关系:
继承关系,只能单继承,但是可以多层继承
类与接口的关系:
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口与接口的关系:
继承关系,可以单继承,也可以多继承
public interface Inter1{ }
//接口与接口的关系 //继承关系,可以单继承,也可以多继承。下面是单继承 public interface Inter2 extends Inter1 { }
//接口与接口的关系 //继承关系,可以单继承,也可以多继承。下面是多继承 public interface Inter3 extends Inter1, Inter2 { }
//类与接口的关系 //实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口 //下面是继承一个类的同时实现多个接口 public class InterImpl extends Object implements Inter1, Inter2, Inter3 { }
3.6 抽象类和接口的区别
成员区别:
抽象类:变量,常量;有构造方法;有抽象方法,也有非抽象方法
接口:常量;抽象方法
关系区别:
类与类:继承,单继承
类与接口:实现,可以单实现,也可以多实现
接口与接口:继承,单继承,多继承
设计理念区别:
抽象类:对类抽象,包括属性、行为
接口:对行为抽象,主要是行为
门和警报的例子:
门:都有open()和close()两个动作,这个时候,我们可以分别使用抽象类和接口来定义这个抽象概念
//抽象类 public abstract class Door { public abstract void open(); public abstract void close(); } //接口 public interface Door { void open(); void close(); }
之后,门有了警报功能:
//抽象类 public abstract class Door { public abstract void open(); public abstract void close(); public abstract void alarm(); }
继承这个抽象类的子类都会具有警报功能,但有的门不具备有警报功能,所以将open、close、alarm都放到一个抽象类中不好。
//接口 public interface Door { void open(); void close(); void alarm(); }
需要用到警报功能的类就可以实现这个接口,但是open和close一并实现了,也许这个类并不具备open和close功能,所以这样做也不好。
可以这么做:
public interface Alarm { void alarm(); }
public abstract class Door { public abstract void open(); public abstract void close(); }
public class AlarmDoor extends Door implements Alarm { public void open() { //... } public void close() { //... } public void alarm() { //... } }
4. 综合案例
案例需求:
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所学知识分析,这个案例中有哪些具体类,哪些抽象类,哪些接口,并用代码实现。
分析:从具体到抽象
乒乓球运动员 & 篮球运动员(具体)—— 运动员(抽象)
乒乓球教练 & 篮球教练(具体) —— 教练(抽象)
运动员 & 教练 —— 人(抽象)
乒乓球运动员 & 乒乓球教练 —— 学习英语(接口)

实现:从抽象到具体
使用:使用的是具体的类的对象
定义说英语接口:
public interface SpeakEnglish { public abstract void speak(); }
定义抽象人类:
//抽象人类 public abstract class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } 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; } public abstract void eat(); }
定义抽象教练类,继承人类:
//抽象教练类 public abstract class Coach extends Person{ public Coach() { } public Coach(String name, int age) { super(name, age); } public abstract void teach(); }
定义抽象运动员类,继承人类:
//抽象运动员类 public abstract class Player extends Person { public Player() { } public Player(String name, int age) { super(name, age); } public abstract void study(); }
定义具体篮球教练类,继承教练类:
public class BasketballCoach extends Coach { public BasketballCoach() { } public BasketballCoach(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("篮球教练教如何运球和投篮"); } @Override public void teach() { System.out.println("篮球教练吃饭"); } }
定义具体乒乓球教练类,继承教练类,实现说英语接口:
public class PingPangCoach extends Coach implements SpeakEnglish { public PingPangCoach() { } public PingPangCoach(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("乒乓球教练吃饭"); } @Override public void teach() { System.out.println("乒乓球教练教如何发球和接球"); } @Override public void speak() { System.out.println("乒乓球教练说英语"); } }
定义具体篮球运动员类,继承运动员类:
public class BasketballPlayer extends Player { public BasketballPlayer() { } public BasketballPlayer(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("篮球运动员吃饭"); } @Override public void study() { System.out.println("篮球运动员学习如何运球和投篮"); } }
定义具体乒乓球运动员类,继承运动员类,实现说英语接口:
public class PingPangPlayer extends Player implements SpeakEnglish { public PingPangPlayer() { } public PingPangPlayer(String name, int age) { super(name, age); } @Override public void eat() { System.out.println("乒乓球运动员吃饭"); } @Override public void study() { System.out.println("乒乓球运动员学习如何发球和接球"); } @Override public void speak() { System.out.println("乒乓球运动员说英语"); } }
定义测试类,写代码测试:
/* 测试类 */ public class PersonDemo { public static void main(String[] args) { //创建对象 PingPangPlayer ppp = new PingPangPlayer(); ppp.setName("张三"); ppp.setAge(24); System.out.println(ppp.getName() + "," + ppp.getAge()); ppp.eat(); ppp.study(); ppp.speak(); System.out.println("----------"); PingPangPlayer ppp2 = new PingPangPlayer("李四",35); System.out.println(ppp2.getName() + "," + ppp2.getAge()); ppp2.eat(); ppp2.study(); ppp2.speak(); System.out.println("**********"); BasketballPlayer bp = new BasketballPlayer("王五", 26); System.out.println(bp.getName() + "," + bp.getAge()); bp.eat(); bp.study(); System.out.println("----------"); BasketballCoach bc = new BasketballCoach(); bc.setName("赵六"); bc.setAge(32); System.out.println(bc.getName() + "," + bc.getAge()); bc.eat(); bc.teach(); } }

浙公网安备 33010602011771号