【Java SE语法篇】9.抽象类和接口

简介: 【Java SE语法篇】9.抽象类和接口


1. 抽象类

1.1 抽象类的概念

在面向对象的概念中,所有的对象都是通过类来描绘的,但是放过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类

1.2 抽象类的语法

在Java 中,一个类如果被abstract 修饰的类成为抽象类,抽象类中被abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。

抽象类的定义格式如下:

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

从以上格式可以看出,抽象类的定义比普通类多了抽象方法,类的其他功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样

1.3 抽象类的特性

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

  2. 抽象方法不能是被 private 修饰的

  3. 抽象方法不能被finalstatic修饰,因为抽象方法要被子类重写

  4. 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则子类也是抽象类,必须要使用 abstract 修饰,如果一个非抽象类继承了抽象类,那么这个子类必须实现抽象类中的全部抽象方法。
abstract class Shape {
    // 抽象方法
    public abstract  void draw();
}
abstract class A extends Shape {
    public abstract void testA();
}
class B extends A {
    @Override
    public void testA() {
    }
    @Override
    public void draw() {
    }
}
  1. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
  2. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

2. 接口

接口用来描述类应该做什么,而不指定它们具体应该如何做。一个类可以实现一个或多个接口。有些情况可能要求符合这些接口,主要有这种要求,就可以使用实现了这个接口的类(即实现类)的对象。

2.1 接口的概念

在Java程序设计语言中,接口不是类,而是对希望符合这个接口的类的一组需求。

接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用

在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型

2.2 接口的语法规则

接口的定义格式与定义类的格式基本相同,将class 关键字换成interface关键字,就定义了一个接口。

public interface 接口名称{
    // 抽象方法
}

注意事项:

  1. 创建接口时,接口的命名一般以大写字母I开头
  2. 接口命名一般使用“形容词”词性的单词。

2.3 接口的使用

接口不能直接被使用,必须有一个"实现类"来实现该接口,实现接口的所有的抽象方法。

public class 类名 interface 接口名称{ // 可以使用,分隔,实现多个接口
    // ...
}

注意:子类和父类之间是extends 继承关系,类与接口之间是implements 实现关系。

2.4 接口的特性

  1. 接口类型是一种引用类型,但是不能直接new 接口的对象

  2. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是
    public abstract,其他修饰符都会报错)

  3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

  4. 重写接口中方法时,不能使用默认的访问权限

  5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

  1. 接口中不能有静态代码块和构造方法(编译错误)

  2. 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
  3. 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类

2.5 实现多个接口

在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承但是一个类可以实现多个接口。下面通过类来表示一组动物。

abstract public class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
    }
}

另外我们再提供一组接口, 分别表示 “会飞的”, “会跑的”, “会游泳的”。

interface IFly {
    void fly();
}
interface IRun {
    void run();
}
interface ISwim {
    void swim();
}

接下来我们创建几个具体的动物:

猫, 是会跑的。

public class Cat extends Animal implements IRun{
    public Cat(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用四条腿跑");
    }
}

鱼,是会游泳的

public class Fish extends Animal implements ISwim{
    public Fish(String name) {
        super(name);
    }
    
    @Override
    public void swim() {
        System.out.println(this.name + "正在游泳");
    }
}

青蛙,既能跑,又能游泳(两栖动物)

public class Fish extends Animal implements IRun,ISwim{
    public Fish(String name) {
        super(name);
    }
    
    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }
    
    @Override
    public void swim() {
        System.out.println(this.name + "正在游泳");
    }
}

注意:一个类实现多个接口,每个接口的抽象方法都要实现,否则类必须设置为抽象类

而鸭子即可以飞,又能跑、还可以游泳。

public class Duck extends Animal implements IFly,IRun,ISwim{
    public Duck(String name) {
        super(name);
    }
    @Override
    public void fly() {
        System.out.println(this.name + "正在用两个翅膀飞");
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在用两条腿游泳");
    }
}

上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口

继承表达的含义是 is-a 语义, 而接口表达的含义是 具有 xxx 特性 .

猫是一种动物,具有跑的特性。

鱼是一种动物,具有游泳的特性。

青蛙是一种,具有跑和游泳的特性。

鸭子是一种动物,具有跑、游泳和飞的特性。

这样设计有什么好处呢? 时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型,而只关注某个类是否具备某种能力.

例如:现在定义一个方法:testRun

public static void testRun(IRun iRun) {
    iRun.run();
}

在这个testRun方法内部, 我们并不关注到底是哪种动物, 只要参数是会跑的, 就行。

public static void main(String[] args) {
    testRun(new Bird("布谷"));
    testRun(new Duck("唐老鸭"));
    testRun(new Dog("旺财"));
}
// 运行结果
布谷正在用两条腿跑
唐老鸭正在用两条腿跑
旺财正在用四条腿跑

甚至参数可以不是 “动物”, 只要会跑!

class Roboot implements IRun{
    @Override
    public void run() {
        System.out.println("机器人正在用两条腿跑");
    }   
}
public class Test {
    public static void testRun(IRun iRun) {
    iRun.run();
  }
    
    public static void main(String[] args) {
        testRun(new Roboot());
    }
}
// 运行结果
机器人正在用两条腿跑

2.6 接口之间的继承

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的

接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.

interface IRun {
    void run();
}
interface ISwim {
    void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRun,ISwim{
    
}
public class Frog extends Animal implements IAmphibious{
    ...
}

通过接口继承创建一个新的接口IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog 类, 就继续要实现 run 方法, 也需要实现 swim 方法.

接口间的继承相当于把多个接口合并在一起.

3. 抽象类和接口的区别

抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别。

核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法。

如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口.

public class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
    }
}
区别 抽象类 接口
结构组成: 普通类 + 抽象方法 抽象方法 + 静态常量
权限: 各种权限 public
子类使用: 使用extends关键字继承抽象类 使用implements 关键字实现接口
关系: 一个抽象类可以实现若干接口 接口不能继承抽象类,但是接口可以使用extends 关键字继承多个父接口
子类权限: 一个子类只能继承一个抽象类 一个子类可以实现多个接口
相关文章
|
2天前
|
Java 开发者
探索 Java 的函数式接口和 Lambda 表达式
【4月更文挑战第19天】Java 中的函数式接口和 Lambda 表达式提供了简洁、灵活的编程方式。函数式接口有且仅有一个抽象方法,用于与 Lambda(一种匿名函数语法)配合,简化代码并增强可读性。Lambda 表达式的优点在于其简洁性和灵活性,常用于事件处理、过滤和排序等场景。使用时注意兼容性和变量作用域,它们能提高代码效率和可维护性。
|
2天前
|
Java
Java接口中可以定义哪些方法?
【4月更文挑战第13天】
5 0
Java接口中可以定义哪些方法?
|
4天前
|
设计模式 Java
Java接口与抽象类
Java接口与抽象类
15 0
|
8天前
|
安全 Java 编译器
接口之美,内部之妙:深入解析Java的接口与内部类
接口之美,内部之妙:深入解析Java的接口与内部类
26 0
接口之美,内部之妙:深入解析Java的接口与内部类
|
8天前
|
存储 安全 Java
Java语法掌握:打好编程基础的关键(二)
Java语法掌握:打好编程基础的关键
41 0
|
8天前
|
存储 Java
Java语法掌握:打好编程基础的关键(一)
Java语法掌握:打好编程基础的关键
10 0
Java语法掌握:打好编程基础的关键(一)
|
11天前
|
存储 Java
java接口和内部类
java接口和内部类
|
12天前
|
缓存 安全 Java
Java中函数式接口详解
Java 8引入函数式接口,支持函数式编程。这些接口有单一抽象方法,可与Lambda表达式结合,简化代码。常见函数式接口包括:`Function<T, R>`用于转换操作,`Predicate<T>`用于布尔判断,`Consumer<T>`用于消费输入,`Supplier<T>`用于无参生成结果。开发者也可自定义函数式接口。Lambda表达式使实现接口更简洁。注意异常处理和线程安全。函数式接口广泛应用于集合操作、并行编程和事件处理。提升代码可读性和效率,是现代Java开发的重要工具。
26 0
|
4月前
|
Java
Java抽象类和接口
Java抽象类和接口
36 0
|
6月前
|
Java API 定位技术
Java---抽象类和接口
Java---抽象类和接口