【JavaSE】抽象类和接口

简介: 本文讲解:抽象类和接口的详解

 image.gif编辑

前言

大家好,我还是那个不会打拳的程序猿。本期给大家带来的是抽象类、接口、Object类的讲解。文章通过抽象类的基本概念、抽象类的具体实现和接口的基本概念、接口的实现。再到Object的基本概念以及Object实现重写equals方法来讲解,文章图文并茂值得阅读。

image.gif编辑

目录

1.抽象类

1.1抽象类的概念

1.2抽象类语法

1.3抽象类的特性

2.接口

2.1接口的概念

2.2语法规则

2.3接口的特性

2.4接口间的继承

2.5继承与接口同时继承

2.6接口使用实例

2.7抽象类与接口类的区别

3.Object类

3.1对象比较equals方法


1.抽象类


1.1抽象类的概念

在面向对象中,所有对象都是依靠类来描述的。但是如果一个类中无法完整的描述到另一个类的具体信息时,这样我们就把这个类叫做抽象类。比如:

image.gif编辑

说明:

    1. 在蔬菜Vegetable类中,拥有eat()方法,eat()方法代表蔬菜可以吃。但不能代表所有蔬菜的口味,因此无法实现各个蔬菜的eat()方法。
    2. 小白菜是蔬菜,因此Pakchou与蔬菜Vegetable类为继承关系,但小白菜吃起来是清甜的,只有自己的eat()方法能够实现。
    3. 西红柿也是蔬菜,因此Tomato与蔬菜Vegetable类为继承关系,但西红柿吃起来是酸甜的,只有自己的eat()方法能够实现。
    4. 苦瓜也是蔬菜,因此Bitter与蔬菜Vegetable类为继承关系,但苦瓜吃起来是苦,只有自己的eat()方法能够表示。
    5. 因此Vegetable可以设计为“抽象类”。

    1.2抽象类语法

    抽象类语法格式为:abstract class 类名{}式,抽象类是由abstract修饰的,再就是类的统一标志class再加上类名。如一个蔬菜类:

    abstract class Vegetable {
        //成员变量
        public int num;
        //成员方法
        public void fun(){
        }
        //抽象方法
        abstract void show();
        //构造方法
        public void Vegetable() {
        }
    }

    image.gif

    抽象类也是类,内部可以包含普通方法和属性,甚至构造方法。


    1.3抽象类的特性

    特性1,抽象类中不一定要有抽象方法

    abstract class Vegetable {
        public void eat() {
            System.out.println("蔬菜好处");
        }
    }

    image.gif

    抽象类里面有普通的方法也是可以的,并不局限于只能有抽象方法。


    特性2,抽象类里面的抽象方法可以不初始化定义

    abstract class Vegetable {
        abstract void show();
    }

    image.gif

    抽象类里面的抽象方法可以不用初始化,直接用分号(;)结束。


    特性3,一个类继承了抽象类,那么这个类一定要重写抽象类中的抽象方法

    abstract class Vegetable {
        abstract void show();
    }
    class Tomato extends Vegetable {
    }

    image.gif

    运行后输出:

    image.gif编辑 因此,当我们抽象类里面有抽象方法时,只要是有子类继承了这个抽象类就必须在子类中覆盖抽象方法,我们应该这样去写代码:

    abstract class Vegetable {
        abstract void show();
    }
    class Tomato extends Vegetable {
        public void show() {
        }
    }

    image.gif


    特性4,抽象类不能被实例化,但能实例化子类对象。

    abstract class Vegetable {
        public void eat() {
            System.out.println("蔬菜好吃");
        }
    }
    public class Test {
        public static void main(String[] args) {
            Vegetable vegetable = new Vegetable();
        }
    }

    image.gif

    运行后输出:

    image.gif编辑

    但我们使用抽象类实例化子类对象时。

    abstract class Vegetable {
        public void eat() {
            System.out.println("蔬菜好处");
        }
    }
    class Tomato extends Vegetable {
        @Override
        public void eat() {
            System.out.println("西红柿酸甜");
        }
    }
    public class Test {
        public static void main(String[] args) {
           Vegetable vegetable = new Tomato();
           vegetable.eat();
        }
    }

    image.gif

    运行后输出:

    image.gif编辑 我们可以看到,抽象类Vegetable可以实例化它的子类对象Tomato。


    特性5,抽象方法不能被private、static、final修饰,要满足重写的规则

    abstract class Vegetable {
       abstract private void show();
       abstract final void fun();
       abstract private void show();
    }

    image.gif

    运行后输出:

    image.gif编辑


    特性6,抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

    abstract class Vegetable {
      public Vegetable(int a,int b ) {
          System.out.println(a+b);
        }
    }
    class Tomato extends Vegetable {
        public Tomato(int a,int b) {
            super(a,b);
        }
        public void show() {
        }
    }
    public class Test {
        public static void main(String[] args) {
            Vegetable vegetable = new Tomato(2,3);
        }
    }

    image.gif

    运行后输出:

    image.gif编辑


    2.接口


    2.1接口的概念

    接口在我们生活中有USB接口,插排接口等等。USB接口专门是用来传输数据或者充电的,插排接口有两孔插排与三孔插排。

    image.gif编辑

    那么这些接口都是有规定的,规定了实现什么样的操作。USB接口就连接数据,插排就用来充电,这就是他们的规范。因此Java里面也有自己的规范和操作,这些规范和操作我们称之为接口


    2.2语法规则

    接口是通过关键字interface来定义的,在类的基础上把class改为interface即可。

    interface IUSB {
        //...
    }

    image.gif

    以上就是一个接口的定义,定义了一个名为IUSB的接口。

    提示:

      1. 创建接口时, 接口的命名一般以大写字母 I 开头.
      2. 接口的命名一般使用 "形容词" 词性的单词.
      3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.

      2.3接口的特性

      (1)接口不能被实例化

      interface Usb {
          //...
      }
      public class Test {
          public static void main(String[] args) {
              //接口不能被实例化
              Usb usb = new Usb();
          }
      }

      image.gif

      运行后输出:

      image.gif编辑 当我们,实例化一个接口后,编译会认为这个接口为抽象的,因此我们认为接口不能实例化。


      (2)接口中定义成员变量时必须初始化

      interface Usb {
          //接口内的成员变量未初始化
          public int num;
          public String name;
      }
      public class Test {
          public static void main(String[] args) {
          }
      }

      image.gif

      运行后输出:

      image.gif编辑当我们没有对接口内的成员变量进行初始化就运行后,就会报错,因此我们应该初始化接口内成员变量。


      (3)接口内的成员变量默认为public static final类型

      interface Animal {
          //完整的定义
          public static final int num = 10;
          //编译器会默认加上public static final
          String nam = "abc";
      }

      image.gif

      如果我们的变量没有,加上public static final的话,编译器会默认给这些变量加上。


      (4)接口内的方法默认为抽象方法

      interface Usb {
          //抽象方法
          public abstract void show();
          //省略了public abstract 但编译器会默认加上
          void fun();
      }

      image.gif


      (5)接口里面的方法不能有内容

      interface Usb {
          //有方法体的方法
          public void show() {
          }
      }
      public class Test {
          public static void main(String[] args) {
          }
      }

      image.gif

      运行后输出:

      image.gif编辑以上代码展示接口内定义方法时,方法里面不能带有方法体也就是{},但在接口内方法是可以定义的不初始化的。如:

      interface Usb {
          //没有方法体的方法
          public void show();
      }
      public class Test {
          public static void main(String[] args) {
          }
      }

      image.gif

      我们只需要在这个方法后面用分号(;)结束就好了。


      (6)要实现接口中的方法得加上default

      //名为Usb的接口
      interface Usb {
          //default修饰的方法可以在接口内有方法体
          default void show(){
              System.out.println("今天");
          }
      }

      image.gif


      (7)接口内不能有构造方法

      interface Usb {
          public int num = 10;
          //接口中不能有构造方法
          public int Usb(int num){
          }
      }

      image.gif

      运行后输出:

      image.gif编辑

      以上代码运行后结果跟接口里面的方法不能有内容是一样的。


      (8)接口内可以有static修饰的方法

      interface Animal {
          public static void eat() {
              System.out.println("动物爱吃粮");
          }
      }

      image.gif

      当我们运行以上代码是完全可以通过的。


      (9)继承接口时,用implements来修饰接口名

      interface Usb {
          public int num = 10;
      }
      //继承接口用implements
      class Fun implements Usb {
      }

      image.gif

      跟普通类继承不一样,普通类是靠extends来继承,而接口是靠implements来继承的。


      2.4接口间的继承

      接口之间的继承也是用到extends关键字,接口可以实现单继承和多继承与多层继承

      (1)单继承

      interface A {
      }
      interface B extends A {
      }

      image.gif

      所谓单继承,就像接口2继承接口1,在上述代码中展现的就是B接口继承A接口。

      (2)多层继承

      interface A {
      }
      interface B extends A {
      }
      interface C extends B {
      }

      image.gif

      多层继承就是接口3继承接口2,接口2继承接口1这种,上述代码展现的是C继承B,B继承A。

      (3)多继承

      interface A {
      }
      interface B {
      }
      interface C extends A,B {
      }

      image.gif

      多继承,就是一个接口继承多个接口。在我们的类之间继承中这样是不可行的,但是在接口之间这样是完全可以的。


      2.5继承与接口同时继承

      当有以下代码:

      abstract class Animal {
          public void run() {
              System.out.println("跑");
          }
      }
      class Dog extends Animal  {
          @Override
          public void run() {
              super.run();
          }
      }
      class Bird extends  Animal {
          @Override
          public void run() {
              super.run();
          }
      }
      public class Test {
          public static void main(String[] args) {
              Dog dog = new Dog();
              Bird bird = new Bird();
              dog.run();
              bird.run();
          }
      }

      image.gif

      运行后输出:

      image.gif编辑

      以上代码中,当Dog类继承Animal类时里面的run方法是适合Dog类的因为狗的行走方式是跑,而Bird类继承Animl后里面的run方法并不适合Bird类。因为鸟的行走方式是飞,因此我们可以通过一个接口来提供鸟类需要的方法。

      abstract class Animal {
          public void run() {
              System.out.println("跑");
          }
      }
      class Dog extends Animal  {
          @Override
          public void run() {
              super.run();
          }
      }
      interface Irun {
          void run();
      }
      class Bird extends  Animal implements Irun{
          @Override
          public void run() {
              System.out.println("飞");
          }
      }
      public class Test {
          public static void main(String[] args) {
              Dog dog = new Dog();
              Bird bird = new Bird();
              dog.run();
              bird.run();
          }
      }

      image.gif

      运行后输出:

      image.gif编辑 我们可以发现,Bird类继承了Animal类并且继承了Irun接口。最终到了鸟可以飞的一个效果。


      2.6接口使用实例

      (1)实现简单继承接口

      我们知道,接口内的方法是抽象方法。因此,当子类继承了这个接口时,子类里面应当重写接口内的方法。

      interface Animal {
          void eat();
      }
      class Dog implements Animal {
          @Override
          public void eat() {
              System.out.println("吃狗粮");
          }
      }
      public class Test {
          public static void main(String[] args) {
              Animal animal = new Dog();
              animal.eat();
          }
      }

      image.gif

      运行后输出:

      image.gif编辑 以上代码就很好的展示了,继承接口重写方法并输出。


      (2)实现类名引用

      从上方2.4中我学到了接口里面可以有static修饰的方法,因此当我们的接口里有静态方法时,也是可以直接使用类名来引用的。

      interface Animal {
          public static void eat() {
              System.out.println("动物爱吃粮");
          }
      }
      public class Test {
          public static void main(String[] args) {
              Animal.eat();
          }
      }

      image.gif

      运行后输出:

      image.gif编辑


      (3)实现USB接口

      //IUSB.java文件下
      public interface IUSB {
          //打开
          void open();
          //结束
          void close();
      }
      //Mouse.java文件下
      public class Mouse implements IUSB{
          @Override
          //重写打开
          public void open() {
              System.out.println("鼠标点击开始");
          }
          //重写关闭
          @Override
          public void close() {
              System.out.println("鼠标点击结束");
          }
          //鼠标的操作
          public void mouseSerive() {
              System.out.println("点击中~~~~");
          }
      }
      //KeyBoard.java文件下
      public class KeyBoard implements IUSB{
          @Override
          //重写打开
          public void open() {
              System.out.println("键盘打印开始");
          }
          //重写关闭
          @Override
          public void close() {
              System.out.println("键盘打印结束");
          }
          //键盘的操作
          public void keyBoardSerive() {
              System.out.println("打印中~~~~");
          }
      }
      //Computer.java文件下
      public class Computer {
          public static void serive(IUSB str) {
              str.open();
             if (str instanceof Mouse) {
                 //向下转型,因为IUSB接口内没有mouseSerive方法
                 Mouse mouse = (Mouse) str;
                 mouse.mouseSerive();
             }else if (str instanceof KeyBoard) {
                 //向下转型,因为IUSB接口内没有keyBoardSerive方法
                 KeyBoard keyBoard = (KeyBoard) str;
                 keyBoard.keyBoardSerive();
             }
             str.close();
          }
          public static void main(String[] args) {
              //Mouse的匿名对象传给servie方法
              serive(new Mouse());
              //KeyBoard的匿名对象传给servie方法
              serive(new KeyBoard());
          }
      }

      image.gif

      运行后输出:

      鼠标点击开始
      点击中~~~~
      鼠标点击结束
      键盘打印开始
      打印中~~~~
      键盘打印结束

      通过以上几个例子,我们可以发现接口是可以向上转型、向下转型、可以实现多态等效果的。


      2.7抽象类与接口类的区别

      抽象类使用abstract加上class修饰,而接口类使用interface代替class修饰。

      abstract class Animal {
      }
      interface IUSB{
      }

      image.gif


      接口里面的成员方法以及成员变量默认为public static final修饰的。

      interface IUSB{
          //全面写法
          public static final int num = 10;
          //编译器默认为public static final name = "张三";
          String name = "张三";
      }

      image.gif


      抽象类里面的方法被子类继承时子类不必重写此方法,接口类里面的方法被继承时子类必须重写此方法。继承抽象类用关键字extends,继承接口类用implements

      interface IUSB{
          void show();
      }
      class Try implements IUSB {
          @Override
          public void show() {
          }
      }
      abstract class Animal {
          public void fun() {
          }
      }
      class Dog extends Animal {
      }

      image.gif


      抽象类里面的成员变量可以不初始化,而接口类里面的成员变量必须初始化。

      interface IUSB{
          public String name;
          public int age;
      }
      abstract class Animal {
          public String name;
          public int age;
      }

      image.gif

      出现警告:

      image.gif编辑


      抽象类里面的方法可以有代码块,而接口类里面的方法不能有代码块。

      interface IUSB{
          void show(){
          }
      }
      abstract class Animal {
          public void show() {
          }
      }

      image.gif

      运行后输出:

      image.gif编辑


      3.Object类

      Object类是所有类的父类,从何体现,请看下方代码:

      //一个狗类继承了Object类
      class Dog extends Object{
      }
      //一个猫类继承了Object类
      class Cat extends Object{
      }
      //一个抽象类继承了Object类
      abstract class Brid extends Object {
      }
      public class Test {
          public static void main(String[] args) {
              //obj引用了一个Object类对象
              Object obj = new Object();
          }
      }

      image.gif

      通过以上代码,我们可以看到。我们的普通类、抽象类、实例化对象。都可以用Object类来继承或产生对象。


      3.1对象比较equals方法

      当我们有这样一个代码:

      class Student {
          public String name;
          public int age;
          public int score;
      }
      public class Test {
          public static void main(String[] args) {
              Student student1 = new Student();
              Student student2 = new Student();
              student1.name = "zhangsan";
              student2.name = "zhangsan";
              boolean flag = student1.equals(student2);
              System.out.println(flag);
          }
      }

      image.gif

      运行后输出:

      image.gif编辑

      此时我们就有了疑问,明明student1和student2引用了两个对象值是一样为啥还返回false呢?因为此时我们在调用equals方法的时候,调用的是Object类里面的equals方法,我们可以看到毫无用处。以下为Object类里面的equals方法:

      image.gif编辑

      而我们真正需要调用的是String里面的equals方法,也就是以下的代码:

      public boolean equals(Object anObject) {
              if (this == anObject) {
                  return true;
              }
              if (anObject instanceof String) {
                  String anotherString = (String)anObject;
                  int n = value.length;
                  if (n == anotherString.value.length) {
                      char v1[] = value;
                      char v2[] = anotherString.value;
                      int i = 0;
                      while (n-- != 0) {
                          if (v1[i] != v2[i])
                              return false;
                          i++;
                      }
                      return true;
                  }
              }
              return false;
          }

      image.gif

      因此我们得在Student类里面重写一个equals方法来达到我们想要的效果。

      class Student {
          public String name;
          public int age;
          public int score;
          @Override
          public boolean equals(Object obj) {
              Student student = (Student) obj;
              if (this.name.equals(student.name)) {
                  return true;
              }
              return false;
          }
      }
      public class Test {
          public static void main(String[] args) {
              Student student1 = new Student();
              Student student2 = new Student();
              student1.name = "zhangsan";
              student2.name = "zhangsan";
              boolean flag = student1.equals(student2);
              System.out.println(flag);
          }
      }

      image.gif

      运行后输出:

      image.gif编辑

      我们可以看到输出的是true,唯一的难点就是Class类里面的this.name是哪个对象。我们知道在对象的创建时第一条被创建的对象,就是该类的的对象。因此我们的student1这个引用代表的就是Student类。因此student1为当前的对象,所以this.name为student1中的name。而我们在main方法中传参给我们重写的equals方法的参数是student2引用的对象中的name。相信大家把这两个引用区分开了,就不难理解了。

      因此,我们可以发现所有的类都可以引用到Object类但是如果我们没有利用好Object类就不会达到我们想要实现的效果。因此我们在使用Object类的时候要三思而行。


      好了,本次的博文到这里就结束了,感谢您的阅读。如有收获还请给博主一个小小的关注,感谢您的支持。

      image.gif编辑

      下期预告:图书管理系统

      相关文章
      【JavaSE专栏64】抽象类和接口,不能被实例化的类有什么用?
      【JavaSE专栏64】抽象类和接口,不能被实例化的类有什么用?
      202 0
      |
      Java 编译器
      【JAVASE】继承 中
      【JAVASE】继承
      |
      6月前
      |
      Java
      Java的抽象方法和抽象类
      Java的抽象方法和抽象类
      |
      Java 编译器
      【JavaSE】抽象类
      【JavaSE】抽象类
      【JavaSE】抽象类
      |
      6月前
      |
      存储 Java 机器人
      JavaSE学习之--抽象类,接口,内部类(二)
      JavaSE学习之--抽象类,接口,内部类(二)
      55 0
      |
      6月前
      |
      存储 Java 编译器
      JavaSE学习之--抽象类,接口,内部类(三)
      JavaSE学习之--抽象类,接口,内部类(三)
      39 0
      |
      6月前
      |
      Java
      JavaSE学习之--抽象类,接口,内部类(一)
      JavaSE学习之--抽象类,接口,内部类(一)
      89 0
      |
      11月前
      |
      Java 编译器
      JavaSE抽象类和接口
      JavaSE抽象类和接口
      |
      Java 编译器
      【javaSE】 抽象类
      【javaSE】 抽象类
      |
      Java
      java中抽象类和抽象方法
      java中抽象类和抽象方法
      74 0