【Java22】抽象类

编写类的时候,通常会为这个类定义一些方法,用来描述该类的行为方式,这些方法通常应该有具体的实现代码。但在有些情况下,父类只知道子类应该有什么行为,却不知道子类该如何实现这些行为。

例如,父类Shape知道,它的子类应该能够计算周长。但是不同的子类计算周长的方式不同,即Shape类不知道子类该如何实现这个方法。

既然这样,那父类干脆不要知道这个行为就好了。但是这样一来,假设一个Shape引用变量Shape a实际上指向的是Shape的子类的实例,那么这个引用变量就无法调用子类的方法了。除非将这个引用变量强制转换成子类类型(回忆一下向上转换和强制转换),才能调用子类的方法。

而想要让程序更加灵活,只需要在父类里也声明子类的方法,但是不做具体实现即可。但是只声明一个空方法,有可能会让指向父类的引用变量直接调用,出现一些不可预期的错误。所以再加一道保险,就是把这个方法声明为抽象方法

在这里插入图片描述

abstract修饰符

使用abstract修饰的方法称为抽象方法;使用abstract修饰的类称为抽象类。

  • 含有抽象方法的类必须定义为抽象类;
  • 抽象类里可以不包含抽象方法。

抽象方法很简单,就一条要求:没有方法体。

抽象类稍微事儿多一点:

  1. 抽象类不能被实例化,不能使用new调用它的构造器创建实例。即使抽象类里没有抽象方法,也不能实例化。
  2. 抽象类可以包含成员变量、方法、构造器、初始化块、内部类(接口|枚举)等5种成分。需要重复一遍,抽象类的构造器不能用来实例化,只能被子类调用。
  3. 只要含有抽象方法,它就必须被定义成抽象类。

我们可以把抽象和具象对比起来看:父类里干的都是抽象的事儿,具象化是由子类实现的。

注意,抽象方法不是空方法。它没有方法体。所谓没有方法体,就是没有{}。直接在方法定义后加分号。

public abstract class Shape
{
  {
    System.out.println("执行Shape的初始化块");
  }
  private String color;
  // 计算周长的抽象方法
  public abstract double calPerimeter();
  // 返回形状类型的抽象方法
  public abstract String getType();
  // 构造器,这个构造器不能用来实例化,只能被子类调用
  public Shape(){}
  public Shape(string color)
  {
    System.out.println("执行Shape的构造器");
    this.color = color;
  }
  // color的get和set方法
}
  • 因为Shape类里定义了抽象方法,所以Shape只能是抽象类;
  • Shape类的两个构造器都不能用来实例化Shape对象,只能被它的子类调用。

定义Triangle类,派生自Shape类

public class Triangle extends Shape
{
  // 三角形的三条边
  private double a;
  private double b;
  private double c;
  public Triangle(String color, double a, double b, double c)
  {
    super(color); // 调用父类带一个参数的构造器
    this.setSides(a, b, c);
  }
  public void setSides(double a, double b, double c)
  {
    if (a >= b + c || b >= a + c || c >= a + b)
    {
      System.out.println("三角形两边之和必须大于第三边");
      return;
    }
    this.a = a;
    this.b = b;
    this.c = c;
  }
  // 重写(Override)Shape类中的抽象方法
  public double calPerimeter()
  {
    return a + b + c;
  }
  public String getType()
  {
    return "三角形";
  }
}

定义Circle类,同样是Shape的子类

public class Circle extends Shape
{
  private double radius;
  public Circle(String color, double radius)
  {
    super(color);
    this.radius = radius;
  }
  public void setRadius(double radius)
  {
    this.radius = radius;
  }
  // 重写父类的抽象方法
  public double calPerimeter()
  {
    return 2 * Math.PI * raduis;
  }
  public String getType()
  {
    return getColor() + "圆"; // getColor是父类的普通方法
  }
}

main中使用

public class Class
{
  public static void main(String[] args)
  {
    Shape s1 = new Triagle("黑色", 3, 4, 5);
    Shape s2 = new Circle("红色", 3);
    System.out.println(s1.getType());
    System.out.println(s1.calPerimeter());
    System.out.println(s2.getType());
    System.out.println(s2.calPerimeter());
  }
}

注意,s1和s2都是Shape引用变量,但是因为Shape里定义了getType和calPerimeter方法,所以不需要强制类型转换也可以调用子类的方法。

抽象类是多态的重要组成机制。

多态的另两个重要组成机制是重载和重写。

抽象类的作用是啥?

类似于顶层设计,先用父类给子类的行为做一个设定。子类必须按照父类的设定,实现具体方法,从而避免子类设计过于随意。

这种父类先打个样,子类照着往里填写的方式,就是模版模式。模版就是基于抽象类实现的。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值