Java编程——对于接口的理解

接口常量和抽象方法的集合,是所有实现接口的子类的“协议”和“规范”。

既然接口是常量和抽象方法的集合,那么咱们先来看看抽象类及其抽象方法和非抽象方法。下面来看Demo1。

Demo1:Animal类的常规类
无测试类

package com.shenqi.xiaobaiyang;
/*
 * (父类Animal中描述动物类型的变量type
和	一个输出动物叫声信息的方法sound()。
 */
public class Animal {
	private String type;
	
	public Animal(String type){
		this.type = type;
	}
	
	public String toString(){
		/*
		 * toString()方法
		 * 在Object类里面定义toString()方法的时候返回的对象
		的哈希code码(对象地址字符串)。
			可以通过重写toString()方法来表示出对象的属性。
		 */
		return "这是一只" + type;
	}
	
	public void sound(){
		System.out.println("不知道是什么声音...");
	}
}
package com.shenqi.xiaobaiyang;

public class Cat extends Animal {
	public Cat(String type){
		super(type);//对父类的构造方法进行初始化
	}
	
	public void sound(){
		System.out.println("喵    喵     喵...");
	}
	
	
}
package com.shenqi.xiaobaiyang;

public class Duck extends Animal {

	public Duck(String type) {
		super(type);
	}
	
	public void sound(){
		System.out.println("嘎     嘎     嘎...");
	}

}

分析:看父类Animal类中的sound()方法,发现每个子类都重写了父类的Animal中sound()方法,即各子类都没有采用父类该方法的具体实现。

实际上,一个抽象的动物是没有具体的叫声的,也就是说对于Animal类,给出的sound()方法的具体实现本身是没有任何意义的。父类的方法只是充当了一个“占位符”的作用,其作用在于确保子类一定拥有一个sound()方法。说白了,就是只是为子类提供了一个框架,而把方法的具体实现下放到每个具体子类中】。


Demo2Animal类的抽象类

Animal类的抽象类与Animal类的常规类:之所以可以使用抽象类来替代常规类,是因为事实上,一个抽象的东西是没有具体的叫声的。在常规类中,Animal类,给出的sound()方法的具体实现本身是没有任何意义的。父类的方法只是充当了一个“占位符”的作用,其作用在于确保子类一定拥有一个sound()方法。

现在使用抽象方法和抽象类改写Animal类。

package com.qiji.xiaobaiyang;

 abstract class Animal {
	 private String type;
	 
	 public Animal(String type){
		 this.type = type;
	 }
	 
	 public String toString(){
		 return "这是一只" + type;
	 }
	 
	 public abstract void sound();
 }

	 
package com.qiji.xiaobaiyang;

public class Cat extends Animal {
	public Cat(String type){
		super(type);//对父类的构造方法进行初始化
	}
	
	public void sound(){
		System.out.println("喵    喵     喵...");
	}
	
	
}
package com.qiji.xiaobaiyang;

public class Duck extends Animal {

	public Duck(String type) {
		super(type);
	}
	
	public void sound(){
		System.out.println("嘎     嘎     嘎...");
	}

}

程序中把sound()方法定义成了抽象方法。

抽象方法是指只有方法头部定义,没有方法实现体的方法,定义为抽象方法时需要在方法中加入abstract关键字。包含有抽象方法的类必须被定义为抽象类。

知识点:

①     定义为抽象方法和抽象类的基本格式如下:

【限定修饰符】abstract class类名{

         【限定修饰符】abstractvoid方法名();

}

②      使用抽象方法与抽象类时应遵循的相应规则

    1. 任何包含抽象方法的类必须被定义为抽象类,抽象类中也可包含非抽象方法常规类中除构造方法以外的所有方法为非抽象方法,甚至没有包含抽象方法的类也能定义为抽象类。

    2.抽象类不可以被实例化。如下述代码:

Animalanimal = new Animal();

抽象类之所以不能被实例化为对象是因为对象必须具有具体的属性(变量)状态和行为(方法)。

     3.虽然抽象类不能被实例化,但可以将抽象类的非抽象子类的实例对象赋给抽象类类型的引用,并通过该引用调用子类的相应方法。如下述代码:

package com.qiji.xiaobaiyang;

public class AnimalText {
	public static void callSound(Animal animal){//方法参数为Animal类型
		animal.sound();
	}
	public static void main(String[] args) {
		Cat cat = new Cat("宠物猫");
		Duck duck = new Duck("小鸭子");
		System.out.println(cat);
		callSound(cat);//调用时实参为子类实例化对象
		System.out.println(duck);
		callSound(duck);//调用时实参为子类实例化对象
		
	}

}

 注意:    

    1.若子类没有实现抽象父类中的所有抽象方法,则子类也必须被定义为抽象类,并且不能被实例化。反之,子类就是一个具体类,能被实例化。

    2.static、private、final方法不能是抽象的,因为这些类型的方法是不能被覆盖的。


     使用抽象类与抽象方法的优势【相对于常规类】:能够将实现下放到具体子类中,进一步实现了类的接口和实现的分离,能够有效地提高程序的扩展性。

接下来,来看接口

1. 在Java中,一个类只能够有一个直接的父类,但是一个类可以实现多个接口,Java采用这种方式实现多继承;

 2. 接口的概念:

     a.接口明确地描述了系统对外提供的所有服务,清晰地把系统的实现细节与接口分离,系统的实现细节由接口的实现类负责实现,接口负责对外描述系统提供的服务,对内描述系统应该具备的功能;

     b.接口和抽象类都不能够被实例化,但是抽象类中可以包含具体的实现,这样可以提高代码的重用性,而接口不能包含任何具体的实现;

  3.接口的特点

    (1)接口中的成员变量必须全部是public,static,final类型(编译时常量),必须被显式地初始化;

    (2)接口中的方法必须全部是public,abstract类型;

            综合(1)(2)两点,接口中不能够出现:

             A.实例变量(因为对象中只保存实例变量,而接口不能够有实例,因此没有实例变量)

             B.非抽象的实例方法

             C.静态方法

     (3)接口中没有构造方法,因此不能够创建接口的实例对象;

    (4)一个接口不能够实现另外一个接口,只能够继承另外多个接口(类可以实现多个接口,接口可以继承多个接口),如果接口C同时继承接口A和B,则接口C成为复合接口;

    (5)接口必须通过类实现它的抽象方法,当类实现某个接口时,它必须实现接口中所有的抽象方法,否则这个类必须被定义为抽象类

    (6)不能够创建接口的实例,但可以创建接口类型的引用变量,该变量可以引用实现这个接口的类的实例


    4.抽象类与接口

     4.1  相同点

     (1)抽象类和接口都位于继承树的上层;

             这里有一个设计思想:当一个系统(一个类)使用一棵继承树上的类时,应该尽可能地把引用变量声明为继承树的上层抽象类型,引用变量最好声明为接口类型,因为接口是作为系统和外界的交互窗口,这样的好处是可以实现两个系统之间的松耦合;

     (2)抽象类和接口都不能够被实例化;

      (3)抽象类和接口都可以包含抽象方法;

     4.2 不同点

      (1)抽象类中可以为部分方法提供默认的实现,从而避免在子类中重复实现这些方法,提高代码的可重用性,而接口中只能够定义抽象的方法

     这里有一个扩展性的问题,抽象类比较容易被扩展,因为可以在抽象类中加入具体的方法,加入具体的方法并不会影响到它的任何子类,子类默默继承了这些方法,而对于接口而言,一旦接口被公布,就不能随意在接口中添加方法,因为这样,实现接口的类需要实现新加入的方法,否则需要将类定义为抽象类

     (2)一个类只能够继承一个直接的父类,这个父类有可能是抽象类,但是一个类可以实现多个接口

         原理思想:接口中只有抽象方法,只有接口的实现类会实现接口的抽象方法,一个类即使有多个接口,也不会增加JVM进行动态绑定的复杂度,因为JVM不会把方法与接口绑定,只会把方法和实现类绑定。

      (3)Java程序的设计思想:

       对于一棵已经存在的继承树,可以方便地从类中抽象出新的接口,接口更有利于软件系统的维护和重构,对于一棵继承树而言,如果B继承A,B和C提供一种相同的服务S,这样如果将B和C共同的服务抽象成为抽象类O,因为Java不允许多继承,需要A继承类O,这样A就需要实现O中定义的服务S,否则需要将A定义成为抽象类,这显然是不合理的。

       因此这时需要接口,将服务S抽象出来封到一个接口中,让B和C都实现这个接口,这样不会影响到其他类

       (4)一种解决方案是:

        对已经存在的系统进行自下而上的抽象时,需要借助于接口,对于任何两个类,不管它们的类型是否相同,只要它们存在相同的功能,提供相同的服务,就可以将这个服务抽象出来,封装到一个接口中,让这两个类都实现这个接口,这样,这两个类就可以对外提供服务了。

      (5)一种Java程序的设计原则是:

        系统之间的耦合都通过接口实现,即将引用变量定义成为接口类型,这样可以保证系统之间的松耦合(继承树的上层抽象类型)

    4.3 何时使用抽象类??何时使用接口??

       (1)将接口作为系统与外界交互的窗口,对外,接口向使用者说明系统能够提供的服务,对内,接口指定系统必须实现的服务,

   下面这句话很重要:

       接口是一个系统中最高层次的抽象类型,这里的系统可以是一个很大的系统,也可以是一个局部系统,任何系统之间,都应该通过接口进行交互,这样可以实现系统之间的松耦合。

        (2)外界只能够通过接口访问系统提供的服务,而不能够修改接口,接口一旦定义,不要轻易修改,否则对外对内都会造成很大的影响。

        (3)抽象类可以作为系统的扩展点,将抽象类视为介于“抽象”和“实现”之间的半成品,它力所能及地完成了部分功能,还有一些功能有待于它的子类去实现。

          例如一个系统A中包括接口i和抽象类1,系统B中包括四个类abcd,系统B中的类通过与接口i建立组合和依赖关系,访问系统A中的服务,通过继承系统A中的抽象类1,扩展系统A的功能

         当一个软件系统对外发布时,会在文档中说明哪些接口允许使用者实现,哪些类允许被继承;

         时刻牢记,系统之间通过接口进行耦合,访问系统提供的服务,通过继承抽象类实现系统功能的扩展。 


抽象类与接口的对比 

参数

抽象类

接口

默认的方法实现

它可以有默认的方法实现

接口完全是抽象的。它根本不存在方法的实现

实现

子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。

子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现。

构造器

抽象类可以有构造器

接口不能有构造器

与常规类的区别

除了你不能实例化抽象类之外,它和常规JAVA类没有任何区别

接口是完全不同的类型

访问修饰符

抽象方法可以有public、protected和default这些修饰符

接口方法默认修饰符是public。你不可以使用其他修饰符

main方法

抽象方法可以有main方法并且我们可以运行它

接口没有main方法,因此我们不能运行它

多继承

抽象方法可以继承一个类和实现多个接口

接口只可以继承一个或多个其它接口

速度

它比接口速度要快

接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法

添加新方法

如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码

如果你往接口中添加方法,那么你必须改变实现该接口的类。

 

说了这么多,看一个实例吧。

Demo3 接口

package com.shenqiqiji.xiaobaiyang;

public interface Qiji {
	final float PI=3.14159f;//定义用于表示圆周率的常量PI  
	float getArea(float r);//定义一个用于计算面积的方法getArea()  
	float getCircumference(float r);//定义一个用于计算周长的方法getCircumference()  
	
	//与Java的类文件一样,接口文件的文件名必须与接口名相同。
}
package com.shenqiqiji.xiaobaiyang;

public class Cire implements Qiji {

	@Override
	public float getArea(float r) {
		float area=PI*r*r;	//计算圆面积并赋值给变量area  
		return area;		//返回计算后的圆面积  
	}

	@Override
	public float getCircumference(float r) {
		//计算圆周长并赋值给变量circumference  
		float circumference=2*PI*r; 
		//返回计算后的圆周长  
		return circumference;       
	}
	
	public static void main(String[] args)   {  
		 Cire c = new Cire();
		 //控制格式化输出
		 float f = c.getArea(2.0f);  
		 System.out.println(Float.toString(f));  
    }  

}

猜你喜欢

转载自blog.csdn.net/shenqixiayang/article/details/79326284