【JAVA】抽象类和接口

简介: 定义一个类时常常需要定义一些成员方法用于描述类的行为特征,但有时这些方法的实现方式是无法确定的。例如上期定义的 Animal类中的 shout()方法用于描述动物的叫声,但是不同的动物叫声也不相同,因此在 shout()方法中无法准确描述动物的叫声。

240721e0e1c343c49061fc3e9e3161e7.jpg


🏆今日学习目标:抽象类和接口

😃创作者:颜颜yan_

✨个人主页:颜颜yan_的个人主页

⏰本期期数:第二期

🎉专栏系列:JAVA


一、抽象类


定义一个类时常常需要定义一些成员方法用于描述类的行为特征,但有时这些方法的实现方式是无法确定的。例如上期定义的 Animal类中的 shout()方法用于描述动物的叫声,但是不同的动物叫声也不相同,因此在 shout()方法中无法准确描述动物的叫声。


针对上面描述的情况,Java 提供了抽象方法来满足这种需求。抽象方法是使用abstract 关键字修饰的成员方法,抽象方法在定义时不需要实现方法体。


抽象方法的语法格式如下:


    abstract 返回值类型 方法名称(参数列表);


当一个类包含了抽象方法,该类就是抽象类。抽象类和抽象方法一样,必须使用abstract 关键字进行修饰。抽象类的语法格式如下:


    abstract class 抽象类名称{
      属性;
      访问权限 返回值类型 方法名称(参数){ //普通方法
        return [返回值];
      }
      访问权限 abstract 返回值类型 抽象方法名称(参数);   //抽象方法,无方法体
    }


从上面抽象类的语法格式中可以发现,抽象类的定义比普通类多了一个或多个抽象方法,其他地方与普通类的组成基本相同。


抽象类的定义规则


(1)包含抽象方法的类必须是抽象类

(2)声明抽象类和抽象方法时都要使用== abstract== 关键修饰。

(3)抽象方法只需要声明而不需要实现。

(4)如果一个非抽象类继承了抽象类之后,那么该类必须重写抽象类中的全部抽象方法。


示例


Animal类:


//定义抽象类Animal
abstract class Animal {
    //定义抽象方法shout()
    abstract void shout();
}


Dog类:


//定义Dog类继承抽象类Animal
class Dog extends Animal{
    //重写抽象方法shout()
    @Override
    void shout() {
        System.out.println("汪汪汪……");
    }
}


测试类:


//定义测试类
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();  //创建Dog类的对象
        dog.shout();  //通过dog对象调用shout()方法
    }
}


在上述代码中,声明了Animal抽象类,在Animal中定义了抽象方法shout();在Dog类中重写了父类Animal类的抽象方法shout();在测试类中创建了Dog对象,并使用Dog对象调用shout()方法,实现控制台输出的“汪汪汪……”信息。


控制台输出信息如下图:


f4d9e1b8d9cd420681e57c70f35b7857.png


由上图可知,控制台输出“汪汪汪……”,说明了dog对象调用了Dog类中重写的父类Animal的抽象方法shout()。


使用 abstract 关键字修饰的抽象方法不能使用 private 关键字修饰,因为抽象方法必须被子类实现,如果使用了 private 关键字修饰抽象方法,则子类无法实现该方法。

二、接口


接口定义与语法格式


==接口是一种用来定义程序的协议,它用于描述类或结构的一组相关行为。==接口是由抽象类衍生的一个概念,并由此产生了一种编程方式,可以称这种编程方式为面向接口编程。面向接口编程就是将程序的不同的业务逻辑分离,以接口的形式对接不同的业务模块。按口中不实现任何业务逻辑,业务逻辑由接口的实现类完成。当业务需求变更时,只需要修改实现类中的业务逻辑,而不需要修改接口中的内容,以减少需求变更对系统产生的影响。


下面通过现实生活中的例子来类比面向接口编程。例如,鼠标、U 盘等外部设备通过USB 接口来连接计算机,即插即用,非常灵活。如果需要更换与计算机连接的外部设备,只需要拔掉当前 USB 接口上的设备,把新的设备插入即可,这就是面向接口编程的思想。


在Java 中,使用接口的目的是克服单继承的限制,因为一个类只能有一个父类,而一个类可以同时实现多个父接口。在JDK 8之前,接口是由全局常量和抽象方法组成的。JDK 8对接口进行了重新定义,接口中除了抽象方法外,还可以定义默认方法和静态方法,默认方法使用 default 关键字修饰,静态方法使用 static 关键字修饰,而且这两种方法都允许有法体。


接口使用 interface 关键字声明,语法格式如下:


[public] interface 接口名 [extends 接口1,接口2,..]{
[public][static] [final] 数据类型 常量名 =常量;
[public] [abstract] 返回值的数据类型 方法名(参数列表);
[public] static 返回值的数据类型 方法名(参数列表){}
[public] default 返回值的数据类型 方法(参数列表){}
}


上述语法格式中,“extends 接口1,接口2,…”表示一个接口可以有多个父接口,父接口之间使用逗号分隔。接口中的变量默认使用 public static final 进行修饰,即全局常量。接口中定义的抽象方法默认使用 public abstract 进行修饰。


在很多的 Java 程序中,经常看到编写接口中的方法时省略了 public,有很多读者认为它的访问权限是 default,这实际上是错误的。不管写不写访问权限,接口中方法的访问权限永远是 public。


接口本身不能直接实例化,接口中的抽象方法和默认方法只能通过接口实现类的实例对象进行调用。实现类通过== implements== 关键字实现接口,并且实现类必须重写接口中所有的抽象方法。需要注意的是,一个类可以同时实现多个接口,实现多个接口时,多个接口名需要使用英文逗号(,)分隔。


定义接口实现类的语法格式如下:


修饰符 class 类名 implements 接口 1,接口 2,……{
  ……
}


示例


Animal接口:


//定义接口Animal
interface Animal {
        int ID = 1;  //定义全局常量,编号
        String NAME ="牧羊犬";  //定义全局常量,名称
        void shout();   //定义抽象方法 shout()
        public void info();  //定义抽象方法 info()
        static int getID(){  //定义静态方法 getID(),用于返回 ID值
            return Animal.ID;
        }
}


Action接口:


interface Action {
    public  void eat();
}


Dog类:


//定义Dog类实现抽象类Animal和Action
class Dog implements Animal,Action{
    //重写抽象方法shout()
    @Override
    public void shout() {
        System.out.println("汪汪汪……");
    }
    //重写Animal接口中的抽象方法info()
    @Override
    public void info() {
        System.out.println("名称:"+NAME);
    }
//重写Action接口中的抽象方法eat()
    @Override
    public void eat() {
        System.out.println("喜欢吃骨头");
    }
}


测试类:


//定义测试类
public class Main {
    public static void main(String[] args) {
        System.out.println("编号:"+Animal.getID());
        Dog dog = new Dog();  //创建Dog类的对象
        dog.info();  //调用Dog中重写的info()方法
        dog.shout();  //调用Dog中重写的shout()方法
        dog.eat();   //调用Dog中重写的eat()方法
    }
}


在上述代码中,定义了 Animal接口,在Animal接口中定义了全局常量ID和NAME、抽象方法 shout()、info()和静态方法 getID()。定义了Action接口,在Action 接口中定义了抽象方法 eat(),用于输出信息“喜欢吃骨头”。定义了Dog类,Dog 类通过implements关键字实现了Animal接口和Actio接口,并重写了这两个接口中的抽象方法。使用 Animal 接口名直接访问了Animal接口中的静态方法 getID(),输出编号信息。测试代码中创建了 Dog 类的象 dog,并通过 dog 对象调用重写的 info()方法、shout()方法以及 eat()方法。


运行结果:


11b0a1fc42bf4fb6983ce6d2df525a25.png


需要注意的是,接口的实现类必须实现接口中的所有抽象方法,否则程序编译报错。


上述代码演示的是类与接口之间的实现关系。如果在开发中一个子类既要实现接口又要继承抽象类,则可以按照以下语法格式定义子类


  修饰符 class 类名 extends 父类名 implements 接口 1,接日2,……{
      ……
  }


下面对上述代码稍加修改,演示一个类既可以实现接口又可以继承抽象类的情况。修改后的代码如下:


Animal接口:


//定义接口Animal
interface Animal {
        public String NAME ="牧羊犬";  //定义全局常量,名称
        public void shout();   //定义抽象方法 shout()
        public void info();  //定义抽象方法 info()
        }
}


Action接口:


//定义抽象类Action
interface Action {
    ppublic abstract void eat(); //定义抽象方法eat()
}


Dog类:


//定义Dog类继承Action抽象类,并且实现Animal接口
class Dog extends Animal,Action{
  //重写Action接口中的抽象方法eat()
    @Override
    public void eat() {
        System.out.println("喜欢吃骨头");
    }
    //重写Animal中的抽象方法shout()
    @Override
    public void shout() {
        System.out.println("汪汪汪……");
    } 
     //重写Animal接口中的抽象方法info()
      @Override
    public void info() {
        System.out.println("名称:"+NAME);
    }
}


测试类:


//定义测试类
public class Main {
    public static void main(String[] args) {
        System.out.println("编号:"+Animal.getID());
        Dog dog = new Dog();  //创建Dog类的对象
        dog.info();  //调用Dog中重写的info()方法
        dog.shout();  //调用Dog中重写的shout()方法
        dog.eat();   //调用Dog中重写的eat()方法
    }
}


在上述的代码中,定义了Animal接口,其中声明了全局变量NAME(名称)和抽象方法 shout()info()。定义了抽象类 Action,其中定义了抽象方法 eat()。定义了 Dog 类,它通过 extends 关键字继承了 Action 抽象类同时通过 implements 重写了 Animal接口。Dog 类重写了 Animal 接口和 Action 抽象类中的所有抽象方法,包括 shout()方法、info()方法和 eat()方法。测试类代码创建了Dog 类对象 dog,通过该对象分别调用了 ino()、shout()和 eat()方法。


运行结果如图所示:


0e4a4394b30c42e49b0947a845db95c7.png


由图 可知,控制台输出“名称: 牧羊犬”和“汪汪…”,证明 Dog 类成功重写Animal接口的 info()方法和shout()方法;控制台输出“喜欢吃骨头”,证明 Dog 类成功Action 抽象类的 eat()方法。这说明,Dog 类的实例化对象可以访问该类实现的接F抽象类的方法。


在 Java 中,接口不允许继承抽象类,但是允许接口继承接口,并且一个接口可以同时继承多个接口。


示例


Animal接口


//定义接口Animal
interface Animal {
        public String NAME ="牧羊犬";  //定义全局常量,名称
        public void info();  //定义抽象方法 info()
}


Action接口:


//定义Action接口,同时继承Animal接口和Color接口
interface Action extends Animal,Color {
    public abstract void shout();
}


Dog类:


//定义Dog类实现Action接口
    class Dog implements Action{
    //重写Animal接口中的抽象方法info()
    @Override
    public void info() {
        System.out.println("名称:"+NAME);
    }
    //重写抽象方法shout()
    @Override
    public void shout() {
        System.out.println("汪汪汪……");
    }
    //重写Color接口中的抽象方法black
    @Override
    public void black() {
        System.out.println("黑色");
    }
}


测试类:


//定义测试类
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();  //创建Dog类的对象
        dog.info();  //调用Dog中重写的info()方法
        dog.shout();  //调用Dog中重写的shout()方法
        dog.black();   //调用Dog中重写的black()方法
    }
}


上述代码中,定义了 Action 接口并继承 Animal 接口和Color接口,这样 Action 接口中就同时拥有 Animal 接口中的 info()方法、NAME属性和 Color接口中的 black()方法以及本接口中的 shout()方法。


定义了 Dog 类并实现了 Action 接口,这样 Dog 类就必须同时重写Animal 接口中的抽象方法 info()、Color 接口中的抽象方法 black()和 Action 接口中的抽象方法 shout()。


测试代码创建了 Dog 类的对象 dog,通过 dog 对象调用重写的 shout()方法info()方法和 black()方法。


代码的运行结果如图:


43bb2567e8534dd1b8737d9c9b62aae0.png


由图 可知,控制台输出“名称:牧羊犬”,证明 Dog 类成功重写了 Action 接口继承自Animal接口的抽象方法 info();控制台输出“汪汪…”,证明 Dog 类成功重写了 Action接口的抽象方法 shout();控制台输出“黑色”,证明 Dog 类成功重写了 Ation 接口继承自Color 接口的抽象方法 black()。


总结


以上就是今天的学习内容啦~

如果有兴趣的话可以订阅专栏,持续更新呢~

咱们下期再见~


b1a65990fa7f4f37a9d7a4744e0e2d6c.gif

目录
相关文章
|
3天前
|
Java 开发者
Java一分钟之-Lambda表达式与函数式接口
【5月更文挑战第12天】Java 8引入的Lambda表达式简化了函数式编程,与函数式接口结合,实现了代码高效编写。本文介绍了Lambda的基本语法,如参数列表、箭头符号和函数体,并展示了如何使用Lambda实现`Runnable`接口。函数式接口仅有一个抽象方法,可与Lambda搭配使用。`@FunctionalInterface`注解用于确保接口具有单一抽象方法。文章还讨论了常见的问题和易错点,如非函数式接口、类型冲突以及Lambda表达式的局部变量可见性,并提供了避免这些问题的策略。通过理解Lambda和函数式接口,开发者能提高代码可读性和效率。
42 4
|
3天前
|
Java
Java 抽象类
5月更文挑战第4天
|
2天前
|
存储 Java 编译器
Java中的抽象类与接口,在阿里工作5年了
Java中的抽象类与接口,在阿里工作5年了
|
3天前
|
Java API 容器
Java8函数式编程接口:Consumer、Supplier、Function、Predicate
Java8函数式编程接口:Consumer、Supplier、Function、Predicate
8 1
|
3天前
|
Java ice
【Java开发指南 | 第二十九篇】Java接口
【Java开发指南 | 第二十九篇】Java接口
9 0
|
3天前
|
Java
【Java开发指南 | 第二十七篇】Java抽象类
【Java开发指南 | 第二十七篇】Java抽象类
13 0
|
3天前
|
Java
【Java开发指南 | 第九篇】访问实例变量和方法、继承、接口
【Java开发指南 | 第九篇】访问实例变量和方法、继承、接口
14 4
|
3天前
|
安全 Java 调度
Java一分钟:多线程编程初步:Thread类与Runnable接口
【5月更文挑战第11天】本文介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口,并讨论了多线程编程中的常见问题,如资源浪费、线程安全、死锁和优先级问题,提出了解决策略。示例展示了线程通信的生产者-消费者模型,强调理解和掌握线程操作对编写高效并发程序的重要性。
45 3
|
3天前
|
Java API
Java 接口
5月更文挑战第6天
|
3天前
|
存储 安全 Java
Java一分钟之-Map接口与HashMap详解
【5月更文挑战第10天】Java集合框架中的`Map`接口用于存储唯一键值对,而`HashMap`是其快速实现,基于哈希表支持高效查找、添加和删除。本文介绍了`Map`的核心方法,如`put`、`get`和`remove`,以及`HashMap`的特性:快速访问、无序和非线程安全。讨论了键的唯一性、`equals()`和`hashCode()`的正确实现以及线程安全问题。通过示例展示了基本操作和自定义键的使用,强调理解这些概念对编写健壮代码的重要性。
10 0