面试题目
- 什么是抽象类和接口
- 抽象类和接口能解决什么编程问题
- 接口和抽象类有什么区别
面向对象编程中,抽象类和接口是经常被用到的语法概念,也是面向对象四大特性。
题目解析
什么是抽象类
- 概念
没有方法体的方法叫抽象方法,有抽象方法的类一定是抽象类。而抽象类就是对类进行抽象。在编写时,需要子类必须实现的属性和行为,可以在对父类进行一个抽象,抽象类的运用场景。
- 特点
-
抽象类和抽象方法用abstract修饰;
-
抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
-
抽象类多态。抽象类不能直接实例化,要通过子类对象的实例化来实现(只能继承)。
-
抽象类的存在就是为了被继承,所以抽象类中的抽象方法不能被private、static、final修饰,否则无法被继承。
private abstract void eat();
public static abstract void run();
public final abstract void runA();
- 抽象类的子类必须实现抽象类中的所有抽象方法。如果子类不重写父类的抽象方法,那么子类也必须被abstract修饰为抽象类。
(1)重写父类抽象
public class Test extends AnimalAbstract{
@Override
public void eat() {
}
@Override
public void run() {
}
}
(2)子类如果不重写父类的抽象方法,那么直接声明子类为抽象类,否则不能编译通过。
public abstract class Test extends AnimalAbstract{
//子类如果不重写父类的抽象方法,那么子类
}
- 抽象类虽然不能被实例化,但是它可以有构造方法,供子类创建对象时,初始化父类成员
public class Test extends AnimalAbstract{
public Test(int age) {
super(age);
}
@Override
public void eat() {
}
@Override
public void run() {
}
public static void main(String[] args) {
Test test = new Test(5);
}
}
- 成员特点
- 成员变量。可以是变量,也可以是常量;
- 构造方法。可以有构造方法,但不能直接实例化。作用是用于子类访问父类数据的初始化。
- 成员方法。可以有抽象方法来限定子类必须完成某些操作,也可以有非抽象方法,提高代码的复用性(继承)
先来看一下这个例子,创建一个AbstractTest类,然后添加一个fun方法,然后再写一个Abstract1类,继承AbstractTest,并重写fun方法。
public class AbstractTest {
public void fun() {
System.out.println("父类的fun方法");
}
}
class Abstract1 extends AbstractTest {
public void fun() {
System.out.println("子类重写了父类的fun方法");
}
}
在创建一个Test类,用来调用Abstract1类中的fun方法。
public class Test {
public static void main(String[] args) {
AbstractTest abstractTest = new Abstract1();
abstractTest.fun();
}
}
输出的结果为
输出的是子类中的fun方法的值,我们可以看到当一个子类重写了父类的方法后,经过向上转型,父类引用了子类的对象,再去调用这个方法时,调用的是子类的方法,和父类的方法无关,那么父类的这个方法就没有实现的必要了,那么我们就可以把它设计成一个抽象方法,包含抽象方法的类称为抽象类。
抽象类语法
通常是使用abstract关键字来创建一个抽象类和抽象方法。
public abstract class AnimalAbstract {
public int age;
public AnimalAbstract() {
System.out.println("抽象类的构造方法");
}
public void getName() {
System.out.println("抽象类的普通方法");
}
public abstract void eat();
public abstract void run();
}
抽象类也是类,它也可以有自己的普通变量、方法和构造方法。
什么是接口
- 概念
一种公共的规范标准,只要符合就可以通用。就像现实中的USB接口一样,只要对应的插头,都可以接上。
在Java中,接口是一种引用数据类型
,可以看成是多个类的公共规范。
接口的运用场景,当多人实现一个项目,每个人编写的功能模块会有重合的部分,这个时候可以使用接口,便于之后的汇总。
- 语法
定义接口需要借助interface关键字,定义方式与定义类的方式相似。
在接口中的每个抽象方法前都会隐藏着public abstract修饰,下面4个都是抽象方法,平时一般使用第4种。
public interface MsyDemo {
public abstract void func();
public void func1();
void func2();
abstract void func3();
}
接口的名称前一般要先写个大写字母****I****,以此来表示这是一个接口(不写也不会报错)
阿里编码规范中约定,接口中的方法和属性前不加任何修饰符,保持代码的简洁性。
- 特点
- 接口中的成员变量,默认都是被public static final修饰的
public interface MsyDemo {
public int a = 10;
public static int b = 10;
public static final int c = 10;
int d = 10;
}
- 接口中的方法,默认都是被public abstract修饰的抽象方法
public interface MsyDemo {
public abstract void func();
void func2();
}
- 接口中的普通成员方法,一般是不能有具体的实现内容的
- 接口中的普通成员方法如果要有具体的实现,必须要被default修饰(从JDK8开始才有的)
public default void func1() {
System.out.println("接口中的普通成员方法如果要有具体的实现");
}
- 接口中可以有静态成员方法,它和default修饰的默认方法一样,都是被public修饰的;
public static void func4() {
System.out.println("接口中的静态成员方法");
}
- 和抽象类一样,接口也是不能被实例化的
- 接口中不能有静态代码块、实例代码块以及构造方法;
-
当一个类通过implements实现一个接口后,必须要重写接口中的抽象方法,否则这个类就要被abstract修饰为抽象类。
-
一个接口引用可以引用一个实现它的类的对象,即向上转型
public class Msy implements MsyDemo{
@Override
public void func() {
}
@Override
public void func2() {
}
}
----------------------------------------------------
public class Test {
public static void main(String[] args) {
MsyDemo msyDemo = new Msy();
}
}
- 接口的使用
- 接口不能直接使用,必须要通过一个类来“实现”接口,并重写接口中所有的抽象方法。子类和父类的关系是继承extends,类和接口的关系是实现implements。
- 一个类可以在继承一个父类之后再实现接口
- 一个类只能继承一个父类,但是一个类可以实现多个接口
- 接口的继承
在Java中,类和类之间是单继承的,但是接口与接口之间可以是多继承的。所以,我们可以通过接口来达到类的多继承的目的。
public class MyDemo {
public void test() {
}
}
================================
public interface IMsyRun {
void run();
}
================================
public interface IMsySay {
void say();
}
================================
public interface Msy extends IMsyRun, IMsySay {
void mainMsy();
}
================================
public class Test extends MyDemo implements Msy{
@Override
public void run() {
}
@Override
public void say() {
}
@Override
public void mainMsy() {
}
}
抽象类和接口能解决什么编程问题
- 抽象类
抽象类不能实例化,需要子类去继承,所以,抽象类的存在就是为了类继承来使用的,而类继承更多是为了代码复用而生,故认为抽象类是为了更好的解决代码复用问题。
一般的类继承不是也可以解决代码复用的问题了吗?是的,但是会存在一些约束性的问题。
- 接口
接口是对行为的一种抽象,相当于一组协议或者契约,你可以联想类比一下 API 接口,调用者只关注抽象的接口,不需要了解具体的实现,具体的实现代码对调用者透明。接口实现了约定和实现相分离,降低代码间的耦合性,提供代码的可扩展性。
接口和抽象类有什么区别
- 结构组成不同
抽象类:可以包含普通成员变量和普通成员方法、抽象方法、构造方法等(普通类有的它都有)。
接口:包含public static final修饰的常量(只能是常量)、抽象方法、静态方法、默认方法。
- 权限不同
抽象类:可以使用各种权限来修饰变量和方法。
接口:属性和方法默认都是public的。
- 子类使用方式不同
抽象类:子类通过extends关键字继承抽象类。
接口:子类通过implements关键字实现接口。
- 子类限制不同
抽象类:一个子类只能继承一个抽象类。
接口:一个子类可以实现多个接口。
========================================================================================
类与类:继承关系,只能单继承,也可多层继承。
类与接口:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口。
接口和接口。继承关系,可以单继承,也可以多继承。
练习
按要求实现下列问题:
- 动物类Animal包含了抽象方法 abstract void shout();
- Cat类继承了Animal,并实现方法shout,打印“猫会喵喵叫”
- Dog类继承了Animal,并实现方法shout,打印“狗会汪汪叫”
- 在测试类中实例化对象Animal a1 =new Cat(),并调用a1的shout方法
- 在测试类中实例化对象Animal a2 =new Dog(),并调用a2的shout方法
public abstract class Animal {
public abstract void shout();//抽象方法shout
}
public class Cat extends Animal {
@Override//重写
public void shout() {
System.out.println("猫会喵喵叫");
}
}
public class Dog extends Animal {
@Override//重写
public void shout() {
System.out.println("狗会汪汪叫");
}
}
public class Test {
public static void main(String[] args) {
Animal a1 = new Cat();//实例化
a1.shout();//调用
Animal a2 = new Dog();
a2.shout();
}
}