接口
接口的基本概念
Java接口是一系列方法的声明,是一些方法特征的集合。一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。一个类通过实现(implements)接口的方式,从而来继承接口中的抽象方法。接口中的访问权限只有public,即使省略不写也会默认为public。
接口与其说是描述一个类的行为特征,还不如说是描述多个类的行为共性。比如, 每一种动物都有一些共同的行为特征,定义两个行为特征——叫、吃。但是每种动物的叫法和吃法是不一样的,所以我们在这里定义一个接口,里面涵盖了叫和吃两个抽象方法,等待实体动物类去自我实现。
接口的定义
用interface修饰
interface A{ //定义一个接口
public static final String MSG = "Hello"; //全局常量
public abstract void print(); //抽象方法
}
注意事项:
- 定义变量时,一定要直接赋值。
- 接口中不能使用static代码块。
接口的使用方法
一、接口中存在抽象方法,所以接口对象不能直接用关键字new进行实例化。
(1)接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口;
(2)接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法; 如果子类是抽象类则可以不用在该子类中实现接口中的抽象方法。
(3)接口的对象可以利用子类对象的向上转型进行实例化。
不能使用new操作符实例化一个接口,但可以声明一个接口变量,该变量必须引用一个实现该接口的类的对象。可以使用 instanceof 检查一个对象是否实现了某个特定的接口。
interface A{ //定义一个接口A
public static final String MSG = "Hello"; //全局常量
public abstract void print(); //抽象方法
}
interface B{ //定义一个接口B
public abstract void get();
}
class X implements A,B{ //X类实现了A和B两个接口,两个接口没有任何直接联系,却拥有同一个子类X
@Override
public void print() {
System.out.println("接口A的抽象方法print()");
}
@Override
public void get() {
System.out.println("接口B的抽象方法get()");
}
}
public class Test {
public static void main(String[] args){
X x = new X(); //实例化子类对象
A a = x; //向上转型
B b = x; //向上转型
a.print();
b.get();
}
}
Output:
接口A的抽象方法print()
接口B的抽象方法get()
以上代码实例化了X类的对象,由于X类是A和B的子类,那么X类的对象可以转换为A接口或B接口的对象。
修改公有类代码如下:
public class Test {
public static void main(String[] args){
A a = new X();
B b = (B) a; //向上转型
b.get();
}
}
Output:
接口B的抽象方法get()
修改公有类代码如下:
public class Test {
public static void main(String[] args){
A a = new X();
B b = (B) a;
b.get();
System.out.println(a instanceof A);
System.out.println(a instanceof B);
}
}
Output:
接口B的抽象方法get()
true
true
二、子类除了实现接口外,还可以继承抽象类。若既要继承抽象类,同时还要实现接口的话,语法格式:
class 子类 [extends 父类] [implemetns 接口1,接口2,…] { }
三、在Java中,一个抽象类只能继承一个抽象类,但一个接口却可以使用extends关键字同时继承多个接口,但接口不能继承抽象类(没必要用接口去继承抽象类,还不如把接口改成子类)。
从继承关系上来说接口的限制比抽象类少:
- 一个抽象类只能继承一个父类,而接口可以继承多个接口。
一个子类只能继承一个抽象类,却可以实现多个接口(在Java中,接口的主要功能是解决单继承局限问题)。
interface A{
public abstract void funA();
}
interface B{public abstract void funB();
}
interface C extends A,B{//使用extends使C接口同时继承了接口A和Bpublic abstract void funC();
}
class X implements C{ //实现继承链上所有接口@Override
public void funA() {
System.out.println("funA");
}
@Override
public void funB() {
System.out.println("funB");
}
@Override
public void funC() {
System.out.println("funC");
}
}
public class Test {public static void main(String[] args){
X x = new X(); //实例化子类对象
A a = x; //向上转型
B b = x;
C c = x;
a.funA();
b.funB();
c.funA();c.funB();c.funC();
}
}
Output:
funA
funB
funA
funB
funC
四、正如抽象类中可以定义抽象内部类(static abstract),抽象内部类的子类一样,在接口中也可以定义普通内部类(public static)、抽象内部类(public abstract)和内部接口(public static)。权限见访问权限
在接口中定义一个抽象内部类:
interface A{
public abstract void funA();
abstract class B{ //定义一个抽象内部类,默认 public 权限
public abstract void funB();
}
}
class C extends A.B implements A{
@Override
public void funA() {
System.out.println("funA");
}
@Override
public void funB() {
System.out.println("funB");
}
}
public class Test {
public static void main(String[] args){
C c = new C();
c.funA();
c.funB();
A a = c;
a.funA();
}
}
Output:
funA
funB
funA
在接口中使用static去定义一个内接口,它表示一个外部接口:
interface A{
public abstract void funA();
static interface B{ //使用了static,是一个外部接口,默认 public 权限
public abstract void funB();
}
}
class C implements A.B{
@Override
public void funB() {
System.out.println("funB");
}
}
public class Test {
public static void main(String[] args){
C c = new C();
c.funB();
A.B ab = c;
ab.funB();
}
}
Output:
funB
funB
五、接口可以用来实现解耦。
六、接口的标识用法
虽然接口内部定义了一些抽象方法,但是并不是所有的接口内部都必须要有方法,比如Serializable接口。Serializable接口的作用是使对象能够“序列化”,但是Serializable接口中却没有任何内容。也就是说,如果有一个类需要实现“序列化”的功能,则这个类必须去实现Serializable接口,但是它却并不用实现接口中的方法(因为接口中没有方法)。此时,这个Serializable接口仅仅是一个“标识”接口,是用来标志一个类的,标志(表示)这个类具有“序列化”功能。
常见问题
一、为什么接口中的常量必须使用public static final修饰?
public: 使接口的实现类可以使用这个常量。
static:用static修饰表示它(常量)是属于类的,随的类的加载而存在;如果是非static的话,就表示它是属于对象的,只有建立对象时才有它;而接口是不能去建立对象的,所以接口的常量必须定义为static
final:用final修饰是保证接口中定义的常量不能被实现接口的类修改,如果没有final的话,由子类随意去修改的话,接口建立这个常量就没有意义了。
同时,Java是不支持多继承的。
如果多个父类中含有同名变量或相同的函数,那么子类在调用父类的这些(非static修饰)同名变量或相同函数时就会出现分歧,(比如用super调用时)不知道到底要调用哪一个,所以不支持多继承;但为了弥补只支持单继承带来的缺点,Java可以只继承一个类,但却可以同时实现多个接口,而接口中的成员变量都用static和final修饰,使其成为一个常量(调用时直接接口名.变量名),如果不这么做,那就又会出现上面的分歧(除非java成员变量永远都是static)。
在设计模式中的应用
工厂设计模式,代理设计模式。
接口的实际应用(标准定义)
例一
要先开发出USB接口标准,然后设备厂商才可以设计出USB设备。
interface USB { // 定义一个USB的标准
public abstract void install() ;
public abstract void work() ;
}
class Computer { //在电脑上应用此接口
public void plugin(USB usb) {
usb.install() ;
usb.work() ;
}
}
class Phone implements USB { //定义USB设备----手机
public void install() {
System.out.println("安装手机驱动程序。") ;
}
public void work() {
System.out.println("手机与电脑进行工作。") ;
}
}
class Print implements USB { //定义USB设备----打印机
public void install() {
System.out.println("安装打印机驱动程序。") ;
}
public void work() {
System.out.println("进行文件打印。") ;
}
}
class MP3 implements USB { //定义USB设备----MP3
public void install() {
System.out.println("安装MP3驱动程序。") ;
}
public void work() {
System.out.println("进行MP3拷贝。") ;
}
}
public class K {
public static void main(String args[]) {
Computer c = new Computer() ;
c.plugin(new Phone()) ;
c.plugin(new Print()) ;
c.plugin(new MP3()) ;
}
}
Output:
安装手机驱动程序。
手机与电脑进行工作。
安装打印机驱动程序。
进行文件打印。
安装MP3驱动程序。
进行MP3拷贝。
还没有评论,来说两句吧...