抽象类与接口

抽象类与接口

抽象类

概念

当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰的类就是抽象类。

abstract class Animal {
    String name;
    int age;
    public abstract void call();
}
public class AnimalTest extends Animal{
    @Override
    public void cry() {

    }
}

介绍

1、用abstract关键字来修饰一个类时,这个类就叫抽象类

//格式
访问修饰符 abstract 类名{ }

2、用abstract关键字来修饰一个方法时,这个方法就是抽象方法

//格式 
访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体

3、抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类

细节

1、抽象类不能被实例化

public class AnimalTest {
    public static void main(String[] args) {
        //会报错'Animal' is abstract; cannot be instantiated
        Animal animal =new Animal();
    }
}

2、抽象类不一定要包含abstract方法,即抽象类可以没有abstract方法

public abstract class Animal {
    String name;
    int age;
    //public abstract void call();
    public void cry(){
        
    }
}

3、一旦类包含了abstract方法,则这个类必须声明为abstract

//Class 'Animal must either be declared abstract or implement abstract method 'call' in 'Animal' 
//类“动物”必须声明为抽象,或者在“动物”中实现抽象方法“调用”
public  class Animal {
    String name;
    int age;
    public abstract void call();
    //public void cry(){}
}

4、abstract只能修饰类和方法

public abstract class Animal {
    String name;
    //Modifier 'abstract' not allowed here
    abstract int age;
    public abstract void call();
}

5、抽象类可以拥有任意成员(抽象类的本质还是一个类),比如:非抽象方法、构造器、静态属性等等

public abstract class Animal {
    public int n1 = 10;//属性
    public static  String name = "Ck";//静态属性
    //方法
    public void hi() {
        System.out.println("hi");
    }
    //抽象方法
    public abstract void hello();
    //静态方法
    public static void ok() {
        System.out.println("ok");
    }
    //构造函数
    public Animal(int n1) {
        this.n1 = n1;
    }
}

6、抽象方法不能有主体(方法体)

public abstract class Animal {
    //Abstract methods cannot hcallave a body
    public abstract void (){}
}

7、如果一个类继承了一个抽象类,则它必须实现那个抽象类的所有抽象方法,除非它自己也声明为abstract类

//Class 'AnimalTest' must either be declared abstract or implement abstract method 'cry()' in 'Animal'
public class AnimalTest extends Animal{
    public static void main(String[] args) {
       
    }
}

实现Animal中cry抽象方法

public class AnimalTest extends Animal{
    public static void main(String[] args) {

    }
    @Override
    public void cry() {
        
    }
}

8、抽象方法不能使用private、final 和 static 来修饰,因为这些关键字和重写是相违背的

public abstract class Animal {
    //抽象方法不能使用private、final 和 static来修饰,因为这些关键字都是和重写相违背的
    public /*这里不能使用private/final/static*/  abstract void cry();//抽象方法
}

接口

概念

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。

interface 接口名{ 
    属性; 
    抽象方法; 
} 
class 类名 implements 接口{ 
    该类属性; 
    该类方法; 
    必须实现接口的抽象方法; 
}

注意

接口是更加抽象的抽象的类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体。

接口体现了程序设计的多态和高内聚低耦合的设计思想。

JDK8.0开始,接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现。

interface Animal {
    static void cry(){
        
    }
}

细节

1、接口不能实例化

interface Animal {
     void cry();
}
public class AnimalTest implements Animal{
    public static void main(String[] args) {
        //报错'Animal' is abstract; cannot be instantiated
        new Animal();
    }
}

2、接口中所有的方法是public方法,接口中的抽象方法,可以不用abstract修饰

interface Animal {
    //修饰符“抽象”对于接口方法来说是多余的
    //Modifier 'abstract' is redundant for interface methods 
    public abstract void cry();

}

3、一个普通类实现接口,就必须将该接口的所有方法都实现

interface Animal {
    void cry();
    void call();
}
public class AnimalTest implements Animal{
    public static void main(String[] args) {
    }

    @Override
    public void cry() {

    }

    @Override
    public void call() {
        
    }
}

4、抽象类实现接口,可以不用实现接口的方法

interface Animal {
    void cry();
    void call();
}
abstract class AnimalDemo implements Animal{
}

5、接口中的属性,只能是 final 的,而且是 public static final 修饰符

interface Animal {
    //Modifier 'public' is redundant for interface fields 
    public int d = 13;
    //Modifier 'static' is redundant for interface fields 
    static int c = 12;
    //Modifier 'final' is redundant for interface fields 
    final int a = 10;
    //public static final int e =10;与  int b = 10;效果相当
    int b = 10;
}

6、一个类同时可以实现多个接口(理解为接口是对于Java单继承机制的一种补充)

interface Animal {

}
interface AnimalDemo{
}
public class AnimalTest implements Animal,AnimalDemo{
    public static void main(String[] args) {
    }
}

7、接口中的属性的访问形式:接口名.属性名

public class AnimalTest implements Animal,AnimalDemo{
    public static void main(String[] args) {
         //证明 接口中的属性,是 public static final
        int a = Animal2.a;
         //Cannot assign a value to final variable 'a'
        Animal2.a=30;
    }
}
interface Animal2{
    int a = 2;
}

8、接口不能继承其它的类,但是可以继承多个接口

继承的Animal2,Animal必须是接口,不能是类名
interface AnimalDemo extends Animal2,Animal{
}

9、接口的修饰符只能是 public 和默认,这点和类的修饰符是一样的

public和默认(default)。因为接口的上一单元是包,所以接口只有两个作用域:同包,任何位置。因此,只需要两种控制权限:包控制权限和公开访问权限,也就对应两种控制修饰符:默认(default)和public。

多态

多态形式

public class Polymorphic {
    public static void main(String[] args) {
        //接口的多态体现
        //接口类型的变量 g 可以指向 实现了G接口类的对象实例
        G g = new H();
        g=new C();
        //继承体现的多态
        //父类类型的变量 d 可以指向 继承D的子类的对象实例
        D d = new E();
        d= new F();
    }
}
abstract class D{
}
class E extends D{
}
class F extends D{
}
interface G{
}
class H implements G{
}
class C implements G{
}

多态数组

在案例中 UsbInterface usb 既可以接收手机对象,又可以接收相机对象,就体现了接口多态(接口引用可以指向实现了接口的类的对象)

public class InterfacePolyArr {
    public static void main(String[] args) {

        //多态数组 -> 接口类型数组
        Usb[] usbs = new Usb[2];
        usbs[0] = new Phone_();
        usbs[1] = new Camera_();
        /*
        给Usb数组中,存放 Phone  和  相机对象,Phone类还有一个特有的方法call(),
        请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,
        还需要调用Phone 特有方法 call
         */
        for (int i = 0; i < usbs.length; i++) {
            usbs[i].work();//动态绑定..
            //和前面一样,我们仍然需要进行类型的向下转型
            if (usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
                ((Phone_) usbs[i]).call();
            }
        }
    }
}

interface Usb {
    void work();
}

class Phone_ implements Usb {
    public void call() {
        System.out.println("手机可以打电话...");
    }

    @Override
    public void work() {
        System.out.println("手机工作中...");
    }
}

class Camera_ implements Usb {

    @Override
    public void work() {
        System.out.println("相机工作中...");
    }
}

手机工作中…
手机可以打电话…
相机工作中…

多态传递

public class InterfacePolyPass {
    public static void main(String[] args) {
        //接口类型的变量可以指向,实现了该接口的类的对象实例
        CG cg = new Teacher();
        //如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口
        //那么,实际上就相当于 Teacher 类也实现了 IH接口.
        //这就是所谓的 接口多态传递现象.
        CH ch = new Teacher();
    }
}

interface CH {
    void ch();
}
interface CG extends CH{ }
class Teacher implements CG {
    @Override
    public void ch() {
    }
}

关系

1、接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性。
接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。

2、接口比继承更加灵活
接口比继承更加灵活,继承是满足 is - a 的关系,而接口只需要满足 like - a 的关系。

3、接口在一定程度上实现代码解耦[即:接口规范性 + 动态绑定机制]

4、抽象类和接口的应用场景不同

抽象类的应用场景:
(1)父类只知道其子类应该包含怎样的方法,不能准确知道这些子类如何实现这些方法的情况下,使用抽象类。
(2)从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。

接口的应用场景:
(1)一般情况下,实现类和它的抽象类之前具有 “is-a” 的关系,但是如果我们想达到同样的目的,但是又不存在这种关系时,使用接口。
(2)由于 Java 中单继承的特性,导致一个类只能继承一个类,但是可以实现一个或多个接口,此时可以使用接口。

5、什么时候使用抽象类和接口:
(1)如果拥有一些方法并且想让它们有默认实现,则使用抽象类。
(2)如果想实现多重继承,那么必须使用接口。因为 Java 不支持多继承,子类不能继承多个类,但可以实现多个接口,因此可以使用接口。
(3)如果基本功能在不断改变,那么就需要使用抽象类。如果使用接口并不断需要改变基本功能,那么就需要改变所有实现了该接口的类。

区别

(一)语法层面上的区别

参数抽象类接口
实现子类使用extends 关键字来继承抽象类,如果子类不是抽象类,则需要提供抽象类中所有声明的方法的实现。子类使用implements 关键字来实现接口,需要提供接口中所有声明的方法的实现。
访问修饰符可以用public和default修饰默认修饰符是 public,不能使用其它修饰符
方法完全可以包含普通方法只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现
变量既可以定义普通成员变量,也可以定义静态常量只能定义静态常量,不能定义普通成员变量
构造方法抽象类里的构造方法并不是用于创建对象,而是让其子类调用这些构造方法来完成属于抽象类的初始化操作没有构造方法
初始化块可以包含初始化块不能包含初始化块
main方法可以有main方法,并且能运行没有main方法
与普通Java类的区别抽象类不能实例化,除此之外和普通Java类没有任何区别是完全不同的类型
运行速度比接口运行速度要快需要时间去寻找在类种实现的方法,所以运行速度稍微有点慢

(二)设计层面上的区别

1、抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类 Airplane,将鸟设计为一个类 Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

2、设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过 ppt 里面的模板,如果用模板 A 设计了 ppt B 和 ppt C,ppt B 和 ppt C 公共的部分就是模板 A 了,如果它们的公共部分需要改动,则只需要改动模板 A 就可以了,不需要重新对 ppt B 和 ppt C 进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。

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

abstract class Door {
    public abstract void open();
    public abstract void close();
}

或者

interface Door {
    public abstract void open();
    public abstract void close();
}

但是现在如果我们需要门具有报警 的功能,那么该如何实现?下面提供两种思路:
(1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;
(2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的 open( ) 和 close( ),也许这个类根本就不具备 open( ) 和 close( ) 这两个功能,比如火灾报警器。

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

interface Alram {
    void alarm();
}

abstract class Door {
    void open();
    void close();
}

class AlarmDoor extends Door implements Alarm {
    void oepn() {
      //....
    }
    void close() {
      //....
    }
    void alarm() {
      //....
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值