Luckylau's Blog

Java基础之抽象类和接口

java的抽象类和接口

什么是java的抽象类和接口?

abstract classinterface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。 在面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是 这样。并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领 域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。而接口呢,它则是一组规则的集合,它规定了实现本接口的类必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。下面详细分析抽象类和接口以及它们之间的区别。

抽象类和接口的区别和联系?

语法层面上的区别:

1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;

2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;

3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;

4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

跨域层面上的区别:

​ 抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在”is-a” 关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。

设计层面上的区别:

抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。从动机角度,使用抽象类是很好的解决了代码的复用,而使用接口的却实现了多态性。

设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。

考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示:

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

其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢?

方案一:

简单的在Door的定义中增加一个alarm方法。

对于抽象类,所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能。

对于接口,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。

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

也就是说这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念”报警器”的行为方法混在了一起。

ISP(Interface Segregation Principle):面向对象的一个核心原则。它表明使用多个专门的接口比使用单一的总接口要好。
一个类对另外一个类的依赖性应当是建立在最小的接口上的。
一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。

方案二:

既然open()、close()和alarm()属于两个不同的概念,那么我们依据ISP原则将它们分开定义在两个代表两个不同概念的抽象类里面,一个使用抽象类定义,一个是用接口定义。

1
2
3
public interface Alarm{
void alarm();
}
1
2
3
4
public abstract class Door{
abstract void open();
abstract void close();
}
1
2
3
4
5
public class AlarmDoor extends Door implements Alarm{
void open(){}
void close(){}
void alarm(){}
}

抽象类和接口使用注意?

抽象类使用之前需要注意:

抽象类不能被实例化,实例化的工作应该交由它的子类来完成,它只需要有一个引用即可。

抽象方法必须由子类来进行重写,同时必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;。

只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含有其他方法。

抽象类中可以包含具体的方法,当然也可以不包含抽象方法。

子类中的抽象方法不能与父类的抽象方法同名。

abstract不能与final并列修饰同一个类。

abstract 不能与private、static、final或native并列修饰同一个方法。

接口使用之前需要注意:

接口的所有方法的访问权限自动被声明为public,而且只能是public。

接口中可以定义“成员变量”,或者说是不可变的常量,因为接口中的“成员变量”会自动变为为public static final。可以通过类命名直接访问:ImplementClass.name。

接口中不存在实现的方法。

实现接口的非抽象类必须要实现该接口的所有方法。抽象类可以不用实现。

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

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

抽象类的特殊使用?

抽象类使用static注意事项:

1.关于外部抽象类不能声明为static,例如将上述的Employee改为public static abstract class Employee {…},会报Illegal modifier for the class Employee; only public, abstract & final are permitted,但是内部抽象类可以。

1
2
3
4
5
public abstract class Employee {
public abstract static class Salary{
public abstract void printSalary();
}
}
1
2
3
4
5
6
7
public class Salesman extends Employee.Salary {
@Override
public void printSalary() {
// TODO Auto-generated method stub
System.out.println("Salesman's salary is $36000.00");
}
}
1
2
3
4
5
6
7
8
9
public class Demo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Salesman s = new Salesman();
s.printSalary();
Employee.Salary s2= new Salesman();
s.printSalary();
}
}

2.可以直接调用抽象类中用static声明的方法,就像普通类一样。

3.利用抽象类对用户隐藏不需要知道的子类。

1
2
3
4
5
6
7
8
9
10
11
public abstract class Employee {
public abstract void printSalary();
private static class Salesman extends Employee{
public void printSalary(){
System.out.println(" the Salary is $36000.00");
}
}
public static Employee getInstance(){
return new Salesman();
}
}
1
2
3
4
5
6
7
public class Demo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee s =Employee.getInstance();
s.printSalary();
}
}

抽象类常规示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public abstract class Employee {
private String name;
private String address;
private int telephone;
public Employee(String name, String address, int telephone)
{
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.telephone = telephone;
}
public double computePay()
{
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck()
{
System.out.println("Inside Employee mailCheck");
System.out.println("Mailing a check to " + this.name
+ " " + this.address);
}
public String toString()
{
return name + " " + address + " " + telephone;
}
abstract public void contactConsumer();
public String getName() {
return name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Salesman extends Employee {
private double salary;
public Salesman(String name, String address, int telephone,double salary) {
super(name, address, telephone);
// TODO Auto-generated constructor stub
this.salary =salary;
}
@Override
public void contactConsumer() {
// TODO Auto-generated method stub
System.out.println("Salesman contact Consumer sometimes");
}
public void mailCheck(){
System.out.println("Salesman mailCheck ");
System.out.println("Mailing check to " + getName()
+ " with salary " + salary);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Demo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Salesman s = new Salesman("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
System.out.println(s.computePay());
s.mailCheck();
System.out.println("===========================");
Employee e = new Salesman("John Adams", "Boston, MA", 2, 2400.00);
System.out.println(e.computePay());
e.mailCheck();
}
}
//output
Constructing an Employee
Inside Employee computePay
0.0
Salesman mailCheck
Mailing check to Mohd Mohtashim with salary 3600.0
===========================
Constructing an Employee
Inside Employee computePay
0.0
Salesman mailCheck
Mailing check to John Adams with salary 2400.0

Java8的接口新特性?

静态方法

static定义的方法用接口名调用

默认方法

default定义的普通方法用对象调用

1
2
3
4
5
6
7
8
9
public interface Flyanimal {
public void eat();
public static void fly() {
System.out.println("i can fly now");
}
default void run(){
System.out.println("i can run now");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Animal implements Flyanimal{
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("i am eating now ");
}
public static void main(String[] args) {
Flyanimal animal = new Animal();
animal.eat();
animal.run();
Flyanimal.fly();
}
}

参考:

http://blog.csdn.net/wenwen091100304/article/details/48381023

http://www.cnblogs.com/azai/archive/2009/11/10/1599584.html

http://www.runoob.com/java/java-abstraction.html

Luckylau wechat
如果对您有价值,看官可以打赏的!