02、抽象类

  • 在 Java 中,抽象类(Abstract Class) 是一种介于普通类接口之间的半实现 类型
    • 使用 abstract 关键字修饰
      • 既可以有抽象方法(没有方法体),也可以有具体方法(有方法体)。
    • 抽象类不能像普通类那样,通过 new 关键字直接创建对象。即使它没有抽象方法
      • 可以通过 new 关键字创建 匿名内部类 对象,并实现抽象方法

一、抽象类 – 用 extends 表示一种 is-a 的关系 (是一个)


1、基本结构

  • 抽象类:使用 abstract 修饰的类
package org.rainlotus.materials.javabase.a04_oop.abstractclass;

/**
 * @author zhangxw
 */
public abstract class MyAbstractClass {
    /** 实例变量(可非 final) */
    protected int state;

    /** 常量 */
    public static final String TYPE = "ABSTRACT";

    /** 构造器(可带参数) */
    public MyAbstractClass(int state) {
        this.state = state;
    }

    /** 抽象方法(无方法体) */
    public abstract void abstractMethod();

    /** 具体方法(有方法体) */
    public void concreteMethod() {
        System.out.println("State: " + state);
    }

    /** 静态方法 */
    public static void staticMethod() {
        System.out.println("Static method in abstract class");
    }
}

2、抽象类的主要目的

  • 子类提供一个公共的模板以及部分可复用的代码逻辑。同时,强制子类实现某些特定逻辑

  • 其核心设计目标包括:
    • 1、代码复用:通过具体方法共享通用逻辑减少子类重复代码
    • 2、强制规范:通过抽象方法 强制子类必须实现特定逻辑
    • 3、定义类的层次结构:表示类之间的 是什么”(Is-a 关系
      • 如:Animal → Dog/Cat。

  • 典型的应用场景:模板设计模式

3、核心特性:

  1. 抽象类不能privatestaticfinal关键字修饰。
  2. 继承关系
    • 子类通过 extends 继承抽象类。
    • 单继承:一个类只能继承一个抽象类。
  3. 方法实现
    • 抽象方法:用 abstract 声明,无方法体,子类必须实现。
      • 抽象类可以没有抽象方法
      • 抽象类的子类必须实现所有抽象方法。否则,子类自身也需声明为抽象类
    • 具体方法:可包含已实现的方法,供子类复用。
  4. 抽象类不能像普通类那样,通过 new 关键字直接创建对象。即使它没有抽象方法****。
    • 可以通过 new 关键字创建 匿名内部类 对象,并实现抽象方法
  5. 成员变量
    • 可以包含实例变量(非 final)和常量。
  6. 构造器
    • 可以定义构造器,用于初始化成员变量。
    • 虽然抽象类不能创建实例,但依然有构造器,其作用子类构造器调用
  7. 访问修饰符
    • 方法/字段可以是 public、protected、private 或包级私有。

4、抽象类 的使用

  • 通过 继承,在子类中,实现抽象方法。
class ConcreteClass extends MyAbstractClass {
    /** 必须调用父类构造器 */
    public ConcreteClass(int state) {
        super(state);
    }

    /** 必须实现所有抽象方法 */
    @Override
    public void abstractMethod() {
        System.out.println("抽象的子类(extends 实现的子类)中,实现了抽象方法!!!");
    }

    public static void main(String[] args) {
        MyAbstractClass myAbstractClass = new ConcreteClass(2);

        System.out.println(myAbstractClass.state);
        myAbstractClass.abstractMethod();
    }
}
  • 通过创建抽象类的 匿名内部类,在内部类中,实现抽象方法。
class Test {
    public static void main(String[] args) {
        MyAbstractClass myAbstractClass = new MyAbstractClass(1) {
            @Override
            public void abstractMethod() {
                System.out.println("抽象的子类(匿名内部类)中,实现了抽象方法!!!");
            }
        };

        System.out.println(myAbstractClass.state);
        myAbstractClass.abstractMethod();
    }
}

二、抽象方法


1、基本结构

  • 1、使用 abstract 修饰
    • 因此,不能被 privatestaticfinal关键字修饰。
  • 2、没有方法体
  • 3、必须存在于 抽象类 或 接口 中。

2、特点:

  • 抽象方法不能方法体花括号部分)。
    • 只要在方法后出现花括号,即使花括号中没有任何代码也是 方法体

3、abstract 与 final、static、private 不能同时出现的分析

  • abstract 不能与 final 方法同时出现。
    • 对于 abstract 方法,该方法应该有子类重写,final 方法不能被重写。所以,不能同时出现
    • 对于 abstract 类,该类主要用于派生子类。fianl 类不能被派生,所以,不能同时出现
      • abstract 与 final 永远互斥
  • abstract 与 static 不能同时修饰方法
    • static 修饰的方法,用类来调用。
    • 用类调用 abstract 方法没有意义,所以会出错
  • abstract 与 private 不能同时修饰方法
    • private 意味着该方法不能被子类来访问,abstract 要求方法必须有子类来实现

四、抽象类的使用场景

1、模板方法模式(Template Method Pattern)

  • 目的:定义算法骨架,将某些步骤延迟到子类实现。
    • :数据处理的通用流程
public abstract class DataProcessor {
    // 模板方法(final 防止子类覆盖流程)
    public final void process() {
        loadData();
        transformData(); // 抽象方法,子类实现
        saveData();
    }
    
    protected abstract void transformData();
    
    protected void loadData() {
        System.out.println("Loading data from default source...");
    }
    
    protected void saveData() {
        System.out.println("Saving data to default destination...");
    }
}

// 子类实现
public class CsvProcessor extends DataProcessor {
    @Override
    protected void transformData() {
        System.out.println("Transforming CSV data...");
    }
    
    @Override
    protected void loadData() {
        System.out.println("Loading CSV data...");
    }
}

public class XmlProcessor extends DataProcessor {
    @Override
    protected void transformData() {
        System.out.println("Transforming XML data...");
    }
}

2、共享 状态 与 初始化

  • 抽象类适合封装对象的状态和初始化逻辑,通过构造器和字段管理公共状态。
public abstract class Connection {
    private String url;
    private int timeout;
    
    public Connection(String url, int timeout) {
        this.url = url;
        this.timeout = timeout;
        initialize(); // 在构造器中调用初始化方法
    }
    
    private void initialize() {
        System.out.println("Initializing connection to " + url);
        // 执行通用初始化逻辑
    }
    
    public abstract void connect();
    
    public void close() {
        System.out.println("Closing connection...");
    }
}

public class HttpConnection extends Connection {
    public HttpConnection(String url, int timeout) {
        super(url, timeout);
    }
    
    @Override
    public void connect() {
        System.out.println("Establishing HTTP connection...");
    }
}

3、结合 泛型 实现类型安全

  • 抽象类可与泛型结合,定义类型安全的模板:
public abstract class Repository<T> {
    public abstract T findById(String id);
    public abstract void save(T entity);
    
    // 公共方法
    public void logOperation(String operation) {
        System.out.println("Performing " + operation + " on " + getEntityType());
    }
    
    protected abstract String getEntityType();
}


public class UserRepository extends Repository<User> {
    @Override
    public User findById(String id) {
        logOperation("findById");
        return new User(id);
    }
    
    @Override
    public void save(User user) {
        logOperation("save");
        // 保存逻辑
    }
    
    @Override
    protected String getEntityType() {
        return "User";
    }
}

五、常见问题


1、抽象类是否可以继承具体类?


2、抽象类是否可以实现接口?


3、普通类 vs 抽象类


  • 抽象类有得有失:
    • 得:增加了一个功能,可以包含抽象方法。
    • 失:不能像普通类那样,通过 new 关键字直接创建对象。
      • 可以通过 new 关键字直接匿名内部类对象,并实现抽象方法
  • 其他的,普通类有得抽象类都有。
  • 抽象类可以有抽象方法,抽象方法只需申明,无需实现
  • 含有抽象方法的类必须申明为抽象类
  • 抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
  • 抽象方法不能用 static 修饰
  • 抽象方法不能用 private 修饰
  • 抽象方法不能用 final 修饰

4、抽象类中能不能有普通方法?能不能定义构造方法?


  • 抽象类中可以有普通方法
  • 抽象类中可以定义构造方法

5、抽象类能不能被 new 出来?即抽象类的构造方法能不能被调用?


  • 抽象类也可以直接 new,但要配合**内部类**来使用,一般是不可以 new 的。
  • 抽象类的构造方法被子类调用

6、接口和抽象类相同之处和不同之处


1、相同

A、都可以包含抽象方法。Jdk1.8 之后都可以包含静态方法。

B、都不能创建实例

C、子类继承抽象类或实现接口都要求实现所有的抽象方法

否则子类也只能是抽象类。

2、不同

A、接口里不能有普通方法,但抽象类可以没有抽象方法,可以包含普通方法。

B、jdk 1.8 之后接口里可以有 defalult 方法,抽象类中不能有 default 方法。

C、接口里的 field 总是有 public static final

但抽象类中的 field 完全可以是普通的 field

D、接口 不能包含构造器,但抽象类完全可以有构造器。

E、接口不能包含初始化块,但抽象类完全可以有初始化块。

F、接口可以 extends 多个直接的父接口,但抽象类 只能有一个直接父类

G、接口不可以继承普通类,抽象类可以继承普通类。

7、Java 类初始化顺序是怎样的?


  • 普通类:
    • 静态变量
    • 静态代码块
    • 普通变量
    • 普通代码块
    • 构造函数
  • 继承的子类:
    • 父类静态变量
    • 父类静态代码块
    • 子类静态变量
    • 子类静态代码块
    • 父类普通变量
    • 父类普通代码块
    • 父类构造函数
    • 子类普通变量
    • 子类普通代码块
    • 子类构造函数
  • 抽象的实现子类: 接口 - 抽象类 - 实现类
    • 接口静态变量
    • 抽象类静态变量
    • 抽象类静态代码块
    • 实现类静态变量
    • 实习类静态代码块
    • 抽象类普通变量
    • 抽象类普通代码块
    • 抽象类构造函数
    • 实现类普通变量
    • 实现类普通代码块
    • 实现类构造函数
  • 接口注意:
    • 声明的变量都是静态变量并且是 final 的,所以子类无法修改,并且是固定值不会因为实例而变化
    • 接口中能有静态方法,不能有普通方法,普通方法需要用 defalut 添加默认实现
    • 接口中的变量必须实例化
    • 接口中没有静态代码块、普通变量、普通代码块、构造函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值