接口概念
接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”。
接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。
接口的定义
与定义类的class不同,接口定义时需要使用interface关键字。
定义接口所在的仍为.java文件,虽然声明时使用的为interface关键字的编译后仍然会产生.class文件。这点可以让我们将接口看做是一种只包含了功能声明的特殊类。
定义格式:
public interface 接口名 {
抽象方法1;
抽象方法2;
抽象方法3;
}
使用interface代替了原来的class,其他步骤与定义类相同:
接口中的方法均为公共访问的抽象方法
接口中无法定义普通的成员变量
类实现接口
类与接口的关系为实现关系,即类实现接口。实现的动作类似继承,只是关键字不同,实现使用implements。
其他类(实现类)实现接口后,就相当于声明:”我应该具备这个接口中的功能”。实现类仍然需要重写方法以实现具体的功能。
格式:
class 类 implements 接口 {
重写接口中方法
}
在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
l 接口中定义功能,当需要具有该功能时,可以让类实现该接口,只声明了应该具备该方法,是功能的声明。
l 在具体实现类中重写方法,实现功能,是方法的具体实现。
于是,通过以上两个动作将功能的声明与实现便分开了。(此时请重新思考:类是现实事物的描述,接口是功能的集合。)
类与类之间是 单继承
类与接口是 多实现
接口和接口是 多继承
通过上面的例子总结接口和抽象类的区别:
相同点:
l 都位于继承的顶端,用于被其他类实现或继承;
2 都不能直接实例化对象;
3 都包含抽象方法,其子类都必须覆写这些抽象方法;
区别:
l 抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法;
2 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承)
3 抽象类为继承体系中的共性内容,接口为继承体系中的扩展功能
多态概述
多态是继封装、继承之后,面向对象的第三大特性。
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。
最终多态体现为父类引用变量可以指向子类对象。
多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
父类引用指向子类对象就是多态的定义格式。同一个父类的方法会被不同的子类重写为各自的具体实现。在调用方法时,调用的为各个子类重写后的方法。
父类类型 变量名 = new 子类类型();
变量名.方法名();
此时,虽然该变量指向的是子类对象,但表现为一个父类的形态,可以调用一切父类的方法,子类特有的方法将不能调用。
多态的存在意义(优点)为:
配合继承与方法重写提高了代码的复用性与扩展性,如果没有方法重写,则多态同样没有意义。
多态的弊端: 不能调用子类的特有方法
向上向下类型转换
多态本身是子类类型向父类类型向上转型的过程。
多态的转型分为向上转型与向下转型两种:
l 向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal p = new Cat();
l 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
如:Cat c = (Cat) a; //变量p 实际上指向Cat对象
案例
请使用代码描述:奥迪车(Audi)都具有跑的功能,但是智能奥迪车(SmartAudi)除了具有跑的功能外,还具有自动泊车(automaticParking)和无人驾驶(automaticDrive)的功能!
要求:在测试类中创建Audi车对象和智能奥迪车对象,分别调用各自方法; 然后测试向上转型(转换为父类类型和实现的接口类型)和向下转型.
操作步骤描述
1.定义奥迪车类(Audi)
a)成员方法: 跑(run())
i.输出格式: 奥迪车在跑
2.定义智能接口(Smart)
a)抽象方法: 自动泊车(automaticParking)和无人驾驶(automaticDrive)
3.定义智能奥迪车类(SmartAudi) 继承Audi实现Smart接口
a)成员方法
i.实现automaticParking方法
1.输出:智能奥迪车在自动泊车
ii.实现automaticDrive方法
1.输出:智能奥迪车在无人驾驶
4.定义测试类Test
a)提供main方法
b)在main方法中
i.创建Audi车对象 a,调用跑方法
ii.创建SmartAudi车对象 sa,调用跑,自动泊车,自动驾驶方法
iii.定义Audi类型的变量 aa 把sa赋值aa; 测试aa只能调用run方法,不能调用其他方法
iv.判断如果aa是SmartAudi的实例对象, 把aa强制转换为saa;使用saa调用自动泊车和自动驾驶方法
v.定义Smart类型的变量 s,把sa赋值给s,测试只能调用自动泊车和自动驾驶方法,不能调用run方法.
//定义奥迪类
public class Audi {
public void run(){
System.out.println("奥迪车在跑");
}
}
//定义接口Smart,两个抽象类
public interface Smart {
public abstract void automaticParking();
public abstract void automaticDrive();
}
//定义智能奥迪车类(SmartAudi) 继承Audi实现Smart接口
public class SmartAudi extends Audi implements Smart{
@Override
public void automaticParking() {
System.out.println("智能奥迪车在自动泊车");
}
@Override
public void automaticDrive() {
System.out.println("智能奥迪车在无人驾驶");
}
}
//定义测试类Test
public class Test {
public static void main(String[] args) {
Audi a = new Audi();
a.run();
SmartAudi sa = new SmartAudi();
sa.run();
sa.automaticDrive();
sa.automaticParking();
Audi aa = sa;
aa.run();
SmartAudi saa = (SmartAudi)aa;//向下转型
saa.automaticDrive();
saa.automaticParking();
Smart s = sa;
s.automaticDrive();
s.automaticParking();
}
}