抽象:从具体事物抽出、概括出它们共同的方面、本质属性与关系等,而将个别的、非本质的方面、属性与关系舍弃,这种思维过程,称为抽象。
这句话概括了抽象的概念,而在Java中,你可以只给出方法的定义不去实现方法的具体事物,由子类去根据具体需求来具体实现。
抽象类除了包含抽象方法外,还可以包含具体的变量和具体的方法。类即使不包含抽象方法,也可以被声明为抽象类,防止被实例化。
抽象类不能被实例化,也就是不能使用new关键字来得到一个抽象类的实例,抽象方法必须在子类中被实现。
抽象类总结规定:
interface
关键字使得抽象的概念更加向前迈进了一步,abstract
关键字允许人们在类中创建一个或多个没有任何定义的方法---提供了接口部分。但是没有提供任何相应的具体实现,这些实现是由此类的继承者实现的。
在抽象类中,可以包含一个或多个抽象方法;但在接口(interface)中,所有的方法必须都是抽象的,不能有方法体,它比抽象类更加“抽象”。
接口使用 interface 关键字来声明,可以看做是一种特殊的抽象类,可以指定一个类必须做什么,而不是规定它如何去做。
与抽象类相比,接口有其自身的一些特性:
public abstract
的,因而在声明方法时可以省略这些修饰符。试图在接口中定义实例变量、非抽象的实例方法及静态方法,都是非法的接口与抽象类的区别:
接口作为系统和外界交互的窗口,接口体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式来提供);对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务(就是如何来调用方法)。当在一个程序中使用接口时,接口是多个模块间的耦合标准;当在多个应用程序之间使用接口时,接口是多个程序之间的通信标准。
从某种角度上来看,接口类似于整个系统的“总纲”,它制定了系统各模块之间应该遵循的标准,因此一个系统中的接口不应该经常改变。一旦接口改变,对整个系统而言甚至其他系统的影响将是辐射式的,导致系统中的大部分类都需要改写。所以,在一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。
抽象类则不一样,抽象类作为系统中多个子类的共同父类,它所体现的是模板式设计。抽象类作为多个子类的的抽象父类,可以被当成系统实现过程中的中间产品,这个产品已经实现了系统的部分功能(那些在抽象类中已经提供实现的方法),但这个产品依然不能当成最终产品,必须有更进一步的完善。
除此之外,接口和抽象类在用法上也存在如下区别:
接口里只能包含抽象方法,抽象类则可以包含普通方法。
接口里不能定义静态方法,抽象类里可以定义静态方法。
接口里不包含构造器,抽象类可以包含构造器。抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
接口里不能包含初始化块,但抽象类可以包含初始化块。
接口里只能定义静态常量,抽象类既可以定义普通变量,也可以定义静态常量。
接口可以可以继承多个接口,类只能继承一个类。
抽象类主要是用来抽象类别,接口主要是用来抽象方法功能。当关注事物的本质时,使用抽象类,当关注一种操作时,使用接口。
接口不仅仅是一种更加纯粹的抽象类,它的目标比这更高。因为接口中根本没有任何具体实现,所以没有任何与接口相关的存储,因此也就无法阻止多个接口的组合。在C++中,组合多个类的接口的行为叫做多重继承,但这可能会带来很多副作用,因为每个类都有一个具体实现。在Java中,可以执行一样的行为,但是只有一个类可以有具体实现,所以通过组合多个接口,C++的问题不会在Java中发生。
表达这样一个意思:“ x 从属于 a,也从属于 b,也从属于 c ”
使用接口的核心原因:
1).为了能够向上转型为多个基类型(以及由此带来的灵活性);
2).防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口(这与使用抽象基类原因相同)
这带来的一个问题是,应该使用接口还是抽象类?
如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。事实上,若知道某事物应该成为一个基类,那么第一选择应该是接口。
在实现多重继承时,会碰到一个小陷阱,在前面的例子中,CanFight和ActionCharacter都有一个相同的void fight()方法。问题不是它们方法相同,问题是,如果它们的签名(参数)或返回类型不同,会怎么样呢?
//: interfaces/InterfaceCollision.java package object; interface I1 { void f(); } interface I2 { int f(int i); } interface I3 { int f(); } class C { public int f() { return 1; } } class C2 implements I1, I2 { public void f() {} public int f(int i) { return 1; } // overloaded } class C3 extends C implements I2 { public int f(int i) { return 1; } // overloaded } class C4 extends C implements I3 { // Identical, no problem: public int f() { return 1; } } // Methods differ only by return type: //!class C5 extends C implements I1 {} --23 //! interface I4 extends I1, I3 {} ///:~ --24 I1, I3中f()返回值类型不一致 //class C5 extends C implements I1{ //实现的方法和积累方法命名相同,但方法的返回值不一样。 // int f(){ // return 0; // } //} // //interface I4 extends I1 , I3{ //重写的方法名相同,但是返回值不同。 // // @Override // void f(); //}
因为他们的方法名都相同,但是返回值不同,并不能实现方法重载。所以不能实现多重继承和组合接口。
接口最吸引人的原因之一就是允许同一个接口具有多种不同的实现。
接口最常见的用法就是使用策略设计模式。此时你编写一个执行某些操作的方法,而该方法将接受一个你指定的接口。你主要就是声明:“ 你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口。”
比如Java SE5的Scanner类,它的构造器接收的是一个Readable接口。
public Scanner(Readable source) { this(Objects.requireNonNull(source, "source"), WHITESPACE_PATTERN); } // Readable 是一个字符源。read方法的调用方能够通过 CharBuffer 使用 Readable 中的字符。 public interface Readable { // 将输入内容添加到CharBuffer参数中。 public int read(java.nio.CharBuffer cb) throws IOException; }
example1 : 实现Readable接口。
import java.io.IOException; import java.nio.CharBuffer; import java.util.Random; import java.util.Scanner; public class RandomWords implements Readable { private int count; public RandomWords(int count) { this.count = count; } private static Random random = new Random(47); private static final char[] capitals = "ABCDEFTHIGKLMNOPQRSTUVWXYZ".toCharArray(); private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray(); private static final char[] vowerls = "aeiou".toCharArray(); @Override public int read(CharBuffer cb) throws IOException { if (count-- == 0) { return -1; } cb.append(capitals[random.nextInt(capitals.length)]); for (int i = 0; i < 4; i++) { cb.append(vowerls[random.nextInt(vowerls.length)]); cb.append(lowers[random.nextInt(lowers.length)]); } cb.append(" "); return 10; } public static void main(String[] args) { @SuppressWarnings("resource") Scanner scanner = new Scanner(new RandomWords(10)); while (scanner.hasNext()) { System.out.println(scanner.next()); } } } /*output: Yazeruyac Fowenucor Toeazimom Raeuuacio Nuoadesiw Hageaikux Ruqicibui Numasetih Kuuuuozog Waqizeyoy */
example2 : 未实现Readable的类,就可以使用适配器+代理的方式
class RandomDoubles{ private static Random rand =new Random(47); public double next(){ return rand.nextDouble(); } } // --------------------------------------------------- import java.io.IOException; import java.nio.CharBuffer; import java.util.Random; import java.util.Scanner; public class Test { public static void main(String[] args) { Scanner s=new Scanner(new AdaptedRandomDoubles(7)); while(s.hasNext()){ System.out.println(s.next()); } } } class AdaptedRandomDoubles extends RandomDoubles implements Readable { private int count; public AdaptedRandomDoubles(int count){ this.count=count; } public int read(CharBuffer cb) throws IOException { if(count--==0){ return -1; } String result=Double.toString(this.next()); cb.append(result); return result.length(); } }
实例变量都是static final
在类中嵌套接口的语法是相当显而易见的,就像非嵌套接口一样,可以拥有public和“包访问”两种可视性。
class A { interface B { void f(); } public class BImp implements B { public void f() { } } private class BImp2 implements B { public void f() { } } public interface C { void f(); } class CImp implements C { public void f() { } } private class CImp2 implements C { public void f() { } } private interface D { void f(); } private class DImp implements D { public void f() { } } public class DImpl2 implements D { public void f() { } } public D getD() { return new DImpl2(); } private D dRef; public void receive(D d) { dRef = d; dRef.f(); } } interface E { interface G { void f(); } //Redundant "public" public interface H { void f(); } void g(); //cannot be private within an interface } public class NestingInterface { public class BImpl implements A.B { public void f() { } } class CImpl implements A.C { public void f() { } } // cannot implement a private interface // class DImpl implements A.D { // public void f() { // } // } class EImpl implements E { public void g() { } } class EImpl2 implements E.G { public void f() { } class EG implements E.G { public void f() { } } } public static void main(String[] args) { A a = new A(); A a2 = new A(); //Can't access A.D: 不能访问私有接口A.D //! A.D ad = a.getD(); //Doesn't return anything but A.D: 除了私有接口A.D,不能返回任何东西 //! A.DImp2 di2 = a.getD(); //返回回来的私有接口A.D, 不能向下转型为A.DImp2 //Cannot access a member of the interface: 不能访问私有接口A.D中的成员 //! a.getD().f(); //Only another A can do anything with getD(): 只有另一个A才能使用getD()做任何事 a2.receive(a.getD()); } }
interface E{ // 只能是默认或者public interface G { //默认为public void f(); } // Cannot be private within an interface: //! private interface I {} } class t2 implements E.G{ public void f() { } }
接口时实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式
通过工厂方法,接口和实现完全分离,可以非常方便的更改实现。
interface Service // Service接口,可以有多种实现 { void method1(); void method2(); } interface ServiceFactory // 工厂接口,可以由多种实现 { Service getService(); } class Implementation1 implements Service { //Service接口的实现1 public Implementation1() { } public void method1() { System.out.println("Implementation1 method1"); } public void method2() { System.out.println("Implementation1 method2"); } } class Implementation1Factory implements ServiceFactory{ //生成对象1的工厂1 public Service getService() { return new Implementation1(); } } class Implementation2 implements Service { // Service接口的实现2 public Implementation2() { } public void method1() { System.out.println("Implementation2 method1"); } public void method2() { System.out.println("Implementation2 method2"); } } class Implementation2Factory implements ServiceFactory{//生成对象2的工厂2 public Service getService() { return new Implementation1(); } } public class Factories { //使用service的模块 public static void serviceConsumer(ServiceFactory fact) { Service s = fact.getService(); //向上造型,工厂将生成某类实现接口的对象 s.method1(); s.method2(); } public static void main(String[] args) { serviceConsumer(new Implementation1Factory()); //serviceConsumer(new Implementation2Factory());很方便就可以更改实现 } } /*output: Implementation1 method1 Implementation1 method2 Implementation2 method1 Implementation2 method2 */
匿名内部类改进
interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } class Implementation1 implements Service { private Implementation1() { } public void method1() { System.out.println("Implementation1 method1"); } public void method2() { System.out.println("Implementation1 method2"); } public static ServiceFactory factory = new ServiceFactory() { public Service getService() { return new Implementation1(); } }; } class Implementation2 implements Service { private Implementation2() { } public void method1() { System.out.println("Implementation1 method1"); } public void method2() { System.out.println("Implementation1 method2"); } public static ServiceFactory factory = new ServiceFactory() { public Service getService() { return new Implementation2(); } }; } public class Factories { public static void serviceConsumer(ServiceFactory fact) { Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args) { serviceConsumer(Implementation1.factory); serviceConsumer(Implementation2.factory); } }
优先选择类而不是接口。从类开始,如果接口的必需性变得非常明确,那么就进行重构。