day08-JAVAOOP
课程目标
1. 【理解】什么是接口
2. 【掌握】接口的定义格式
3. 【掌握】接口的使用
4. 【理解】接口的成员特点
5. 【理解】类和接口 抽象类和接口之间的关系
6. 【掌握】单继承多实现
7. 【理解】接口之间的多继承
8. 【掌握】接口的案例
9. 【理解】什么是多态
10. 【理解】使用多态的前提
11. 【掌握】多态的格式
12. 【理解】多态中的成员访问特点
13. 【理解】多态中的好处和弊端
14. 【理解】多态中的转型
15. 【理解】转型的异常
16. 【掌握】综合案例
接口
接口基本概述及格式
接口概述
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法(功能),包含抽象方法 (JDK7及以前) , 默认方法和静态方法(JDK8)私有方法(JDK9)。
-
总结
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
Java中的接口更多的体现在对行为的抽象!
接口定义格式
接口用关键字interface修饰
public interface 接口名 {}
接口的使用
接口是不能创建对象,必须有实现类才能使用,类实现接口用implements表示
public class 类名 implements 接口名 {}
==注意: 接口的实现类必须重写接口中的所有的抽象方法,要么该类是一个抽象类==
接口成员的特点
成员变量
只能是常量,默认修饰符:public static final
成员方法
只能是抽象方法,默认修饰符:public abstract
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
代码演示
-
接口
public interface Inter { //接口只能是常量 //默认修饰符:public static final public int num1 = 10; public final int num2 = 20; public static final int num3 = 30; int num4 = 40; //接口没有构造方法 // public Inter() {} //接口中不能有方法主体 // public void show() {} //默认修饰符:public abstract public abstract void method1(); public void method2(); void show(); }
-
实现类
//子类可以是抽象类。但是意义不大 public abstract class InterImpl extends Object implements Inter {} /** * 字类是具体类,要实现接口中所有的抽象方法 * 所有类都默认继承Object类,Object类是所有类的超类 */ public class InterImpl extends Object implements Inter { public InterImpl(){ super();//所有super走得是object类 } @Override public void method1() { System.out.println("实现类中的method1"); } @Override public void method2() { System.out.println("实现类中的method2"); } @Override public void show() { System.out.println("实现类中的show"); } }
-
测试类
public class Test { public static void main(String[] args) { //接口不能实例化 // Inter i = new Inter(); //通过多态的方法实 Inter i = new InterImpl(); //访问成员变量 System.out.println(i.num1); //接口的常量是不能修改的 // i.num1 = 100; i.method1(); i.method2(); i.show(); } }
类与类的关系
类与类:
继承关系,只能单继承,不能多继承,可以多层继承(Father son sun)。
extends
接口与接口:
继承关系,可以单继承也可以多继承。public interface Sister extends Father,Mother
类与接口:
实现关系,可以单实现,也可以多实现。 public class Son extends Object implements Father,Mother
并且还可以在继承一个类的同时实现多个接口。
抽象类和接口的区别
A:成员区别
抽象类:
成员变量:可以变量,也可以常量
构造方法:有
抽象类不能实例化
成员方法:可以抽象,也可以非抽象
接口:
成员变量:只可以静态常量 可以省略 static final
构造方法:无
接口不能实例化
成员方法:默认是抽象的,可以省略abstract
B:关系区别
类与类
继承,单继承, 多层继承
类与接口
实现,单实现,多实现
接口与接口
继承,单继承,多继承
C:设计理念区别
抽象类【共性功能】。一般父类抽象
接口【扩展功能】。 方法集合
继承父类并实现多个接口
之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
多实现格式
class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... {
// 重写接口中抽象方法【必须】
// 重写接口中默认方法【不重名时可选】
}
代码演示
-
定义接口
interface A { public abstract void showA(); public abstract void show(); } interface B { public abstract void showB(); public abstract void show(); }
-
定义父类
public class Fu{}
-
定义实现类
public class C extends Fu implements A,B{ @Override public void showA() { System.out.println("showA"); } @Override public void showB() { System.out.println("showB"); } @Override public void show() { System.out.println("show"); } }
-
==注意事项==
接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次即可! 如果实现类继承了父类,这个父类是一个抽象类时,我们还需要再重写抽象类中的所有抽象方法。
接口默认方法
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。
默认方法定义格式
public default 返回值类型 方法的名称(参数列表){
方法体
}
默认方法的好处
接口的默认方法,实现类可以不用重写,默认方法可以用于接口升级
/** * @Auther: yanqi * @Desc 实现类没有没重写默认方法呢? * 接口中的抽象方法是必须要重写(实现) * 接口中的默认方法,不用重写,也可以手动重写,你默认方法存在的意义? * * 如果今后项目代码已以经写好,后期可能要扩展,接口中加新的方法(功能),如果你加抽象方法(功能) * 所有的实现类有影响,所以针对以上后期可能要扩展功能的话,代码的维护性比较差,出现了默认方法 */
-
定义接口
public interface LiveAble { //接口的默认方法 public default void fly(){ System.out.println("天上飞"); } }
-
定义实现类
public class LiveAbleImpl implements LiveAble { // default 可以选择是否重写,也可以根据实际需求进行重写 /* @Override public void fly() { System.out.println("自由自在的飞"); } */ }
-
定义测试类
public class InterfaceDemo { public static void main(String[] args) { // 创建子类对象 LiveAble a = new LiveAbleImpl(); // 调用默认方法 a.fly(); } }
接口静态方法
静态方法:使用 static 修饰,供接口直接调用。
静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用
静态方法定义格式
public static 返回值类型 方法名称(参数列表){
方法体
}
静态方法使用
-
定义接口
public interface LiveAble { //静态方法 public static void show2(){ System.out.println("静态方法-show2"); } }
-
定义实现类
public class LiveAbleImpl implements LiveAble { // 无法重写静态方法 }
-
定义测试类
public class InterfaceDemo { public static void main(String[] args) { //无法调用 // LiveAble l = new LiveAbleImpl(); // l.show2(); //接口名.静态方法(参数列表) LiveAble.show2(); } }
接口之间的多继承
一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。
-
定义父接口
interface A { public void method1(); } interface B { public void method2(); }
-
定义子接口
interface D extends A,B{ public void method2(); }
-
==注意==
接口多继承之后,如果想使用,我们还必须定义实现类,才能使用
接口小结
-
接口中只有常量和抽象方法 、默认方法、静态方法、私有方法
-
接口是没有静态代码块和构造方法的。
-
一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。 单继承多实现
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB { // 覆盖重写所有抽象方法 }
-
如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
-
如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
-
如果实现类,同时继承父类,实现接口,父类和接口的方法名、访问权限、返回类型都一样,则“类优先”原则。
接口案例-TODO
-
需求
对猫和狗进行训练,他们就可以跳高了,这里加入跳高功能。
请采用抽象类和接口来实现猫狗案例,并在测试类中进行测试。
-
代码实现
-
Animal类
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(); }
-
跳高接口Jumping
public interface Jumpping { public abstract void jump(); }
-
猫类(Cat)
//子类继承 抽象类,实现接口 public class Cat extends Animal implements Jumpping { 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) { Cat c = new Cat(); c.setName("加菲"); c.setAge(5); System.out.println(c.getName()+","+c.getAge()); c.eat(); c.jump(); } }
-
多态
什么是多态
多态是继封装、继承之后,面向对象的第三大特性。
生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。
-
定义:同一个对象,在不同时刻表现出来的形态是不同的
多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象 fu f = new zi();
多态格式
-
普通类多态的格式
父类 对象 = new 子类();
-
抽象类多态的格式
抽象类 对象名 = new 抽象类子类();
-
接口多态的格式
接口 对象名 = new 接口实现类();
多态中的成员访问特点
成员变量
编译看左边(父类),运行看左边(父类)
成员方法
编译看左边(父类),运行看右边(子类)
代码演示
-
动物类(Animal)
public class Animal { public int age = 40; public void eat() { System.out.println("动物吃东西"); } }
-
猫类(Cat)
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); // System.out.println(a.weight); //成员方法 //编译看左边(父类),运行看右边(子类) a.eat(); // a.playGame(); } }
多态的好处和弊端
多态的好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
多态的弊端
不能使用子类的特有成员
多态的好处代码演示
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利
-
定义父类
public abstract class Animal { public abstract void eat(); }
-
定义子类
class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } }
-
定义测试类
public class Test { public static void main(String[] args) { // 多态形式,创建对象 Cat c = new Cat(); Dog d = new Dog(); // 调用showCatEat showCatEat(c); // 调用showDogEat showDogEat(d); /* 多态的好处: 以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代,而执行效果一致 */ showAnimalEat(c); showAnimalEat(d); } public static void showCatEat (Cat c){ c.eat(); } public static void showDogEat (Dog d){ d.eat(); } public static void showAnimalEat (Animal a){ a.eat(); } }
多态的好处小结
由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,
所以showAnimalEat完全可以替代以上两方法。
不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用showAnimalEat都可以完成。
所以,多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。
多态中的转型
为什么要用转型
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点
"小麻烦"
。所以,想要调用子类特有的方法,必须做向下转型。
向上转型
多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
父类:Pet
^
|
子类:Pig Tiger Monkey
-
使用格式
向上转型 父类类型 变量名 = new 子类类型(); 如:Animal a = new Cat();
向下转型
父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
-
使用格式
子类类型 变量名 = (子类类型) 父类变量名; 如:Cat c =(Cat) a;
代码演示
-
定义类
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void watchHouse() { System.out.println("看家"); } }
-
测试类
public class Test { public static void main(String[] args) { // 向上转型 fu f = new zi() Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 猫c,抓老鼠; Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } }
转型的异常
问题描述
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
Cat c = (Cat)a;
c.catchMouse();//调用自己的方法
System.out.println("================");
// 向下转型
Dog d = (Dog)a; //引用的父类,是多态cat类,并不是dog
d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
}
}
这段代码可以通过编译,但是运行时,却报出了ClassCastException类型转换异常!这是因为,明明创建了
Cat类型对象,运行时,当然不能转换成Dog对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。
为了避免ClassCastException的发生,Java提供了instanceof关键字,给引用变量做类型的校验,格式如下:
instanceof使用格式
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。
instanceof代码演示
public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}
综合案例-TODO
案例需求
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所学知识分析,这个案例中有哪些具体类,哪些抽象类,哪些接口,并用代码实现。
代码实现
-
Person类
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(); }
-
抽象运动员类(Player)
public abstract class Player extends Person { public Player() { } public Player(String name, int age) { super(name, age); } public abstract void study(); }
-
抽象教练类(Coach)
public abstract class Coach extends Person { public Coach() { } public Coach(String name, int age) { super(name, age); } public abstract void teach(); }
-
学英语接口(SpeakEnglish)
public interface SpeakEnglish { public abstract void speak(); }
-
蓝球教练(BasketballCoach)
public class BasketballCoach extends Coach { public BasketballCoach() { } public BasketballCoach(String name, int age) { super(name, age); } @Override public void teach() { System.out.println("篮球教练教如何运球和投篮"); } @Override public void eat() { System.out.println("篮球教练吃羊肉,喝羊奶"); } }
-
乒乓球教练(PingPangCoach)
public class PingPangCoach extends Coach implements SpeakEnglish { public PingPangCoach() { } public PingPangCoach(String name, int age) { super(name, age); } @Override public void teach() { System.out.println("乒乓球教练教如何发球和接球"); } @Override public void eat() { System.out.println("乒乓球教练吃小白菜,喝大米粥"); } @Override public void speak() { System.out.println("乒乓球教练说英语"); } }
-
乒乓球运动员(PingPangPlayer)
public class PingPangPlayer extends Player implements SpeakEnglish { public PingPangPlayer() { } public PingPangPlayer(String name, int age) { super(name, age); } @Override public void study() { System.out.println("乒乓球运动员学习如何发球和接球"); } @Override public void eat() { System.out.println("乒乓球运动员吃大白菜,喝小米粥"); } @Override public void speak() { System.out.println("乒乓球运动员说英语"); } }
-
篮球运动员(BasketballPlayer)
public class BasketballPlayer extends Player { public BasketballPlayer() { } public BasketballPlayer(String name, int age) { super(name, age); } @Override public void study() { System.out.println("篮球运动员学习如何运球和投篮"); } @Override public void eat() { System.out.println("篮球运动员吃牛肉,喝牛奶"); } }