当前位置: 首页 > news >正文

Java(六)——抽象类与接口

文章目录

  • 抽象类和接口
    • 抽象类
      • 抽象类的概念
      • 抽象类的语法
      • 抽象类的特性
      • 抽象类的意义
    • 接口
      • 接口的概念
      • 接口的语法
      • 接口的特性
      • 接口的使用
      • 实现多个接口
      • 接口与多态
      • 接口间的继承
      • 抽象类和接口的区别

抽象类和接口

抽象类

抽象类的概念

Java使用类实例化对象来描述现实生活中的实体,不过,不是所有的类都能描述实体的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类(准确来说,我们可以将这样的类设计成抽象类)。

以画图形为例,有如下代码:

//Shape.java
public class Shape {public void draw() {System.out.println("画一个图形");}
}//Pentagram.java
public class Pentagram extends Shape {@Overridepublic void draw() {System.out.println("五角星☆");}
}//Rotundity.java
public class Rotundity extends Shape {@Overridepublic void draw() {System.out.println("圆形⭕");}
}

我们在实现Shape类的draw方法时,由于Shape是一个宽泛的概念,不是一个具体的图形,所以我们实际上是没办法实现的,上代码中的实现也没有实际的意义,因此,我们可以将Shape类设计成抽象类,而对于Shape类中的draw方法,我们可以将它设计成抽象方法.


抽象类的语法

Java中,abstract修饰的类称为抽象类,抽象类中被 abstract修饰的方法称作抽象方法

抽象方法不用给出具体的实现。(这是强制的,不能给出方法体

将上面的Shape类设计成抽象类,draw方法设计成抽象方法:

//Shape.java
public abstract class Shape {public abstract void draw();
}

注意: 抽象类也是类,抽象类也可以有普通的成员方法、成员变量或构造方法。

抽象类与普通类的区别是抽象类有abstract修饰,且可以存在抽象方法(不存在也可以)。


抽象类的特性

  1. 抽象类不能实例化对象

    public class Test {public static void main(String[] args) {Shape shape = new Shape();//实例化抽象类,报错}
    }
    

在这里插入图片描述

抽象类虽然不能实例化,但是抽象类的引用可以引用其子类的对象

//Shape.java
public abstract class Shape {public abstract void draw();
}//Pentagram.java
public class Pentagram extends Shape {@Overridepublic void draw() {System.out.println("五角星☆");}
}//Rotundity.java
public class Rotundity extends Shape {@Overridepublic void draw() {System.out.println("圆形⭕");}
}//Test.java
public class Test {public static void func(Shape shape) {shape.draw();}public static void main(String[] args) {Pentagram pentagram = new Pentagram();Rotundity rotundity = new Rotundity();Shape[] shapes = {pentagram, rotundity};//向上转型for(Shape shape : shapes){func(shape);}}
}
  1. 抽象类必须被继承,且继承抽象类的子类必须重写抽象类中的抽象方法,如果不想重写抽象方法,需要加abstract修饰。不过,一旦这个类被一个普通的类继承,则此普通类必须重写所有的抽象方法。

    public abstract class Test {public abstract void func1();
    }abstract class A extends Test {public abstract void func2();
    }class B extends A {@Overridepublic void func1() {System.out.println("重写抽象类Test的抽象方法");}@Overridepublic void func2() {System.out.println("重写抽象类A的抽象方法");}
    } 
    

    ​ 抽象类A继承了抽象类Test,普通类B继承了A,那么普通类B必须重写A 类和Test类中的所有抽象方法。

  2. 由于抽象方法必须被重写,所以抽象方法不能被privatefinalstatic修饰

  3. 抽象类中可以不包含抽象方法,但抽象方法必须在抽象类中

    public class Test {public abstract void func();
    }
    

    在这里插入图片描述

  4. 抽象类中可以存在构造方法,用于子类实例化时初始化父类的成员

    //Shape.java
    public abstract class Shape {public int a;//抽象类的构造方法public Shape(int a) {this.a = a;}public abstract void draw();
    }//Pentagram.java
    public class Pentagram extends Shape {public Pentagram(int a) {super(a);}@Overridepublic void draw() {System.out.println("五角星☆");}
    }//Rotundity.java
    public class Rotundity extends Shape {public Rotundity(int a) {super(a);}@Overridepublic void draw() {System.out.println("圆形⭕");}
    }//Test.java
    public class Test {public static void main(String[] args) {Shape shape = new Pentagram(20);System.out.println(shape.a);}
    }
    

    打印结果是:
    在这里插入图片描述


抽象类的意义

我们可能会有一个疑问,普通类也可以被继承,普通的成员方法也可以被重写,为什么要用抽象类和抽象方法呢?

抽象类中的抽象方法如果没有被重写,编译器会报错。 是的,使用抽象类和抽象方法的好处就是多了一层编译器的校验,能帮助我们检查抽象方法是否被重写。

抽象类不能被实例化。 很多引用场景,比如上面的画图形场景,其实际工作不应该由父类完成,而应由子类完成,此时误用父类编译器会报错。

总的来说,使用抽象类就是为了预防出错,并让我们尽早发现问题,充分利用编译器的校验,在实际开发中是非常有意义的。


接口

接口的概念

接口(英文:Interface),在JAVA编程语言中是一个抽象(引用)类型,是抽象方法的集合。一个类通过继承接口的方式,从而来继承接口的抽象方法。

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

我们很容易联想到实际生活中电脑的USB接口,它是一个标准,符合这个标准,就可以与电脑交互(可以插U盘、鼠标等)。

Java中的接口也是公共行为的规范,我们在实现时,只要符合标准,就可以通用。所以,接口可以看作是:多个类的公共规范,是一种引用类型


接口的语法

接口的定义与类的定义类似,只需要把关键字class换成interface

接口只能被publicabstract修饰,abstract默认不显性显示,如果省略public则使用默认的访问权限

public interface 接口名 {//...
}
  • 接口的命名一般是以I开头
  • 接口的命名一般使用形容词词性的单词,这样就能显示出接口的标准

接口的特性

  1. 接口是一种引用类型,但是不能直接实例化对象(接口实际上比抽象类更加抽象),不过接口可以引用实现该接口的类的对象(向上转型)

        public static void main(String[] args) {ITest iTest = new ITest();//报错,不能直接实例化接口}
    

    在这里插入图片描述

    interface ITest {//待实现的方法void func();
    }class A implements ITest {@Overridepublic void func() {System.out.println("类A实现的func");}
    }class NewTest {public static void main(String[] args) {//接口引用了A类对象ITest iTest = new A();}
    }
    
  2. 接口可以定义变量,且接口的成员变量都是publicstaticfinal修饰的,且接口定义的变量必须手动初始化

    即使我们不写修饰符,成员变量也默认被publicstaticfinal修饰

    public abstract interface ITest {//下面定义的变量都是由 public static final 修饰的(不论是否显性书写或写全)public int a = 1;static int b = 2;final int c = 3;int d = 4;public static int e = 5;static final int f = 6;public final int g = 7;public static final int h = 8;
    }
    
  3. 接口可以定义方法,接口的方法默认是publicabstract修饰的,属于抽象方法,不需要方法体。如果想要在接口中具体实现一个方法,那么它必须是由defaultstatic修饰

    public interface ITest {//以下写法的修饰符都是 public abstract(无论是否写或写全)void func0();public abstract void func1();public void func2();abstract void func3();//被default或static修饰的方法有具体的实现default void func4() {System.out.println("接口中被default修饰的方法有方法体");}static void func5() {System.out.println("接口中被static修饰的方法有方法体");}
    }
    
  4. 实现接口的类,必须重写接口中的所有抽象方法,如果此类不想重写接口中的抽象方法,则它必须由abstract修饰,一旦此类被另一个非抽象类继承,那么这个非抽象类必须重写所有未被重写的抽象方法

    interface IA {//接口抽象方法void abfunc1();
    }abstract class A implements IA {public void myA() {System.out.println("A类特有的方法");}//A类抽象方法abstract void abfunc2();
    }class B extends A {@Overridepublic void abfunc1() {System.out.println("重写IA接口中的抽象方法");}@Overridevoid abfunc2() {System.out.println("重写B类中的抽象方法");}
    }
    
  5. 接口中不能有静态代码块、构造代码块或构造方法

  6. 由于接口的成员方法默认都有public修饰,所以重写后的方法必须是public修饰(重写后的方法的访问权限要大于等于父类)

  7. 上述表述的重写严格来说不准确,不过,语法要求与重写一致(这里不必纠结,只要记住抽象方法要被重写即可)

  8. 接口文件的文件名必须与接口名相同,且接口文件编译后也是.class为后缀的字节码文件


接口的使用

接口不能直接实例化对象,接口的使用需要一个 “实现类” 来 实现 接口中的方法,要用到关键字implements

public classimplements 接口 {//...
}

接口与类之间implements实现关系,父类和子类之间extends继承关系

interface ITest {//待实现的方法void func();
}class A implements ITest {@Overridepublic void func() {System.out.println("类A实现的func");}
}

继承表达的是is A的关系,如狗是动物;接口表达的含义是具有xxx特性,如狗会跑


实现多个接口

Java中不支持多继承,不过一个类可以实现多个接口,这个类需要实现所有接口的抽象方法:

//ISwimming.java
public interface ISwimming {void swim();
}//IRunning.java
public interface IRunning {void run();
}//Dog.java
public class Dog implements IRunning, ISwimming {public String name;public int age;public Dog(String name, int age) {this.name = name;this.age = age;}@Overridepublic void run() {System.out.println(this.name + "正在跑");}@Overridepublic void swim() {System.out.println(this.name + "正在游泳");}
}
  • 一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类

一个类可以继承一个父类并同时实现多个接口:

//Animal.java
public class Animal {public String name;public int age;
}
//IRunning.java
public interface IRunning {void run();
}//ISwimming.java
public interface ISwimming {void swim();
}//Dog.java
public class Dog extends Animal implements IRunning, ISwimming {public Dog(String name, int age) {this.name = name;this.age = age;}@Overridepublic void run() {System.out.println(this.name + "正在跑");}@Overridepublic void swim() {System.out.println(this.name + "正在游泳");}
}
  • 继承与实现都存在时,必须先写extends继承,后写implements实现
  • 对于上面的代码,理解一点:只要一个类实现了如IRunning接口,它就可以run(),而不必是动物。(有些废话,主要是强调接口使用)

接口与多态

Java中,接口可以实现多态,看如下代码:

//IEat.java
public interface IEat {void eat();
}//Animal.java
public class Animal {public String name;public int age;
}//Cat.java
public class Cat extends Animal implements IEat {public Cat(String name, int age) {this.name = name;this.age = age;}@Overridepublic void eat() {System.out.println(this.name + "正在吃猫粮...");}
}//Dog.java
public class Dog extends Animal implements IEat {public Dog(String name, int age) {this.name = name;this.age = age;}@Overridepublic void eat() {System.out.println(this.name + "正在吃狗粮...");}
}//Test.java
public class Test {//体现多态public static void testEat(IEat iEat) {iEat.eat();}public static void main(String[] args) {testEat(new Dog("大哈", 2));//匿名对象testEat(new Cat("小猫", 3));}
}

对于上述代码,我们给同一个方法testEat传入不同的对象CatDog,打印结果不同:

在这里插入图片描述


接口间的继承

接口的继承相当于把所有的接口合并在一起。

接口间可以实现继承

//IExcellentQuality.java
public interface IExcellentQuality {void func1();
}//IKind.java
public interface IKind extends IExcellentQuality {void func2();
}

上述代码逻辑上就是:善良的 是 品质优良的

  • 一个接口继承了另一个接口,子接口不实现父接口的抽象方法,而当一个非抽象类实现了子接口,那么这个类要重写子接口和父接口所有的抽象方法

    public class Person implements IKind {@Overridepublic void func2() {System.out.println("kind");}@Overridepublic void func1() {System.out.println("excellent quality");}
    }
    

Java中,类与类之间不可以实现多继承,但接口与接口之间可以实现多继承

比如以下例子,两栖动物既可以游泳又可以行走:

//IRunning.java
public interface IRunning {void run();
}//ISwimming.java
public interface ISwimming {void swim();
}//IAmphibious.java
public interface IAmphibious extends ISwimming, IRunning {void func();
}

【总结】

  • 一个接口继承了另一个接口,子接口不实现父接口的抽象方法,而当一个非抽象类实现了子接口,那么这个类要重写子接口和父接口所有的抽象方法
  • 接口与接口之间可以实现多继承

抽象类和接口的区别

抽象类中可以包含普通的方法和字段,这些字段和方法可以被子类直接使用(普通方法不需要被重写),而接口中不能定义普通的字段和方法,子类必须重写接口中的所有抽象方法。

在这里插入图片描述

几点具体的区别:

    1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行(除非被defaultstatic修饰)。
    1. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    1. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。(从 Java8 开始接口中可以定义静态方法)
    1. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

另外,我们补充一下实现接口与继承的区别:

1.不同的关键字,即实现接口(implements),继承(extends);

2.(一个类只能继承一个父类,而一个方法可以实现多个接口) 在面向对象编程的时候只能是单继承,但是实现接口可以有多个,简单点说,就是实现接口可以有好多个,但是继承的中父类只能只有一个,因为父亲只有一个,这说明了继承在Java中具有单根性,子类只能去继承一个父类;

3.在接口中只能定义全局变量和抽象方法,而在继承中可以定义普通的属性、方法等等…

4.(接口中的抽象方法必须被重写,而继承中子类可以选择不重写父类的方法(不考虑父类是抽象类)) 当某个接口被实现的时候,在类中一定要重写接口中的抽象方法,而继承中子类可以选择是否重写父类的方法,不是强制的


以上就是对Java中抽象类和接口的所有介绍了,希望能给大家带来帮助!
接下来将会发布不少博客:Java实现简单的图书管理系统、介绍内部类、介绍String…

http://www.lryc.cn/news/357707.html

相关文章:

  • 【4.vi编辑器使用(下)】
  • 【数据结构】探索树中的奇妙世界
  • 搭建YOLOv10环境 训练+推理+模型评估
  • c++(一)
  • java面试中高频问题----1
  • ABB 控制柜
  • 【错误记录】HarmonyOS 运行报错 ( Failure INSTALL_PARSE_FAILED_USESDK_ERROR )
  • 使用C语言openssl库实现 RSA加密 和 消息验证
  • 海外投放面试手册
  • 第十三章 进程与线程
  • Flink面试整理-对Flink的高级特性如CEP(复杂事件处理)、状态后端选择和调优等有所了解
  • 算法:树状数组
  • Kafka SASL_SSL集群认证
  • 同城交友论坛静态页面app Hbuild
  • spring session+redis存储session,实现用户登录功能,并在拦截器里面判断用户session是否过期,过期就跳转到登录页面
  • Debug-013-el-loading中显示倒计时时间
  • 5月29日,每日信息差
  • 2024年弘连网络FIC大会竞赛题线下决赛题
  • Element-UI 入门指南:从安装到自定义主题的详细教程
  • vs工程添加自定义宏
  • shell脚本:将一维数组以二维数组显示
  • QT C++ 读写mySQL数据库 图片 例子
  • Unix环境高级编程--8-进程控制---8.1-8.2进程标识-8.3fork函数-8.4 vfork函数
  • Facebook之魅:数字社交的体验
  • 【重装windows遇到网络适配器无法更改】
  • FFmpeg+QT播放器实战1---UI页面的设计
  • C/C++语法|pthread线程库的使用
  • 四川汇聚荣聚荣科技有限公司是正规的吗?
  • tomcat学习--部署java项目
  • 用 vue3 + phaser 实现经典小游戏:飞机大战