前端视角 Java Web 入门手册 1.4:Java 的面向对象

259 阅读5分钟

对于 JavaScript 开发者来说,面向对象编程中的封装、继承等特性并不陌生。JavaScript 是一种灵活的编程语言,它同时支持面向对象和面向过程的程序设计范式。由于 JavaScript 中的函数具有 “头等公民” 的地位,可以像变量一样被传递和操作,在日常开发中,开发者更容易编写出函数式编程风格的代码。而 Java 则在面向对象编程方面有着更为严格和复杂的规则。接下来我们来了解下 Java 中面向对象的关键概念

访问限制符

在 Java 中,访问限制符用于控制类、接口、变量、方法等成员的访问范围,以保证程序的安全性和可维护性。Java 中有四种访问限制符:

  1. public:具有最大的访问权限,任何类、接口、包都能访问被 public 修饰的成员,是最宽松的访问限制符。
  2. protected:访问权限次之,被 protected 修饰的成员能被同一个包中的类、接口访问,即使在不同包中,只要是其子类也可以访问。
  3. default:没有显式使用访问限制符时,成员就具有 default 访问权限,只能被同一个包中的类、接口访问。
  4. private:访问权限最为严格,被 private 修饰的成员仅能在本类内部访问。

继承

Java 中,每个类仅有一个直接父类,这就是所谓的单继承机制。Java 设计者选择这种方式,是为了规避多继承带来的复杂性与不可预测性。为了实现类似多继承的功能,Java 引入了接口的概念。一个类可以实现多个接口,这样既保证了代码的可读性和可维护性,又能有效避免多继承可能引发的问题。

在 Java 里,可被继承或实现的对象分为普通类、抽象类、接口这三种类型。

抽象类

抽象类是一种特殊的类,它不能被实例化,主要作用是为子类提供一个通用的模板或基础框架。抽象类中可以包含抽象方法,这些方法只有声明,没有具体的实现,子类必须对其进行实现。

// 定义抽象类Shape
abstract class Shape {
    // 私有属性color
    private String color;
    // 构造函数,用于初始化color属性
    public Shape(String color) {
        this.color = color;
    }
    // 获取color属性的方法
    public String getColor() {
        return color;
    }
    // 抽象方法,计算图形面积,需子类实现
    public abstract double getArea();
}
// 继承Shape类的Circle类
class Circle extends Shape {
    // 私有属性radius
    private double radius;
    // 构造函数,初始化color和radius属性
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    // 重写父类的getArea方法,计算圆形面积
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}
// 继承Shape类的Rectangle类
class Rectangle extends Shape {
    // 私有属性width和height
    private double width;
    private double height;
    // 构造函数,初始化color、width和height属性
    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    // 重写父类的getArea方法,计算矩形面积
    @Override
    public double getArea() {
        return width * height;
    }
}

接口

接口是一种抽象类型,它只定义了一组抽象方法,没有任何具体实现。接口中的所有方法默认都是公共的,只有方法签名,没有方法体。接口主要用于描述对象的行为和功能,规定对象应该具备哪些方法,而不关心这些方法的具体实现细节。一个类可以实现一个或多个接口,通过实现接口中的方法来达成多态性

从Java 8 开始,接口中可以包含默认方法(包含实现的方法)和静态方法

interface Shape {
    String getColor();

    default void setColor(String color) {
        throw new UnsupportedOperationException("setColor() method not implemented");
    }

    double getArea();
}

class Circle implements Shape {
    private String color;
    private double radius;

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

    @Override
    public String getColor() {
        return color;
    }

    @Override
    public void setColor(String color) {
        this.color = color;
    }

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

public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle("red", 5);
        System.out.println("Circle area: " + circle.getArea());
        System.out.println("Circle color: " + circle.getColor());

        // 使用默认实现
        circle.setColor("blue");
        System.out.println("Circle color: " + circle.getColor());
    }
}

重写与重载

方法签名的作用是唯一标识一个方法,以便编译器区分不同的方法。在Java中方法签名是指方法名称、参数类型和个数的组合。方法的参数列表和返回值类型不属于方法签名。

参数列表是指方法声明的参数部分,包含参数类型和参数名称

重写发生在子类中,当子类需要修改从父类继承而来的方法时,就会用到重写。重写要求子类中的方法与父类中被重写的方法具有相同的名称、参数列表和返回类型,并且子类方法的访问权限不能低于父类方法。通常会使用@Override注解来标识重写的方法,这样能帮助开发者检查是否满足重写的所有条件。

重载是指在同一个类中定义多个名称相同,但参数列表不同的方法。通过方法重载,可以实现同名方法的多态性。例如,在 JDK 的java.lang.Math类中,max方法就使用了重载。它可以接收不同类型的参数(如intdouble等),方便开发者求出不同类型数值中的最大值。

final

final关键字在 Java 中有多种用途。当它修饰变量时,表示这个变量只能被赋值一次,一旦赋值就不能再更改。按照编程习惯,常量名通常使用全大写字母表示。

final int COUNT = 10

final也可以修饰方法,被final修饰的方法不能被子类重写。在设计类时,如果某些方法不希望被重写,就可以将其定义为final方法

类似地,如果一个类被final关键字修饰,那么这个类就无法被继承。日常编程中经常使用的String类就是被final修饰的,这保证了String类的不可变性,提高了程序的安全性和稳定性