JavaSE之面向对象编程-抽象类与接口

本文详细讲解了Java中的抽象类和接口。从抽象类的定义、使用原则和相关规定,到模板设计模式的应用,再到接口的定义、使用限制及应用场景,最后讨论了工厂模式和代理模式。强调了接口优先原则,并提供了实际代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.抽象类的定义与使用

定义:抽象类只是在普通类的基础上多扩充了一些抽象方法而已。
抽象方法:指的是只声明而未实现的方法,(没有方法体)

所有抽象方法要求使用abstract来定义,并且抽象方法所在的类也一定要使用abstract来定义,表示抽象类。

2.抽象类的使用原则

看一道例题:

abstract class A{
    public A(){ //3.调用父类构造
        this.print() ; //4.调用被子类覆写的方法
    }
    public abstract void print() ;
}
class B extends A{
    private int num = 100 ;
    public B(int num) { //2.调用子类实例化对象
        super() ; //3.隐含一行语句,实际要先调用父类构造
        this.num = num ; //7.为类中属性初始化,30
    }
    public void print() { //5.此时子类对象的属性还没有被初始化
        System.out.println(this.num) ; //6.对应其数据类型的默认值,输出0
    }
}
public class Test{
    public static void main(String[] args) {
        new B(30).print(); //1.实例化子类对象
    }
}

调用过程:1234567
输出:
在这里插入图片描述
抽象类的使用原则:
a. 所有的抽象类必须有子类(abstract与final不能同时使用)
b. 抽象类的子类必须覆写抽象类的所有抽象方法(abstract与private也不能同时使用)
c. 抽象类要想创建实例化对象,必须通过子类向上转型为其实例化。(抽象类无法创建实例化对象)

abstract class Person {
    private String name;
    private String getName(){
        return name;
    }
    public abstract void fun();
}
class Student extends Person{
    public void fun(){
        System.out.println("子类继承抽象类");
    }
}

public class Test{
    public static void main(String[] args){
        Person per = new Student();
        per.fun();
    }
}

3.抽象类的相关规定

a. 抽象类也允许提供构造方法,并且子类也照样遵循对象实例化流程,先调用父类的调用方法而后调用子类构造方法

b. 抽象类中不允许定义任何的抽象方法,但抽象类依然无法直接创建实例化对象。

c. abstract与final不能一起用;abstract与private也不能一起用;

d. 内部抽象类(了解)
内部抽象类中也可以使用static定义来描述外部抽象类。如果现在外部抽象类中使用了static那么就是语法错误,但是内部抽象类允许使用static。

4.模板设计模式(封装算法)

-------基于抽象类,核心是封装算法。

看一个例子:
咖啡冲泡法

  1. 将水煮沸
  2. 用沸水冲泡咖啡
  3. 将咖啡倒进杯子
  4. 加糖和牛奶

茶冲泡法

  1. 将水煮沸
  2. 用沸水浸泡茶叶
  3. 把茶倒进杯子
  4. 加柠檬
class Coffee {
    /*
    咖啡冲泡法(算法)
     */
    void prepareRecipe() {
        boilWater();
        brewCoffeeGrings();
        pourInCup();
        addSugarAndMilk();
    }
    /**
     * 将水煮沸
     */
    // 重复代码
    public void boilWater() {
        System.out.println("Boiling Water");
    }
    /**
     * 冲泡咖啡
     */
    public void brewCoffeeGrings() {
        System.out.println("Dripping Coffee through filter");
    }
    /**
     * 把咖啡倒进杯子中
     */
    // 重复代码
    public void pourInCup() {
        System.out.println("Pouring into cup");
    }
    /**
     * 加糖和牛奶
     */
    public void addSugarAndMilk() {
        System.out.println("Adding Sugar and Milk");
    }
}

class Tea {
    /*
   茶类冲泡法(算法)
    */
    void prepareRecipe() {
        boilWater();
        steepTeaBag();
        pourInCup();
        addLemon();
    }
    /**
     * 将水煮沸
     */
    public void boilWater() {
        System.out.println("Boiling Water");
    }
    /**
     * 冲泡茶
     */
    public void steepTeaBag() {
        System.out.println("Steeping the tea");
    }
    /**
     * 把茶倒进杯子中
     */
    public void pourInCup() {
        System.out.println("Pouring into cup");
    }
    /**
     * 加柠檬
     */
    public void addLemon() {
        System.out.println("Adding Lemon");
    }
}

我们在这两个类中发现了重复代码,因此我们需要重新理一下我们的设计。既然茶和咖啡是如此的相似,因此我们应该将共同的部分抽取出来,放进一个基类中。
在这里插入图片描述

abstract class CaffeineBeverage {
    /**
     * 现在用同一个prepareRecipe()方法处理茶和咖啡。
     * 声明为final的原因是我们不希望子类覆盖这个方法!
     */
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }
    /**
     * 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现
     */
    abstract void brew();
    abstract void addCondiments();
    void boilWater() {
        System.out.println("Boiling water");
    }
    void pourInCup() {
        System.out.println("Pouring into cup");
    }
}

class Tea extends CaffeineBeverage {
    void brew() {
        System.out.println("Steeping the tea");
    }
    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}
class Coffee extends CaffeineBeverage {
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }
    void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
}

除了两个方法有共同点之外,咖啡和茶还有什么共同点呢?
观察咖啡和茶的冲泡法我们会发现,两种冲泡法都采用了相同的算法:

  1. 将水煮沸
  2. 用热水泡饮料
  3. 把饮料倒进杯子
  4. 在饮料内加入适当的调料
    那么,我们可以将prepareRecipe()也抽象化。
    在这里插入图片描述
/*
父类实现
 */
abstract class CaffeineBeverage {
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        //询问顾客是否需要加料
        if(customerWantsCondiments()) {
            addCondiments();
        }
    }

    abstract void brew();

    abstract void addCondiments();

    void boilWater() {
        System.out.println("Boilint water");
    }

    void pourInCup() {
        System.out.println("Pouring into cup");
    }

    /**
     * 钩子方法
     * 超类中通常是默认实现
     * 子类可以选择性的覆写此方法
     * @return
     */
    boolean customerWantsCondiments() {
        return true;
    }
}

/*
子类实现
 */
class Coffee extends CaffeineBeverage {
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }

    void addCondiments() {
        System.out.println("Add Sugar and Milk");
    }

    /**
     * 子类覆写了钩子函数,实现自定义功能
     * @return
     */
    public boolean customerWantsCondiments() {
        String answer = getUserInput();
        if(answer.equals("y")) {
            return true;
        }else {
            return false;
        }
    }
    private String getUserInput() {
        String answer = null;
        System.out.println("您想要在咖啡中加入牛奶或糖吗 (y/n)?");
        Scanner scanner = new Scanner(System.in);
        answer = scanner.nextLine();
        return answer;
    }
}

class Tea extends CaffeineBeverage {
    void brew() {
        System.out.println("Steeping the tea");
    }

    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        CaffeineBeverage tea = new Tea();
        CaffeineBeverage coffee = new Coffee();
        System.out.println("\nMaking tea...");
        tea.prepareRecipe();
        System.out.println("\nMaking Coffee");
        coffee.prepareRecipe();
    }
}

输出:
在这里插入图片描述

开闭原则(OCP):一个软件实体如类、函数、模块应该对扩展开放对修改关闭。

模板方法定义了一个算法的步骤,并且允许子类为一个或 多个步骤提供具体实现

模板(模板方法)模式:Servlet、AQS
在一个方法中定义一个方法的骨架,并将一些具体步骤延迟到子类中实现。
模板模块使得子类可以在不改变算法结构的基础上,重新具体定义算法中的某些步骤。

5.接口的定义与使用

接口优先原则:在一个操作既可以使用抽象类又可以使用接口的时候,优先考虑用接口。

接口的定义
接口就是一个抽象方法和全局常量的集合(JDK8以前)
接口使用interface定义
接口的命名规范:接口前加 I 来区分接口与类。

子类实现接口使用implements关键字,并且子类可以同时实现多个父接口(使用接口实现多继承)一般使用接口+Impl来命名子类。

5.1接口的使用限制
5.1.1

接口中只有public权限(不管是属性还是方法。其权限均为public)
接口中public、static、final、abstract均可以省略
阿里编码规约:接口方法和属性不要加任何修饰符,public也不要加,保证代码简洁性。

5.1.2

当一个子类既需要实现接口又需要继承抽象类时,请先使用extend继承一个抽象类,而后使用implements实现多个接口

5.1.3

抽象类可以使用implements实现多个接口,单接口不能extends抽象类。

5.1.4

接口可以使用extends继承多个父接口。

5.2接口应用

a.定义操作标准(USB2.0标准)
b.表示一种能力,进行一项操作
c.在分布式开发中暴露远程服务方法

6.工厂模式

6.1简单工厂:用于没有产品族并且产品个数较少

专门定义一个类(第三方)来创建其他实例(解耦,将客户端创建对象的操作解耦到外部第三方类),被创建的实例通常都具有相同的父类。
(解耦总的一句话来说,减少依赖,抽象业务和逻辑,让各个功能实现独立。)

package practive.bit.Demo2;

import java.util.Scanner;

interface IBook {
    void sellBook();
}

class Cartoon implements IBook {
    public void sellBook() {
        System.out.println("出售漫画书");
    }
}

class Literature implements IBook {
    public void sellBook() {
        System.out.println("出售文学书");
    }
}

class Magazine implements IBook {
    public void sellBook() {
        System.out.println("出售杂志");
    }
}

// 第三方工厂类
class BookFactory {
    public static IBook getInstance(String type) {
        if(type.equals("漫画书")) {
            return new Cartoon();
        }
        else if(type.equals("文学书")) {
            return new Literature();
        }
        else if(type.equals("杂志")) {
            return new Magazine();
        }
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入你想要买的图书类型:");
        String type = sc.nextLine();
        IBook book = new BookFactory().getInstance(type);
        book.sellBook();
    }
}

在这里插入图片描述
组成:
a.一个抽象产品类
b.N个具体产品类
c.一个工厂类

6.2工厂方法模式(优点在于横向发展)–产品族

定义一个用来创建对象 的接口,让子类实现实例化哪一个工厂。

package practive.bit.Demo2;

import java.util.Scanner;

// 图书接口
interface IBook {
    void sellBook();
}

// 漫画书
class Cartoon implements IBook {
    public void sellBook() {
        System.out.println("出售漫画书");
    }
}

// 文学书
class Literature implements IBook {
    public void sellBook() {
        System.out.println("出售文学书");
    }
}

// 杂志
class Magazine implements IBook {
    public void sellBook() {
        System.out.println("出售杂志");
    }
}

// 图书工厂接口
interface IBookFactory {
    IBook creatBook();
}

// 漫画书生产商
class CartoonFactory implements IBookFactory {
    public IBook creatBook() {
        return new Cartoon();
    }
}

// 文学书生产商
class LiteratureFactory implements IBookFactory {
    public IBook creatBook() {
        return new Literature();
    }
}

// 杂志生产商
class MagazineFactory implements IBookFactory {
    public IBook creatBook() {
        return new Magazine();
    }
}

public class Test {
    public static void main(String[] args) {
        IBookFactory factory = new CartoonFactory();
        IBook book = factory.creatBook();
        book.sellBook();
    }
}

在这里插入图片描述
组成:
a.一个抽象产品类
b.N个具体产品类
c.一个抽象工厂
d.对于具体工厂(每个产品族对应一个具体工厂)

7.代理模式–Spring-AOP

特点:两个子类共同实现一个接口,其中一个子类负责真是业务实现,另一个子类完成辅助真实业务主题的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值