一、定义
Java接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
接口定义的一般形式为:
[访问控制符]interface <接口名> {
类型标识符final 符号常量名n = 常数;
返回值类型 方法名([参数列表]);
…
}
Interface IMessage{
public static final String COUNTRY="China";//全局常量
public abstract void print();//抽象方法
}
由于接口中的方法都是抽象方法和全局常量,我们一般可以省略抽象方法前面的修饰public abstract,以及常量前面public static final的修饰,例如:
Interface IMessage{
String COUNTRY="China";//全局常量
void print();//抽象方法
}
二、接口的特点
1、Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用"_"分隔)
2、Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化
3、Java接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法
4、接口中没有构造方法,不能被实例化
5、一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口
6、Java接口必须通过类来实现它的抽象方法
7、当类实现了某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类
8、不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例
9、一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承.
三、接口的理解
1、 接口是一种特殊的抽象类,包含方法和属性(成员变量),其中方法都是抽象方法,即只有方法名没有方法体的形式。所有成员变量都是常量没有变量。
2、接口存在的意义:
接口的存在是根据外部的功能而存在的。他包含很多抽象方法,这些抽象方法需要实现,那么就需要实现类,实现类必须实现这些方法,当然也可以有自己的方法。但是接口中的方法一个不能少。因此接口就很大程度上限制了实现类的方法或功能。另外接口下面可以有好几个这种类,并且这些实现类功能上可以相同,这就实现了不相关类的相同行为。
3、接口所运用的思想:接口的思想是封装,外部调用只能通过接口调用,并不知道实现类是如何实现的,这就起到了封装作用。
4、接口带来的好处:只要接口不变那么他的实现类想怎么变都行,这样的话就提高了系统的可扩展性和可维护性。即“接口是系统可插拔性的保证”。
5、接口和抽象类:1优先选用接口,2在既定义了子类的行为,又要为子类提供公共的功能时应选择抽象类。
6、接口和继承:一个类可以实现多个接口,但继承只能允许一个类,即单继承。2类的继承有安全隐患,接口没有。
请记住:一切事物均有功能,即一切事物均有接口。
类与接口的关系为实现关系,即类实现接口。实现的动作类似继承,只是关键字不同,实现使用implements。
其他类(实现类)实现接口后,就相当于声明:”我应该具备这个接口中的功能”。实现类仍然需要重写方法以实现具体的功能。
格式:
class 类 implements 接口 {
重写接口中方法
}
在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
1)接口中定义功能,当需要具有该功能时,可以让类实现该接口,只声明了应该具备该方法,是功能的声明。
2)在具体实现类中重写方法,实现功能,是方法的具体实现。
于是,通过以上两个动作将功能的声明与实现便分开了。(此时请重新思考:类是现实事物的描述,接口是功能的集合。)
举例;
比如一台电脑,主板上的PCI插槽就可以理解为现实中的接口
你可你把声卡,显卡,网卡都插在PCI插槽上,而不用担心那个插槽是专门插哪个的
原因是做主板的厂家和做各种卡的厂家都遵守了统一的规定 包括尺寸,排线等等
但是各种卡的内部实现结构是不一样的.
通过生活中例子模拟java面向接口编程实例
package com.test;
/**
* 定义PCI接口,定义主板PCI插槽规范
* @author yanerkang
*
*/
public interface PCI {
void start();
void stop();
}
package com.test;
/**
* 网卡实现PCI接口规范
* @author yanerkang
*
*/
public class NetworkCard implements PCI {
public void start() {
System.out.println("Send...");
}
public void stop() {
System.out.println("Network stop!");
}
}
package com.test;
/**
* 显卡实现PCI接口规范
* @author yanerkang
*
*/
public class GraphicCard implements PCI{
public void start() {
System.out.println("Display Graphic...");
}
public void stop() {
System.out.println("Display Graphic stop!");
}
}
package com.test;
/**
* 声卡实现PCI接口规范
* @author yanerkang
*
*/
public class SoundCard implements PCI {
public void start() {
System.out.println("Du du...");
}
public void stop() {
System.out.println("Sound stop!");
}
}
package com.test;
/**
* 主板类
* @author yanerkang
*
*/
public class MainBoard {
/**
* 通过这个方法,主板上可以插入任何实现PCI接口规范的卡
*
* @param pci 参数类型为接口,任何实现接口的类都可以传进来.
*/
public void usePCICard(PCI pci) {
pci.start();
pci.stop();
}
}
package com.test;
public class Assembler {
/**
* @param args
*/
public static void main(String[] args) {
MainBoard mb=new MainBoard();
//在主板上插入网卡
PCI nc=new NetworkCard();
mb.usePCICard(nc);
//在主板上插入声卡
PCI sc=new SoundCard();
mb.usePCICard(sc);
//在主板上插入显卡
PCI gc=new GraphicCard();
mb.usePCICard(gc);
}
}
输出:
Send...
Network stop!
Du du...
Sound stop!
Display Graphic...
Display Graphic stop!
四、接口、抽象类的区别
总结:
- 1、抽象类和接口都不能被直接实例化,如果二者要实例化,就涉及到多态,不懂多态的参见java的多态 。如果抽象类要实例化,那么抽象类定义的变量必须指向一个子类对象,这个子类继承了这个抽象类并实现了这个抽象类的所有抽象方法。如果接口要实例化,那么这个接口定义的变量要指向一个子类对象,这个子类必须实现了这个接口所有的方法。
- 2、抽象类要被子类继承,接口要被子类实现。
- 3、接口里面只能对方法进行声明,抽象类既可以对方法进行声明也可以对方法进行实现。
- 4、抽象类里面的抽象方法必须全部被子类实现,如果子类不能全部实现,那么子类必须也是抽象类。接口里面的方法也必须全部被子类实现,如果子类不能实现那么子类必须是抽象类。
- 5、接口里面的方法只能声明,不能有具体的实现。这说明接口是设计的结果,抽象类是重构的结果。
- 6、抽象类里面可以没有抽象方法。
- 7、如果一个类里面有抽象方法,那么这个类一定是抽象类。
- 8、抽象类中的方法都要被实现,所以抽象方法不能是静态的static,也不能是私有的private。
- 9、接口(类)可以继承接口,甚至可以继承多个接口。但是类只能继承一个类。10、抽象级别(从高到低):接口>抽象类>实现类。
- 11、抽象类主要是用来抽象类别,接口主要是用来抽象方法功能。当你关注事物的本质的时候,请用抽象类;当你关注一种操作的时候,用接口。
- 12、抽象类的功能应该要远多于接口,但是定义抽象类的代价较高。因为高级语言一个类只能继承一个父类,即你在设计这个类的时候必须要抽象出所有这个类的子类所具有的共同属性和方法;但是类(接口)却可以继承多个接口,因此每个接口你只需要将特定的动作方法抽象到这个接口即可。也就是说,接口的设计具有更大的可扩展性,而抽象类的设计必须十分谨慎。
五、接口实现回调机制
一般来说,模块之间都存在一定的调用关系,从调用方式上看,可以分为三类同步调用、异步调用和回调。
同步调用是一种阻塞式调用,即在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行。
异步调用是一种类似消息或事件的机制解决了同步阻塞的问题,例如 A通知 B后,他们各走各的路,互不影响,不用像同步调用那样, A通知 B后,非得等到 B走完后, A才继续走 。
回调是一种双向的调用模式,也就是说,被调用的接口被调用时也会调用对方的接口,例如A要调用B,B在执行完又要调用A。
简单的举例来说:
小明想让小华帮自己做作业,于是小明就把自己的作业本给小华了,可是不知道小华是否给自己做了,于是要求小华两天后吧作业本归还。自己收到作业本上多了好多内容就证明小明帮自己做了。
1、作业本
作业本是一个接口,两者之间交流的纽带
package homework;
public interface Inote {
void finish();
void unFinish();
}
2、小明要做的事
小明就提供了个作业本(Inote)和待收信息(isFinish),然后自己预处理下,如果小华收到作业本又给我写好了我就谢谢他,如果收到作业本但是没给我写好我就安慰他没关系。
package homework;
public class DoHomeWork {
/**
*小明的作业
*/
public static void homeWorkofMing(boolean isFinish, Inote inote) {
if (inote!= null) {
// 处理回调
if (isFinish) {
inote.finish();
System.out.println("收到,谢谢你帮我完成!!!");
} else {
inote.unFinish();
System.out.println("收到 ,没完成也没关系!!!");
}
}
}
}
3、小华要做的事
小华给出作业完成了还是没完成回复
package homework;
public class Test {
public static void main(String[] args) {
DoHomeWork.homeWorkofMing(false, new Inote() {
@Override
public void finish() {
// 完成了时,传true
}
@Override
public void unFinish() {
// 此时触发接口中的unFinish方法,
System.out.println("抱歉我没完成");
}
});
}
}
六、安卓中的回调
view点击事件
Button btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "onClick: 1111");
Toast.makeText(TestActivity.this, "普通按钮", Toast.LENGTH_SHORT).show();
}
});
点击事件源码–View.java
...略
// View的内部类
static class ListenerInfo {
public OnClickListener mOnClickListener;
...略
}
...略
public void setOnClickListener(@Nullable OnClickListener l) {
// 检测控件的Clickabke 属性
if (!isClickable()) {
// 触发点击事件时吧Clickabke 设置为true
setClickable(true);
}
// 主要看这里:吧用户传递过来的接口实现对象赋值给本地成员变量(mOnClickListener)
getListenerInfo().mOnClickListener = l;
}
public interface OnClickListener {
/**
* 用户点击view时触发
*
* 参数:用户点击的view
*/
void onClick(View v);
}