深入理解接口与抽象类

接口与抽象类


接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。

       在Java的继承、封装、多态抽象四大特征中,抽象作为面向对象编程的一大特征,是非常重要的组成部分。而抽象类又是抽象的典型代表,接下来将深入剖析Java中的抽象类,同时了解Java中抽象类与接口的区别,与其各自运用的环境。本文将按照下面顺序依次阐述:

一、抽象类

二、接口

三、接口与抽象类区别

四、抽象类与接口的运用环境

一、抽象类

定义: 抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。


直白解释:抽象类是一个超类,声明了一个给定结构的抽象内容,但不提供每个方法的具体实现。


抽象类的声明:在Java中,要声明一个类是抽象类,只需要在类声明的开始处class关键词前使用abstract关键词。


抽象类的定义形式:

	abstract  class  类名{
  		 类体
   	}

例子:

	public abstract class ClassName {

 	   abstract void f () ;
	}

抽象方法声明格式:

	abstract void f ();

注意点:

1、包括一个或多个抽象方法的类必须被声明为抽象的。抽象类中可以没有抽象方法(此时定义为抽象类没有任何意义),但含有抽象方法的类必须是抽象类。


2、抽象类不能用来创建对象,即抽象类不能通过new运算符直接实例化,因为在面向对象领域由于抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能实例化的。


3、抽象类中并非只能有抽象方法,他和普通类一样,同样可以拥有普通成员变量和成员方法


4、抽象方法必须由继承他的子类来进行重写,即一个类继承抽象类则必须要实现父类中的抽象方法。否则,必须将该类也声明为抽象类,再交给它的子类来进行实现。


5、子类抽象方法名不能跟父类抽象方法同名。


6、abstract 不能与final 并列修饰同一个类,因为用final来修饰的类是不可变类,不可以被继承,而由abstract修饰的抽象类必须由子类来实现,所以会产生矛盾。


7、abstract 不能与private 、static 、final或 native 并列修饰同一个方法。当static 修饰方法时,表明这个方法属于当前类,如果该方法又被定义为抽象方法,既调用了一个没有方法体的方法,肯定会引发错误。而如果一个方法被定义为private 的话,它将无法被重写,所以这个方法也就永远不会有方法体。


8、抽象方法必须为public 或者protected (因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public.


9、抽象方法与空方法:public abstract void test();  为抽象方法,public void test(); 是一个普通方法,已经定义了方法体,只是方法体为空。


10、抽象类里定义的普通初始化块,只有当其子类创建实例时才被调用。


举例:

UML图:


                 


源代码:

抽象父类

public abstract class Car {
		
		abstract void openDoor();
		
		abstract void startUp();
	}
子类1

public class Bwm extends Car{

		@Override
		void openDoor() {
		
			System.out.println("Bwm open the door !");
		}

		@Override
		void startUp() {
			
			System.out.println("Bwm start up !");
		}
	}

子类2

public class BYD extends Car{

		@Override
		void openDoor() {
			
			System.out.println("BYD open the door !");
		}

		@Override
		void startUp() {
				
			System.out.println("BYD start up !");
		}
		
	}

运行端:

public class Client1 {

	static void openDoor(Car car){
		
		car.openDoor();
	}
	
	static void stratUp(Car car){
		
		car.startUp();
	}
	
	
	public static void main(String[] args) {
		
		Car bwm = new Bwm();
		Car byd = new BYD();
		
		openDoor(bwm);
		stratUp(bwm);
		
		System.out.println("---------------------------"); //分割线
		openDoor(byd);
		stratUp(byd);
		
	}

}

运行结果:

Bwm open the door !
Bwm start up !
---------------------------
BYD open the door !
BYD start up !

二、接口

定义:接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。


简单解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。


接口的声明:使用interface来定义一个接口。接口定义同类的定义类似,也是分为接口的声明和接口体,其中接口体由常量定义和方法定义两部分组成。

      

定义接口的基本格式如下:

[可见度] interface 接口名称 [extends 其他的类名] {
        // 声明变量
        // 抽象方法
}

public interface NameOfInterface
{
   //任何类型 final, static 字段
   //抽象方法
}

实例:

interface Animal {

   public void eat();
   public void travel();
}

接口特性:

·  接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。

·  接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键子。

·  接口中的方法都是公有的。


注意点:

1修饰符:用于指定接口的访问权限,可选值为public(当将接口嵌套在类中是可以用private修饰)。如果省略则使用默认的访问权限。


2、接口如果跟extends关键字一起使用需要放在extends后面,即 [修饰词]class类名 extends父类 implments接口。反过来会发生错误。


3、在不给出修饰词的情况下,接口中的成员变量默认修饰词为 public static final,而被final修饰的变量都为常量,且需要被赋初值。


4不能使用new操作符实例化一个接口,但可以声明一个接口变量,该变量必须引用(refer to)一个实现该接口的类的对象。可以使用 instanceof检查一个对象是否实现了某个特定的接口。例如:if(Object instanceof Comparable){}


5、在实现多接口的时候一定要避免方法名的重复。

 

举例:

下面通过interface 来实现之前用抽象类所实现的汽车的例子。

 

接口:

public interface Car_interface {

 

        abstractvoid openDoor();

        abstractvoid startUp();

}


实现类1:

public class Bwm implementsCar_interface{

 

        @Override

        publicvoid openDoor() {

       

               System.out.println("Bwmopen the door !");

        }

        @Override

        publicvoid startUp() {

              

               System.out.println("Bwmstart up !");

        }

}

 

实现类2

 

public class BYD implementsCar_interface{

 

        @Override

        publicvoid openDoor() {

              

               System.out.println("BYDopen the door !");

        }

        @Override

        publicvoid startUp() {

                      

               System.out.println("BYDstart up !");

        }

       

}


客户端:

public class Client1 {

 

        staticvoid openDoor(Car_interface car){

              

               car.openDoor();

        }     

        staticvoid stratUp(Car_interface car){

              

               car.startUp();

        }      

        public static void main(String[] args) {

              

               Car_interfacebwm = new Bwm();

               Car_interfacebyd = new BYD();

              

               openDoor(bwm);

               stratUp(bwm);

              

               System.out.println("---------------------------");

               openDoor(byd);

               stratUp(byd);

              

        }

 

}


运行结果:

 

Bwm open the door !

Bwm start up !

---------------------------

BYD open the door !

BYD start up !

 

三、接口与抽象类的区别


1、首先从他们的语法层面上来说明
public abstract class Car {
	
	abstract void openDoor();
	
	abstract void startUp();
	
	void f(){
		System.out.println("抽象类的默认方法!");
	}
}

public interface Car_interface {

	abstract void openDoor();
	abstract void startUp();
}
  • 在接口中可以拥有自己的成员方法即普通成员变量,而在接口中则只能有抽象方法和常量(由final修饰)。
  • 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法。
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

2、从设计层面上来说的话
抽象类是对类的抽象,一般需要知道子类的需求,进而才能更好的设计抽象类中的抽象方法,比如设计一个小车的抽象类就要搞明白哪些方法是所有子类所共同需的,而哪些需求是个别子类所独特具有的。
子类所共同需求的,我们可以将它定义在抽象类中,而个别子类的特殊需求则可以考虑定义在接口中。

下面看一个网上流传最广泛的例子:门和警报的例子:门都有open( )和close( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:

1
2
3
4
abstract  class  Door {
     public  abstract  void  open();
     public  abstract  void  close();
}

  或者:

1
2
3
4
interface  Door {
     public  abstract  void  open();
     public  abstract  void  close();
}

但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:

  1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;

  2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。

  从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface  Alram {
     void  alarm();
}
 
abstract  class  Door {
     void  open();
     void  close();
}
 
class  AlarmDoor  extends  Door  implements  Alarm {
     void  oepn() {
       //....
     }
     void  close() {
       //....
     }
     void  alarm() {
       //....
     }
}


四、抽象类与接口的运用环境


interface和abstract class的比较及选择参照:

1. interface和abstract class都不能实例化,但可以声明引用变量,abstract class中子类必须对父类的抽象方法进行重写


2. 如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。


3. 如果知道某个类应该是基类,那么第一个选择的应该是让它成为一个接口,只有在必须要有方法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在一个或多个被具体实现的方法,只要方法没有被全部实现该类就仍是抽象类。


4、恰当的原则应该是优先选择类而不是接口。从类开始,如果接口的必需性变得非常明确,那么就进行重构。接口是一种重要的工具,但是它们容易被滥用。



参考资料:《thinking in java》
http://www.cnblogs.com/dolphin0520/p/3811437.html
http://blog.csdn.net/chenssy/article/details/12858267
http://blog.csdn.net/sunzuqiang/article/details/5621334
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值