MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java抽象类的成员变量与方法

2024-12-182.8k 阅读

Java 抽象类的成员变量

在 Java 中,抽象类可以包含成员变量,这些成员变量的作用和普通类中的成员变量类似,但在一些场景下有着特殊的用途。

成员变量的定义与特点

  1. 定义方式:抽象类中的成员变量定义和普通类一样,遵循 Java 的变量声明规则。例如:
abstract class Animal {
    // 定义一个成员变量表示动物的名字
    protected String name;
    // 定义一个成员变量表示动物的年龄
    protected int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

在上述代码中,Animal 是一个抽象类,它包含了两个成员变量 nameage,分别表示动物的名字和年龄。这里使用 protected 修饰符,使得子类可以访问这些变量。

  1. 作用域:成员变量的作用域取决于其修饰符。private 修饰的成员变量只能在抽象类内部访问;protected 修饰的成员变量可以在抽象类及其子类中访问;默认(包访问权限)修饰的成员变量可以在同一个包内的类中访问;public 修饰的成员变量可以在任何地方访问。

  2. 初始化方式:成员变量可以在声明时初始化,也可以在构造函数中初始化。在上面的例子中,我们在构造函数中对 nameage 进行了初始化。此外,还可以使用初始化块来初始化成员变量。例如:

abstract class Plant {
    protected String type;
    {
        type = "Unknown";
    }
}

在这个例子中,Plant 抽象类的 type 成员变量在初始化块中被初始化为 "Unknown"。

成员变量在抽象类中的应用场景

  1. 共享状态:当抽象类的所有子类都需要共享某些状态信息时,可以使用成员变量。例如,在一个图形绘制的抽象类中,可能有一个成员变量表示颜色,所有具体的图形子类(如圆形、矩形)都共享这个颜色信息。
abstract class Shape {
    protected String color;

    public Shape(String color) {
        this.color = color;
    }
}

class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
}

class Rectangle extends Shape {
    private double width;
    private double height;

    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }
}

在这个例子中,Shape 抽象类的 color 成员变量被 CircleRectangle 子类共享,使得它们都具有颜色属性。

  1. 作为子类共性特征的存储:抽象类的成员变量可以存储子类共有的特征。比如,在一个游戏角色的抽象类中,可能有成员变量表示生命值、攻击力等,这些都是所有具体角色子类(如战士、法师)共有的特征。
abstract class GameCharacter {
    protected int health;
    protected int attackPower;

    public GameCharacter(int health, int attackPower) {
        this.health = health;
        this.attackPower = attackPower;
    }
}

class Warrior extends GameCharacter {
    public Warrior(int health, int attackPower) {
        super(health, attackPower);
    }
}

class Mage extends GameCharacter {
    public Mage(int health, int attackPower) {
        super(health, attackPower);
    }
}

这里 GameCharacter 抽象类的 healthattackPower 成员变量存储了游戏角色共有的生命值和攻击力特征,WarriorMage 子类继承了这些特征。

Java 抽象类的方法

抽象方法

  1. 定义与特点:抽象方法是只有方法声明而没有方法体的方法,它必须在抽象类中声明。抽象方法的声明以分号结尾,而不是大括号。例如:
abstract class Bird {
    // 抽象方法,表示鸟飞行的方法
    public abstract void fly();
}

在上述代码中,Bird 抽象类包含了一个抽象方法 fly。抽象方法的特点如下: - 抽象方法没有方法体,因为不同的具体鸟类飞行方式可能不同,所以无法在抽象类中给出统一的实现。 - 抽象方法必须由子类实现,除非子类也是抽象类。这就强制子类根据自身特点来实现这个方法,体现了多态性。

  1. 使用场景:当抽象类代表一种通用概念,而某些行为在不同子类中有不同实现方式时,适合使用抽象方法。比如在一个图形绘制的抽象类中,绘制图形的方法可以定义为抽象方法,因为圆形、矩形等具体图形的绘制方式不同。
abstract class Shape {
    public abstract void draw();
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

class Rectangle extends Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

在这个例子中,Shape 抽象类的 draw 抽象方法由 CircleRectangle 子类分别实现,实现方式各不相同。

非抽象方法

  1. 定义与特点:抽象类中也可以包含非抽象方法,这些方法有完整的方法体。非抽象方法在抽象类中提供了一种默认的实现,子类可以选择继承这个实现,也可以根据需要重写它。例如:
abstract class Animal {
    protected String name;

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

    // 非抽象方法,打印动物的名字
    public void printName() {
        System.out.println("动物的名字是:" + name);
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
}

在上述代码中,Animal 抽象类的 printName 方法是一个非抽象方法,它提供了打印动物名字的默认实现。Dog 子类继承了这个方法,并且可以直接使用。

  1. 使用场景:当抽象类的所有子类都有一些共性的行为,并且这些行为不需要根据子类的不同而改变时,可以将这些行为定义为非抽象方法。比如在一个文件操作的抽象类中,可能有一个非抽象方法用于获取文件的扩展名,因为这个操作对于所有文件类型都是类似的。
abstract class FileHandler {
    protected String filePath;

    public FileHandler(String filePath) {
        this.filePath = filePath;
    }

    // 非抽象方法,获取文件扩展名
    public String getFileExtension() {
        int dotIndex = filePath.lastIndexOf('.');
        if (dotIndex != -1) {
            return filePath.substring(dotIndex + 1);
        }
        return "";
    }
}

class TextFileHandler extends FileHandler {
    public TextFileHandler(String filePath) {
        super(filePath);
    }
}

class ImageFileHandler extends FileHandler {
    public ImageFileHandler(String filePath) {
        super(filePath);
    }
}

在这个例子中,FileHandler 抽象类的 getFileExtension 非抽象方法提供了获取文件扩展名的通用实现,TextFileHandlerImageFileHandler 子类可以直接继承使用。

抽象类方法的访问修饰符

  1. public:使用 public 修饰的抽象类方法可以在任何地方被访问,包括抽象类外部、子类以及不同包中的类。例如:
abstract class PublicAbstractClass {
    public abstract void publicAbstractMethod();
}
  1. protectedprotected 修饰的方法可以在抽象类及其子类中访问,也可以在同一个包内的其他类中访问。例如:
abstract class ProtectedAbstractClass {
    protected abstract void protectedAbstractMethod();
}
  1. 默认(包访问权限):没有显式修饰符的方法具有包访问权限,只能在同一个包内的类中访问。例如:
abstract class PackageAccessAbstractClass {
    abstract void packageAccessAbstractMethod();
}
  1. private:在 Java 中,抽象方法不能被声明为 private,因为抽象方法的目的是让子类去实现,如果是 private 则子类无法访问。但非抽象方法可以是 private,此时这个方法只能在抽象类内部被调用。例如:
abstract class PrivateNonAbstractClass {
    private void privateNonAbstractMethod() {
        System.out.println("这是一个私有非抽象方法");
    }

    public void callPrivateMethod() {
        privateNonAbstractMethod();
    }
}

在这个例子中,PrivateNonAbstractClassprivateNonAbstractMethod 是一个私有非抽象方法,只能通过 callPrivateMethod 方法在抽象类内部调用。

抽象类成员变量与方法的综合应用

示例:图形计算面积和周长

  1. 抽象类定义
abstract class Shape {
    protected String name;

    public Shape(String name) {
        this.name = name;
    }

    // 抽象方法,计算面积
    public abstract double calculateArea();

    // 抽象方法,计算周长
    public abstract double calculatePerimeter();

    // 非抽象方法,打印图形信息
    public void printShapeInfo() {
        System.out.println("图形名称:" + name);
        System.out.println("面积:" + calculateArea());
        System.out.println("周长:" + calculatePerimeter());
    }
}

在这个 Shape 抽象类中,我们定义了一个成员变量 name 用于存储图形的名称。定义了两个抽象方法 calculateAreacalculatePerimeter 分别用于计算图形的面积和周长,因为不同图形的计算方式不同。还定义了一个非抽象方法 printShapeInfo,它利用抽象方法来打印图形的信息,提供了一个通用的图形信息打印逻辑。

  1. 子类实现
class Circle extends Shape {
    private double radius;

    public Circle(String name, double radius) {
        super(name);
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }

    @Override
    public double calculatePerimeter() {
        return 2 * Math.PI * radius;
    }
}

class Rectangle extends Shape {
    private double width;
    private double height;

    public Rectangle(String name, double width, double height) {
        super(name);
        this.width = width;
        this.height = height;
    }

    @Override
    public double calculateArea() {
        return width * height;
    }

    @Override
    public double calculatePerimeter() {
        return 2 * (width + height);
    }
}

CircleRectangle 子类继承自 Shape 抽象类,实现了抽象方法 calculateAreacalculatePerimeter,根据各自图形的特点提供了具体的计算逻辑。同时,它们也继承了 printShapeInfo 方法,可以直接使用该方法打印图形信息。

  1. 测试代码
public class ShapeTest {
    public static void main(String[] args) {
        Shape circle = new Circle("圆形", 5);
        circle.printShapeInfo();

        Shape rectangle = new Rectangle("矩形", 4, 6);
        rectangle.printShapeInfo();
    }
}

ShapeTest 类的 main 方法中,我们创建了 CircleRectangle 的实例,并调用 printShapeInfo 方法打印它们的信息。通过这种方式,充分展示了抽象类成员变量与方法的协同工作,实现了代码的复用和多态性。

示例:员工薪资计算

  1. 抽象类定义
abstract class Employee {
    protected String name;
    protected double baseSalary;

    public Employee(String name, double baseSalary) {
        this.name = name;
        this.baseSalary = baseSalary;
    }

    // 抽象方法,计算奖金
    public abstract double calculateBonus();

    // 非抽象方法,计算总薪资
    public double calculateTotalSalary() {
        return baseSalary + calculateBonus();
    }

    // 非抽象方法,打印员工信息
    public void printEmployeeInfo() {
        System.out.println("员工姓名:" + name);
        System.out.println("基本工资:" + baseSalary);
        System.out.println("奖金:" + calculateBonus());
        System.out.println("总薪资:" + calculateTotalSalary());
    }
}

Employee 抽象类中,成员变量 namebaseSalary 分别存储员工的姓名和基本工资。抽象方法 calculateBonus 用于计算不同类型员工的奖金,因为不同岗位奖金计算方式不同。非抽象方法 calculateTotalSalary 通过调用 calculateBonus 方法来计算总薪资,printEmployeeInfo 方法用于打印员工的详细信息。

  1. 子类实现
class Salesman extends Employee {
    private double salesVolume;

    public Salesman(String name, double baseSalary, double salesVolume) {
        super(name, baseSalary);
        this.salesVolume = salesVolume;
    }

    @Override
    public double calculateBonus() {
        return salesVolume * 0.1;
    }
}

class Programmer extends Employee {
    private int projectCount;

    public Programmer(String name, double baseSalary, int projectCount) {
        super(name, baseSalary);
        this.projectCount = projectCount;
    }

    @Override
    public double calculateBonus() {
        return projectCount * 500;
    }
}

SalesmanProgrammer 子类继承自 Employee 抽象类,实现了 calculateBonus 抽象方法,根据各自岗位的特点计算奖金。同时,它们继承了 calculateTotalSalaryprintEmployeeInfo 方法,方便计算和展示员工薪资信息。

  1. 测试代码
public class EmployeeTest {
    public static void main(String[] args) {
        Employee salesman = new Salesman("张三", 5000, 10000);
        salesman.printEmployeeInfo();

        Employee programmer = new Programmer("李四", 8000, 3);
        programmer.printEmployeeInfo();
    }
}

EmployeeTest 类的 main 方法中,创建了 SalesmanProgrammer 的实例,并调用 printEmployeeInfo 方法打印员工薪资信息。这个示例进一步展示了抽象类成员变量和方法在实际业务场景中的应用,通过抽象和多态,使得代码结构更加清晰,易于维护和扩展。

通过以上详细的讲解和丰富的代码示例,我们深入了解了 Java 抽象类的成员变量与方法。成员变量用于存储抽象类及其子类共有的状态信息,而方法(包括抽象方法和非抽象方法)则定义了抽象类及其子类的行为。抽象类的这些特性为面向对象编程中的代码复用、多态性以及系统的可扩展性提供了强大的支持。在实际开发中,合理地使用抽象类的成员变量与方法,可以设计出更加灵活、健壮的软件系统。例如,在大型企业级应用开发中,对于不同类型业务对象的共性抽象,或者在框架设计中,为不同的具体实现提供统一的抽象接口,抽象类都发挥着重要的作用。同时,在学习和使用过程中,需要注意成员变量的作用域、方法的访问修饰符以及抽象方法与非抽象方法的合理搭配,以充分发挥抽象类的优势。