Java的抽象类与接口
一、抽象类
在了解抽象类之前,先来了解一下抽象方法。抽象方法是一种只有声明,而没有具体的实现的特殊的方法。抽象方法的声明格式为:
abstract void Quanta();
抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。但是它的子类可以创建对象。
Public abstract class Quanta {
abstract void fun();
}
我们都知道,父类是将子类所共同拥有的属性和方法进行抽取,这些属性和方法中,有的是已经明确实现了的,有的还无法确定,那么我们就可以将其定义成抽象,在后面的子类进行重用,进行具体化。这样,抽象类也就诞生了。
例如,定义了“动物”父类,其中“动物名称”和“动物年龄”属性已经明确了,但是“动物叫”的方法没有明确,此时就可以将“动物叫”定义为抽象方法。
包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。注意,抽象类要注意这几点:
-
抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
-
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
-
抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
-
构造方法,类方法(用static修饰的方法)不能声明为抽象方法。
-
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
-
外部抽象类不能用final、static声明。
示例:
package Course;
abstract class Student{
//私人变量
private String name;
private int age;
//构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void print1() {
System.out.println(name);
}
public abstract void print2();
}
public class abstractTest {
}
如果我们在public class abstractTest {}中尝试实例化,系统就会报错:
我们可以采用另外一种办法来实例化它:
我们创建一个NewStudent类extends我们的抽象类,再实例化NewStudent
package Course;
abstract class Student{
//私人变量
private String name;
private int age;
//构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void print1() {
System.out.println(name);
}
public abstract void print2();
}
class NewStudent extends Student{
public NewStudent(String name, int age) {
super(name, age);
// TODO Auto-generated constructor stub
}
@Override
public void print2() {
// TODO Auto-generated method stub
System.out.println("继承了");
}
}
public class abstractTest {
public static void main(String[] args) {
Student student = new NewStudent("quanta", 0);
}
}
注意:在父类中定义的抽象方法,在子类中必须实现(即方法重写)如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
二、接口
接口,英文称作interface,在软件工程中,接口泛指供别人调用的方法或者函数。从这里,我们可以体会到Java语言设计者的初衷,它是对行为的抽象。在Java中,定一个接口的形式如下:
[public] interface InterfaceName {
}
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口的使用
由于接口里面存在抽象方法,所以接口对象不能直接使用关键字new进行实例化。接口的使用原则如下:
(1)接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口;
(2)接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法;
(3)接口的对象可以利用子类对象的向上转型进行实例化。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
简单一例:
package Course;
//定义一个接口A
interface A {
public static final String a = "Quanta";//一个全局变量
public abstract void print();//定义一个抽象方法
}
interface B{
public abstract void get();
}
class Test1 implements A{
@Override
public void print() {
// TODO Auto-generated method stub
}
}
class Test2 implements A,B{
@Override
public void get() {
// TODO Auto-generated method stub
System.out.println("Quanta");
}
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println("Hello Quanta");
}
}
public class InterfaceTest {
public static void main(String[] args) {
Test2 test2 = new Test2();//实例化子类对象
A a = test2;
B b = test2;//向上转型
a.print();
b.get();
}
}
我们再来看看如果这样:
public class InterfaceTest {
public static void main(String[] args) {
Test2 test2 = new Test2();//实例化子类对象
A a = test2;
// B b = test2;//向上转型
B b = (B)a;
a.print();
b.get();
System.out.println(a instanceof A);
System.out.println(b instanceof B);
}
}
结果是:
Hello Quanta
Quanta
true
true
接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
下面的Sports接口被Hockey和Football接口继承:
// 文件名: Sports.java
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中,类的多继承是不合法,但接口允许多继承。
在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:
public interface Hockey extends Sports, Event
标记接口
最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
例如:java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:
package java.util;
public interface EventListener
{}
没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。