Java面向对象:抽象类和接口
在总结完Java程序面向对象的三大特征(封装、继承、多态)之后,我慢慢理解了Java语言这种面向对象程序设计的优越性,它既能保持自身独立性又可以不断进化和发展。
如果说继承是实现Java多态性的基础,那抽象类和接口更是为多态提供了非常好的支持。
因为抽象类和接口之间很难区分,所以把他们放在一起总结进行区分。
一、抽象类
从形式上来说,用abstract修饰的类就是抽象类。抽象类是不完整的,只能做基类,且不能够被实例化(不能new)。
通俗来讲,抽象类它本身不做任何事情,它是去要求别人来实现它的规范的。它定义了一组抽象的方法,至于这组抽象方法的具体表现形式由派生类(子类)来实现。抽象类的实现要用到继承,不然抽象类就没有意义。
使用抽象类必须注意以下几点:
1.抽象类只是一个引用而已,不能被实例化,实例化的工作要交给它的子类;
2.抽象方法必须由子类来进行重写;
3.在一个类中,只要有一个方法被定义为抽象方法(被abstract修饰了),那么这个
类就必须被定义为抽象类(类名也要被abstract修饰),若子类没有重写父类全部的
抽象方法,则必须为抽象类。
4.抽象类中可以包含具体方法,也可以不包含;
5.子类中的抽象方法不能与父类的抽象方法同名;
6.不能用abstract修饰变量、代码块、构造器;
7.不能用abstract修饰私有方法、静态方法、final的方法、final的类
public abstract class ChouX {
// 抽象类中可以包含具体方法,也可以不包含;
public abstract void method1();
public void method2() {
}
}
代码:
public abstract class Animal {
public abstract void shout();
}
//猫子类
public class Cat extends Animal {
@Override
public void shout() {
System.out.println("喵~~");
}
}
//Dog子类
public class Dog extends Animal {
@Override
public void shout() {
System.out.println("汪汪汪。。。");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.shout();
a2.shout();
}
}
二、接口
对于面向对象编程来说,抽象是它的一大特征(可以说是第四大特征)。在java中,可以通过两种形式来体现OOP的抽象:抽象类和接口。
接口(interface),在软件工程中,接口泛指供别人调用的方法或者函数。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守。
接口(interface)是抽象方法和常量值定义的集合。
接口的特点:
1) 用interface来定义。
2)接口中的所有成员变量都默认是由public static final修饰的。
3)接口中的所有抽象方法都默认是由public abstract修饰的。
4)接口中没有构造器。
5)接口采用多继承机制。
//例子
public interface Door{
public void open();
// 默认是 public abstract
public abstract void close();
}
定义Java类的语法格式:先写extends,后写implements
public class SubClass extends SuperClass implements InterfaceA,InterfaceB{
}
一个类可以实现多个接口,接口也可以继承其它接口(多重继承)。
注意:
1)实现接口的类中必须实现接口中所有方法。否则,仍为抽象类。
2)接口的主要用途就是被实现类实现。(面向接口编程)
3)与继承关系类似,接口与实现类之间存在多态性
4)接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲, 接口是
一种特殊的抽象类,这种抽象类中只包含常量和方法的定义 (JDK7.0及之前),
而没有变量和方法的实现。
Java 8中关于接口的改进
Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static 关键字修饰。
可以通过接口直接调用静态方法,并执行其方法体。
默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
public interface AA {
double PI = 3.14;
public default void method() {
System.out.println("北京");
}
default String method1() {
return "上海";
}
public static void method2() {
System.out.println(“hello lambda!");
}
}
接口冲突
若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同 参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接 口时,会出现:接口冲突。
为解决这种多继承关系,Java8提供了下面三条规则:
1) 类中的方法优先级最高,类或父类中声明的方法的优先级高于任何声明为默认方法
的优先级。
2) 如果第一条无法判断,那么子接口的优先级更高:方法签名相同时,优先选择拥有
最具体实现的默认方法的接口, 即如果B继承了A,那么B就比A更加具体。
3) 如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法,
显式地选择使用哪一个默认方法的实现。
情况一:
public interface A {
default void hello() {
System.out.println("hello from A");
}
}
public interface B extends A {
default void hello() {
System.out.println("hello from B");
}
}
public class C implements A, B {
public static void main(String[] args) {
new C().hello();
}
}
结果:
规则(1)不满足。
因为B继承了A,所以B比A更具体,所以应该选择B的hello()方法。
所以,程序会打印输出"hello from B"。
情况二:
public class D implements A {
public void hello() {
System.out.println("hello from D");
}
}
// 测试类
public class C extends D implements A, B {
public static void main(String[] args) {
new C().hello();
}
}
结果:
C继承了D,D覆盖了A的默认方法。
依据规则(1),父类中声明的方法具有更高的优先级。
所以程序会打印输出"hello from D"。
情况三:
public interface A {
default void hello() {
System.out.println("hello from A");
}
}
// 假设现在B不继承A:
public interface B {
default void hello() {
System.out.println("hello from B");
}
}
// 测试类
public class C implements A, B {
public static void main(String[] args) {
new C().hello();
}
}
结果:
此时,由于编译器无法识别A还是B的实现更加具体。
所以会抛出编译错误:”C inherits unrelated defaults for hello() from types A and B“。
像这种场景要解决冲突,可以在C中覆盖hello()方法并在方法内显示的选择调用A还是B的方法。
调用方式如下:
public class C extends D implements A, B {
public void hello() {
// 显式地选择调用接口A中的方法
A.super.hello();
// 同理,要调用接口B中的方法,可以这样:B.super.hello()
}
public static void main(String[] args) {
// 输出 hello from A
new C().hello();
}
}
三、抽象类和接口的区别
1.语法层面的区别
首先,抽象类可以提供成员方法实现的细节(抽象类就比普通类多了抽象方法),
接口中只能存在public abstract 方法;
其次,抽象类中的成员变量可以是各种类型的,
接口中的成员变量只能是public static final类型的;
再者,接口中不能含有静态代码块和静态方法,而抽象类可以;
最后,一个类只能继承一个抽象类,而可以实现(继承)多个接口。
2.设计层面上的区别
首先,抽象类是对一种事物的抽象,即对类的抽象;
接口是对行为的抽象。
再者,抽象类作为很多子类的父类,它是一种模板式的设计。
接口是一种行为规范。
最后,在一个抽象类中,如果你想更改一个方法,可以直接在抽象类中实现,子类可以不进行更改。
如果发生在接口上,那么所有实现这个接口的类都需要更改。