1 接口
1)概念
- 接口是功能的集合
- 接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。
2)接口的定义
public interface 接口名 {
抽象方法1;
抽象方法2;
抽象方法3;
}
注意:
- 接口中的方法都是公共访问的抽象方法
- 接口中无法定义普通的成员变量
3)类实现接口
格式:
class 类 implements 接口 {
重写接口中的方法
}
注意:
- 在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
4)接口中成员的特点
- 接口中可以定义变量,但是变量必须有固定的修饰符:public static final,所以接口中的变量也称为常量,其值不能改变。
- 接口中可以定义方法,但也要有固定的修饰符:public abstract,可以省略abstract。
- 接口中不可以创建对象
- 子类必须覆盖接口中的所有抽象方法后才能实例化。否则子类就是一个抽象类。
示例:
// 定义一个名为Demo的接口
interface Demo{
public static final int NUM=3;
public abstract void show1();
public abstract void show2();
}
// 实现Demo接口
class DemoImpl implements Demo{
// 重写接口中的方法
public void show1(){}
public void show2(){}
}
5)接口的多实现
- 一个类可以实现多个接口,解决了不能多继承的缺点。
- 接口的出现解决了单继承的局限性。父类中定义了事物的基本功能,接口中定义了事物的扩展功能。
- 一个接口能继承另一个接口
- 接口允许多继承,可以继承多个接口
6)接口与抽象的异同
相同点:
- 都位于继承的顶端,用于被其他类实现或继承
- 都不能直接实例化对象
- 都包含抽象方法,其子类必须覆盖这些抽象方法
区别:
- 抽象类中的方法可以有方法体,而接口中只包含抽象方法,没有方法体。
- 一个类只能继承一个直接父类,可以有多个接口;接口可以继承多个接口。
- 接口中不能含有静态代码及静态方法,而抽象类中可以有。
- 抽象类中的成员变量可以是各种类型的,但接口中只能是public static final的。
7)抽象类与接口的选用
- 优先选用接口,尽量少用抽象类。
- 需要定义子类行为,又要为子类提供共性功能时才选用抽象类。
2 多态
1)概念
多态是同一个行为具有多个不同表现形式或形态的能力。
2)多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象
3)定义格式
父类类型 变量名 = new 子类类型();
变量名.方法名();
// 1. 普通类多态定义格式
class Fu(){}
class Zi extends Fu(){}
// 类的多态使用
Fu f = new Zi();
// 2. 抽象类多态定义的格式
abstract class Fu(){
public abstract void func();
}
class Zi extends Fu(){
public void func(){
System.out.println("重写父类抽象方法");
}
}
// 类的多态使用
Fu fu = new Zi();
// 3. 接口多态定义的格式
interface Fu{
public abstract void func();
}
class Zi implements Fu{
public void func(){
System.out.println("重写接口抽象方法");
}
}
// 接口的多态使用
Fu fu = new Zi();
4)多态成员特点
// 父类
public class Fu {
int num = 4;
void show() {
System.out.println("fu show()");
}
}
// 子类
class Zi extends Fu {
int num = 5;
void show() {
System.out.println("zi show()");
}
}
// 测试类
public class TestSth {
public static void main(String[] args) {
Fu fu = new Zi();
System.out.println(fu.num); // 4,是父类中的num
fu.show(); // zi show(),是子类的show
Zi zi = new Zi();
System.out.println(zi.num); // 5
zi.show(); // zi show()
}
}
总结:
1 当子父类中出现同名的成员变量时,多态调用该变量时:
- 编译时期:参考引用型变量所属类中是否有被调用的成员变量,没有则编译失败。
- 运行时期:调用引用型变量所属的类中的成员变量。
- 简而言之:编译和运行都参考等号的左边。
2 当子父类中出现同名的成员方法时,多态调用该方法时:
- 编译时期:参考引用变量所属的类,若类中没有调用的方法,编译失败。
- 运行时期:参考引用变量所指的对象所属的类,运行对象所属类中的成员方法。
- 简而言之:编译看左边,运行看右边。
5)instanceof 关键字
此关键字用来判断某个对象是否属于某种数据类型。
Fu fu = new Zi();
boolean flag=fu instacneof Zi; // true
6)转型
多态的转型分为向上转型和向下转型。
- 向上转型:当有子类对象赋值给一个父类引用时,是向上转型,多态本身就是向上转型的过程。
父类类型 变量名 = new 子类类型();
例如:
Fu f = new Zi();
- 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。
子类类型 变量名 = (子类类型) 父类类型的变量;
例如:
Zi z = (Fu) f;
7)多态的好处与弊端
向上转型的好处是隐藏了子类类型,提高了代码的扩展性。
但是,只能使用父类共性的内容,无法使用子类特用的功能。
总结:
1 何时用向上转型
- 当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作时。
2 何时用向下转型
- 当要使用子类特有的功能时。
示例:
// 描述动物类,并抽取共性eat方法
abstract class Animal{
abstract void eat();
}
// 描述狗类,继承动物类,重写eat方法,增加看家方法
class Dog extends Animal{
void eat(){
System.out.println("狗吃东西");
}
void lookHome(){
System.out.println("看家");
}
}
// 描述猫类,继承动物类,重写eat方法,增加抓老鼠方法
class Cat extends Animal{
void eat(){
System.out.println("猫吃东西");
}
void catchMouse(){
System.out.println("抓老鼠");
}
}
// 测试类
public class Test{
public static void main(String[] args){
Animal a = new Dog(); // 多态形式,创建一个狗对象
a.eat(); // 狗吃东西
// 健壮性判断
if(!a instanceof Dog){
System.out.println("类型不匹配,不能转换")
return;
}
Dog d = (Dog) a; // 向下转型
d.lookHome(); // 看家
}
}