当前位置: 首页 > 后端技术 > Java

为什么看了那么多源码还是不能用接口和抽象类?

时间:2023-04-01 19:59:58 Java

我们可能经常在面试中被问到什么是接口?什么是抽象类?接口和抽象类有什么区别?如何使用接口和抽象类?同时,我们在工作或者阅读源代码的时候,会发现接口和抽象类经常出现在我们的眼前。今天我们就一起来讨论接口和抽象类。接口的官方解释是一系列的方法声明,是一些方法特性的集合。那么我们就根据这句话说一下接口的特点吧。首先,从上面这句话可以看出,接口中定义了一系列的方法声明,但是并没有方法的实现。从这个角度来看,界面是一种规则制定者。它不关心你如何实现它,它只约束你。必须有这些行为,遵守这些规则。而我们的方法需要定义一系列的业务逻辑代码来完成各种具体的行为,所以方法是系统中的一种行为形式,而接口约束了我们必须有哪些行为,所以综上所述,接口是一种抽象的行为。比如公司接口规定,人要有加班行为(手动调皮)。那么不同的人可能会以不同的方式完成这个行为。有些人完成工作的速度可能更快,有些人可能完成的速度较慢,有些人手头的任务较多,有些人手头的任务较少。行为是抽象的,所以我不管你做了什么,但你必须有加班的行为。必须要有加班的行为。抽象类直观上就是抽象类,我们知道类是属于同一个类的所有对象的抽象。Java是一种面向对象的编程语言。把世界上的客观事物抽象为对象,所以我们知道对象是对现实世界中客观事物的抽象。所以综上所述,抽象类就是对类的抽象,是对现实世界中客观事物的进一步抽象。抽象类是类的抽象接口与抽象类的比较。这个问题应该是经常被问到的问题。作者认为我们可以从三个角度来比较两者:(1)类(2)属性(3)方法从类的角度来看,一个类需要使用extends关键字才能使用抽象类,即,需要继承抽象类,而Java是单继承的,只能继承一个抽象类。要使用接口,类需要使用implements关键字,称为实现接口。同时,由于Java是多实现机制,可以实现多个接口。抽象类和接口都不能实例化,即不能创建接口或抽象类对象。从属性的角度,我们先看下面的代码=1;//可以privatestaticintf=2;//可以privatestaticfinalintg=3;//可以}接口接口{privateinta=1;//不能i??ntb;//不能intc=1;//是否可以保护intd=2;//否;publicinte=3;//是的publicstaticintf=4;//是的publicstaticfinalintg=5;//是的}从上面的代码我们可以看出在抽象类中定义属性和在抽象类中定义属性没有区别在普通类中定义属性。如何定义定义类的属性就是如何定义抽象类的属性。在接口中,我们只能使用不带任何修饰符的修饰符,withpublic,withstatic,withfinal。其实反编译后我们可以看到接口中的所有属性都被publicstaticfinal修饰了。也就是说,接口中定义的属性以常量的形式存在。从方法的角度来看,方法的角度应该是抽象类和接口最大的区别,尤其是现在各个版本都在不断的改变接口中的方法。这里以JDK1.8为例,比较接口和抽象类中的方法有哪些区别。有抽象方法的类一定是抽象类,但是抽象类不需要全是抽象方法,也可以有普通方法,甚至全是普通方法(不过没有抽象方法,为什么要定义成抽象的类?)对于抽象类中的普通方法,其定义方式与普通类中的方法相同,可以修饰为private、unmodified、protected、public。对于抽象方法,不能使用private修饰(因为抽象方法本身需要通过子类继承来重写)。普通方法可以使用静态装饰,但是抽象方法不能使用静态装饰。接口中的方法可以不用修饰符修饰,也可以public。反编译后可以看到接口中的方法都是用publicabstract修饰的,类似于接口中的属性。不同的是,在JDK1.8中,一个接口可以定义静态方法和默认方法,需要给出一个默认实现。两者如何结合使用我们学习抽象类和接口是为了更好的使用抽象类和接口为我们服务。两者之间没有好坏之分,谁强谁弱。如果是这样的话,那么其中之一就没有存在的必要了。我们需要学习的是如何让两者协同工作。我们可以从Java世界中大量的代码和第三方框架来思考这个问题。从前面的讨论可以看出,接口是对行为的约束。它提供了一种机制来要求各个类应该有哪些行为,但不限制行为的具体实现,所以接口的抽象层次应该是相当高的。我们前面说的抽象类就是类的抽象。笔者认为类的设计目的更重要的一方面是重用代码,另一方面是减轻接口实现的负担。当不同的类有一些公共的行为,并且这个行为的实现是一致的,那么我们可以让这些类派生出一个抽象类,避免所有的子类都需要实现接口的所有方法,从而实现代码重用,减少类实现接口的负担(不需要实现所有接口方法)。一个例子现在让我们抽象现实世界中的汽车。下面通过一个例子来思考一下抽象类和接口是如何一起使用的。首先,汽车要能行驶,同时需要能量带动发动机运动,车门才能开合。这样我们就把汽车的四种行为抽象成了接口中的方法,因为这些方法是所有汽车所必需的。interfaceCar{//goingpublicvoidmove();//补充能量publicvoidaddEnergy();//开门publicvoidopenTheDoor();//关门publicvoidcloseTheDoor();首先要实现这四种行为,也就是要实现这四种功能,但是每一种车都需要实现这些功能。各种车之间有很多差异,但是这些基本功能都是可以实现的。它们相似或相同。这时候我们想到现在的小型车可以分为燃油车和有轨电车,那么我们可以抽象的实现这些方法。这样可以减轻造车的负担(减少实现接口的负担),提高代码的复用率。abstractclassAbstractFuelCarimplementsCar{publicvoidmove(){System.out.println("燃油车向前冲!!!");}publicvoidaddEnergy(){System.out.println("加油。");//调用子类重写的加油方法。加油();}}publicvoidopenTheDoor(){System.out.println("燃油车开门...");}publicvoidcloseTheDoor(){System.out.println("燃油车关门...");}publicabstractvoidrefuel();}abstractclassAbstractEnergyCarimplementsCar{publicvoidmove(){System.out.println("电车向前冲!!!");}publicvoidaddEnergy(){System.out.println("Powerup...");//调用子类重写的加油方法。收费();}publicvoidopenTheDoor(){System.out.println("电车开...");}publicvoidcloseTheDoor(){System.out.println("电车关闭...");}publicabstractvoidcharge();}当我们构建具体的汽车时,只需要继承相应的抽象类来实现每辆汽车的模板方法和一些特有的功能方法,不仅提高了代码的复用性,也减少实现接口的负担。classBMWextendsAbstractFuelCar{@Overridepublicvoidrefuel(){System.out.println("BMWComeon!!!");}publicvoidsomeOtherFunction(){System.out.println("我有点贵...");}}classTeslaextendsAbstractEnergyCar{@Overridepublicvoidcharge(){System.out.println("Teslaischarging...");}publicvoidsomeOtherFunction(){System.out.println("我提速快!!!");}}所以通用接口是用来指定业务逻辑应该实现什么样的功能和行为,而抽象类在接口下,它主要用于代码复用,提取一些公共行为,减轻接口实现的负担,通过模板方法划定业务逻辑流程,降低子类的实现复杂度。如下图所示:Java世界中的接口抽象类学习让我们来看看在Java类库和一些常用的第三方开源框架中,接口和抽象类是如何结合使用的。《1.StringBuilder和StringBuffer》StringBuilder和StringBuffer的继承结构如下:StringBuilder和StringBuffer的继承结构实现类、ApplicationContext实现类、BeanPostProcessor、AOPPointCutAdvisor实现等,SpringJDBC、事务管理、SpringMVC、SqlSession在MyBatis等有大量这样的结构。因为他们的继承结构一般都比较复杂,这里就不附图了,谢谢有兴趣的同学可以查看Spring、MyBatis等框架的相关源码。