接口的基本定义
抽象类与普通类的最大优势在于:可以实现对子类覆写方法的控制,但是在抽象类里面也会有普通方法,而普通方法里面可能会涉及到一些安全或者隐私的操作问题。那么这样在进行开发的过程当中,如果想对外部隐藏全部的开发细节,则就可以通过接口来进行描述。
接口可以理解为一个纯粹的抽象类(最原始的定义接口之中只包含抽象方法与全局常量的), 但是从1.8开始,由于引入了Lambda表达式的概念,接口的定义也得到了加强,除了抽象方法与全局常量之外,还可以定义普通方法或静态方法。如果从设计本身的角度来讲,接口之后的组成还是应该以抽象方法和全局常量为主。
在java中接口主要使用interface关键字来定义。
定义一个接口
1interface IMessage{ //定义了一个接口2 public static final String INFO = "Hello"; //全局常量3 public abstract String getInfo(); //定义了一个抽象方法4}
但是明显的问题出现了,此时的接口可你当是无法直接产生实例化对象,所以对于接口的使用: 接口需要被子类实现(implements ),一个子类可以实现多个父接口; 子类(如果不是抽象类)那么一定要覆写接口之中的全部抽象方法。 接口对象可以利用子类对象的向上转型进行实例化。
定义接口子类
1interface IMessage{ //定义了一个接口2 public static final String INFO = "Hello"; //全局常量3 public abstract String getInfo(); //定义了一个抽象方法4}5class MessageImpl implements IMessage{6 public String getInfo(){7 return "秘密消息";8 }9}10public class Inter{11 public static void main(String args []){12 IMessage msg = new MessageImpl();13 System.out.println(msg.getInfo());14 System.out.println(IMessage.INFO);15
2 collapsed lines
16 }17}
子类实现多个父接口
1interface IMessage{ //定义了一个接口2 public static final String INFO = "Hello"; //全局常量3 public abstract String getInfo(); //定义抽象方法4}5interface IChannel{6 public abstract boolean connect(); //定义抽象方法7}8class MessageImpl implements IMessage,IChannel{9 public String getInfo(){10 if(this.connect()){11 return "发送了一个秘密消息";12 }13 return "通道创建失败,消息发送失败";14 }15
11 collapsed lines
16 public boolean connect(){17 System.out.println("消息发送通道,成功建立");18 return true;19 }20}21public class Inter{22 public static void main(String args []){23 IMessage msg = new MessageImpl();24 System.out.println(msg.getInfo());25 }26}
注意:此时MessageImpl子类的对象可以任意的实现父接口的转换。
观察转换:
1public class Inter{2 public static void main(String args []){3 IMessage msg = new MessageImpl();4 IChannel chl = (IChannel) msg;5 System.out.println(chl.connect());6 }7}
注意: 由于MessageImpl子类实现了IMessage与IChannel两个接口,所以这个儿子类可以是这两个接口任意一个接口的实例, 那么就表示这两个接口实例之间是可以相互转换的。
在java程序里面接口是不允许继承父类的, 所以接口绝对不会是Object的子类,但是MessageImpl是Object的子类,所以接口一定可以通过Object接收。
观察: Object与接口的转换:
1public static void main(String args []){2 IMessage msg = new MessageImpl();3 Object obj = msg; //向上转型4 IChannel chan = (IChannel) obj;5 System.out.println(chan.connect());6}
注意: 接口中的方法默认是public 写与不写是一样的,建议写,方法上的abstract 写不写也是一样的,可以不写。
子类继承抽象类并且实现接口
1interface IMessage{ //定义了一个接口2 public static final String INFO = "Hello"; //全局常量3 public abstract String getInfo(); //定义抽象方法4 // String INFO = "Hello"; // 跟上面的效果是一样的5 // public abstract String getInfo(); // 跟上面的效果是一样的6
7}8interface IChannel{9 public abstract boolean connect(); //定义抽象方法10}11
12abstract class DatabaseAbstract{ //定义抽象类13 //接口中的abstract可以省略,抽象类中不允许省略14 public abstract boolean getDatabaseConnection();15}27 collapsed lines
16class MessageImpl extends DatabaseAbstract implements IMessage,IChannel{17 public String getInfo(){18 if(this.connect()){19 if(this.getDatabaseConnection()){20 return "从数据库中获取消息, 发送了一个秘密消息";21 }else {22 return "数据库中获取消息失败";23 }24
25 }26 return "通道创建失败,消息发送失败";27 }28
29 public boolean connect(){30 System.out.println("消息发送通道,成功建立");31 return true;32 }33 public boolean getDatabaseConnection(){34 return true;35 }36}37public class Inter{38 public static void main(String args []){39 IMessage msg = new MessageImpl();40 System.out.println(msg.getInfo());41 }42}
实现接口的多继承
1interface IMessage{ //定义了一个接口2 boolean getInfo();3
4}5interface IChannel{6 boolean connect(); //定义抽象方法7}8
9interface IService extends IMessage, IChannel{10 boolean start();11}12
13class MessageService implements IService{14 public boolean getInfo(){return true;}15 public boolean connect(){return true;}7 collapsed lines
16 public boolean start(){return true;}17}18
19public class Inter{20 public static void main(String args []){21 }22}
口定义加强
接口的主要特点是全部由抽象方法,全局常量组成。但是如果项目设计不当,则会出现严重的问题。比如后期给接口新增加了一个方法,则所有实现的子类都需要覆写新增加的方法。 为了方便子类的修改,往往不会让子类直接实现接口,而是中间追加一个过渡的抽象类。
但是版本1.8之后,为了解决接口设计的缺陷,所以在接口之中允许开发者定义普通方法。
范例:观察接口中的普通方法定义:
1interface IMessage{ //定义了一个接口2 public String message();3 public default boolean connect(){ //必须添加default4 System.out.println("消息通道建立成功");5 return true;6 }7
8}9
10class MessageImpl implements IMessage{11 public String message(){12 return "www.cccc.com";13 }14}15
8 collapsed lines
16public class Inter{17 public static void main(String args []){18 IMessage msg = new MessageImpl();19 if(msg.connect()){20 System.out.println(msg.message());21 }22 }23}
接口中的普通方法必须追加default的声明, 但是这个是补就的方法,平常不建议使用。除了可以追加普通方法之外, 接口里面也可以定义static方法了,而static方法就可以通过接口会直接调用static方法。
工厂设计模式
对于接口而言,已经清楚其必须有子类,并且子类可以通过对象向上转型来获取接口的实例化对象,但是在进行对象实例化的过程当中,也可能存在设计问题。 范例:
1interface IFood{2 public void eat();3}4class Bread implements IFood{5 public void eat(){6 System.out.println("吃面包");7 }8}9class Milk implements IFood{10 public void eat(){11 System.out.println("喝牛奶");12 }13}14
15class Factory{16 collapsed lines
16 public static IFood getInstance(String foodName){17 if("bread".equals(foodName)){18 return new Bread();19 }else if ("milk".equals(foodName)){20 return new Milk();21 }else {22 return null;23 }24 }25}26public class Inter{27 public static void main(String args []){28 IFood food = Factory.getInstance(args[0]);29 food.eat();30 }31}
代理设计模式
代理设计模式的主要功能是可以帮助用户将所有的开发注意力只集中在核心业务功能上。
1interface IEat{2 public void get();3}4class EatReal implements IEat{5 public void get(){6 System.out.println("【真实主题】得到一份食物,然后开始慢慢品尝");7 }8}9class EatProxy implements IEat{10 private IEat eat;11 public EatProxy(IEat eat){12 this.eat = eat;13 }14 public void prepare(){15 System.out.println("【代理主题】1.精心的购买食材");18 collapsed lines
16 System.out.println("【代理主题】2.小心的处理食材");17
18 }19 public void get(){20 this.prepare();21 this.eat.get();22 this.clear();23 }24 public void clear(){25 System.out.println("【代理主题】4.清理碗筷");26 }27}28public class Inter{29 public static void main(String args []){30 IEat eat = new EatProxy(new EatReal());31 eat.get();32 }33}
抽象类与接口的区别
No | 区别 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义 | abstract class 抽象类名称{} | interface 接口名称{} |
2 | 组成 | 构造,普通方法,静态方法,全局常量,普通成员,static方法 | 抽象方法,全局常量,普通方法,static方法 |
3 | 权限 | 可以使用各种权限定义 | 只能使用public |
4 | 子类使用 | 子类通过extends关键字可以成一个抽象类 | 子类使用implements 可以实现多个接口 |
5 | 两者关系 | 抽象类可以实现若干个接口 | 接口不允许继承抽象类,但是允许继承多个父接口 |
6 | 使用 | 1. 抽象类或接口必须定义子类 2. 子类一定 要覆写抽象类或接口中的全部抽象方法 3. 通过子类向上转型实现抽象类或接口对象实例化 |