在面向对象中,所有的对象都是用类来描述的,但是并不是多有的类都可以描述所有对象,如果一个类没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其他功能都在,成员方法、成员变量和构造方法都是可以存在的。
抽象类必须被继承才能被使用。
抽象类
使用abstract class来定义抽象类。
public abstract class Employee
{
private String name;
private String address;
private int number;
public Employee(String name, String address, int number)
{
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay()
{
System.out.println("Inside Employee computePay");
return 0.0;
}
public void mailCheck()
{
System.out.println("Mailing a check to " + this.name
+ " " + this.address);
}
public String toString()
{
return name + " " + address + " " + number;
}
public String getName()
{
return name;
}
public String getAddress()
{
return address;
}
public void setAddress(String newAddress)
{
address = newAddress;
}
public int getNumber()
{
return number;
}
}
public class AbstractDemo
{
public static void main(String [] args)
{
/* 以下是不允许的,会引发错误 */
Employee e = new Employee("George W.", "Houston, TX", 43);
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
当尝试编译AbstractDemo类是,会产生如下错误:
Employee.java:46: Employee is abstract; cannot be instantiated
Employee e = new Employee("George W.", "Houston, TX", 43);
^
1 error
继承抽象类
通过一般的方法继承Employee类
public class Salary extends Employee
{
private double salary; //Annual salary
public Salary(String name, String address, int number, double
salary)
{
super(name, address, number);
setSalary(salary);
}
public void mailCheck()
{
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName()
+ " with salary " + salary);
}
public double getSalary()
{
return salary;
}
public void setSalary(double newSalary)
{
if(newSalary >= 0.0)
{
salary = newSalary;
}
}
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
}
尽管不能实例化Employee类,但是可以实例化它的子类,即实例化一个Salary对象,该对象将从Employee类继承3个成员变量和7个成员方法。
public class AbstractDemo
{
public static void main(String [] args)
{
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("Call mailCheck using Salary reference --");
s.mailCheck();
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
输出:
Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0
Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.
抽象方法public class Salary extends Employee
{
private double salary; // Annual salary
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
//其余代码
}
public class Salary extends Employee
{
private double salary; // Annual salary
public double computePay()
{
System.out.println("Computing salary pay for " + getName());
return salary/52;
}
//其余代码
}
public abstract class Employee
{
private String name;
private String address;
private int number;
public abstract double computePay();
//其余代码
}
- 如果一个类包含抽象方法,那么这个类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
继承抽象方法的子类必须重写该方法,否则,子类也必须声明为抽象方法。最终,必须有子类实现抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
如果Salary类继承了Employee类,那么它必须实现computePay()方法:
接口
一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口和类很类似,但是接口并不是类,类描述对象的属性和方法,接口则包含类要实现的方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。
接口与类的相似点:
- 一个接口可以有多个方法。
- 接口文件保存在.java结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在.class结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象
- 接口没有构造方法
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了static和final变量。
- 接口不是被类继承了,而是要被类实现
- 接口支持多重继承,
接口的声明
接口的声明语法格式如下
[可见度] interface 接口名称 [extends 其他的类名] {
// 声明变量
// 抽象方法
}
import java.lang.*;
//引入包
public interface NameOfInterface
{
//任何类型 final, static 字段
//抽象方法
}
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时不必使用abstract关键字。
- 接口中的方法都是公有的。
实例
interface Animal {
public void eat();
public void travel();
}
接口的实现
当类实现接口的时候,类要实现接口中的所有方法。否则,类必须声明为抽象类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
... implements 接口名称[, 其他接口, 其他接口..., ...] ...
实例
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
输出:
Mammal eats
Mammal travels
重写接口中声明的方法时,应注意:
- 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常,
- 类在重写方法时要保持一致的方法名,并且应该保持相同或相兼容的返回值类型。
- 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
实现接口的时候,应注意:
- 一个类可以同时实现多个接口
- 一个类只能继承一个类,但是能实现多个接口
- 一个接口能继承另一个接口,这和类之间的继承比较相似,并且接口允许多重继承
实例
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
标识接口
没有任何方法和属性的接口(仅仅只有花括号)
标识接口仅仅表明它的类型属于一个特定的类型,供其他代码来测试允许做的一些事情。
标识接口作用:简单地说就是给某个接口打个标(盖个戳),使对象拥有某个或某些特权。
例如:java.awt.event包中的MouseListener接口继承的java.util.EventListener接口定义如下:
package java.util;
public interface EventListener
{}
没有任何方法的接口称为标识接口,主要有两种目的:
- 建立一个公共的父接口
- 正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
- 向一个类添加数据类型
- 这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
抽象类与接口
相同点:
- 都不能被实例化。
- 接口的实现类或抽象类的子类都只有实现了抽象类或接口的方法后才能被实例化。
不同点:
- 抽象类可以有定义与实现,即其方法可以在抽象类中被实现;接口只有定义,其方法不能在接口中实现,只有实现接口的类才能实现接口中定义方法
- 抽象类只能单继承;接口可以多重继承
- 抽象类强调所属关系,设计理念是“is-a”;接口强调的是特定功能的实现,设计理念是“has-a”
- 当功能需要累积是,用抽象类;不需要累积时,用接口
- 抽象类倾向于充当公共类的角色,不适用于日后重新对里面的代码进行修改;接口被运用与实现比较常用的功能,便于日后维护或者添加删除方法
接口是一种特殊的抽象类,使用接口完全有可能实现与抽象类相同的操作,那么,抽象类与接口应该如何选择:
- 抽象类多用于在同类事物中有无法具体描述的方法的场景,所以当子类和父类之间存在有逻辑上的层次结构时,推荐使用抽象类
- 接口多用于不同类之间,定义不同类之间的通信规则,所以当希望支持差别较大的两个或者更多对象之间的特定交互行为时,应该使用接口