Java编程思想(九、接口)
接口和内部类为我们提供了一种将接口和现实分离的更加结构化的方法。
1、抽象类和抽象方法。
下面是抽象方法声明所采用的语法:
abstract void f();
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。(否则,编译器便会报错。) 如果一个抽象类不完整,那么当我们试图产生该类的对象时,由于为抽象类创建对象是不安全的,所以我们会从编译器那里得到一条出错信息。这样,编译器会确保抽象类的纯粹性,我们不必担心会误用它。
如果从一个抽象类继承,并想创建该新类的对象。那么就必须为基类中的所有抽象方法提供方法定义。如果不这样做,那么导出类便也是抽象类,且编译器将会强制我们用abstract关键字来限定这个类。
h创建抽象类和抽象方法非常有用,因为它们可以使类的抽象性明确起来,并告诉用户和编译器打算怎样来使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易地将公共方法沿着继承层级结构向上移动。
2、接口。interface关键字使抽象的概念更向前迈进了一步。abstract关键字允许人们在类中创建一个或多个没有任何定义的方法---提供了接口部分,但是没有提供任何相应的具体实现。这些实现是由此类的继承者创建的。interface这个关键字产生一个完全抽象的类,它根本就没有提供任何具体实现。
可以选择在接口中显式地将方法声明为public的,但即使不这么做, 它们也是public的。因此,当要实现一个接口时,在接口中被定义的方法必须被定义为是public的。否则,它们将只能得到默认的包访问权限,这样在方法被继承的过程中,其可访问权限就被降低了,这是java编译器所不允许的。
在接口中的每一个方法确实都是一个声明,这是编译器所允许的在接口中唯一存在的事物。
3、完全解耦。只要一个方法操作的是类而非接口。那么你就只能使用这个类及其子类。如果你想要将这个方法应用于不在此继承结构中的某个类,那么就不行了。接口可以在很大程度上放宽这种限制。因此,它可以使得我们编写复用性更好的代码。
策略设计模式:创建一个能够根据所传递的参数对象的不同而具有不同行为的方法。这类方法包含所要执行的算法中固定不变的部分。而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。
class Processor{ public String name(){ return getClass().getSimpleName(); } Object process(Object input){ return input;} } class Upcase extends Processor{ String process(Object input){ return ((String)input).toUpperCase();} }
class Downcase extends Processor{
String process(Object input){ return ((String)input).toLowerCase();}
}
public class Apply{
public static void process(Processor p,Object s){
System.out.print(“Using Processor”+p.name());
System.out.print(p.process(s));
}
String s = "Abcd";
public static void main(String[] args){
process(new Upcase(),s);
process(new Downcase(),s)
}
}/Output:
Using Processor Upcase
ABCD
Using Processor DownCase
abcd
适配器设计模式:适配器中的代码将接受你所拥有的接口,并产生你所需要的接口。
class FilterAdapter impletemts Processor{ Filter filter; public FilterAdapter(Filter filter){ this.filter=filter; } }
在这种使用适配器的方式中,FilterAdapter的构造器接受你所有拥有的接口Filter,然后生成你所需要的Processor接口的对象。
4、Java中的多重继承。就是实现多个接口。这也是使用接口的核心原因,为了能够向上转型为多个基类型(以及由此带来的灵活性)。然后,使用接口的第二个原因却是与使用抽象基类相同:防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。但这就带来一个问题,我们应该使用接口还是抽象类?如果要创建不带任何方法和成员变量的基类,那么就应该选择接口而不是抽象类。
interface CanFight{ void fight(); } interface CanSwim{ void swim(); } class ActionCharacter{ public void fight(); } class Hero extends ActionCharacter implements CanFight,CanSwim{ public void swim(){} } public class Adventure{ public static void t(CanFight x) { x.fight(); } public static void u(CanSwim x) { x.swim(); } public static void w(ActionCharacter x) { x.fight(); } public static void main(String[] args){ Hero h = new Hero(); t(h); u(h); w(h); } }
5、通过继承来扩展接口。通过继承,可以很容易地在接口中添加新的/方法声明,还可以通过继承在新接口中组合数个接口。
组合接口时的名字冲突:可以重载方法,覆写方法写个@Override注解。尽量还是避免不同的接口中使用相同的方法名造成代码可读性的混乱。
6、适配接口。接口最吸引人的原因之一就是允许同一个接口具有多个不同的具体实现。在简单的情况中,它的体现形式通常是接受一个接口类型的方法,而该接口的实现和向该方法传递的对象则取决于方法的使用者。主要就是声明:“你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口”。
7、接口中的域。放入接口中的任何域都自动是static和final的。既然域是static的,它们就可以在类第一次被加载时初始化,这发生在任何域首次被访问时。当然,这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。
8、嵌套接口。接口可以嵌套在类或其他接口中。
9、接口与工厂。工厂方法设计模式非常好用,复用代码很方便,与直接调用构造器不同,我们在工厂对象上调用的是创建方法。该工厂对象将生成接口的某个实现的对象。