对于 JavaScript 开发者来说,面向对象编程中的封装、继承等特性并不陌生。JavaScript 是一种灵活的编程语言,它同时支持面向对象和面向过程的程序设计范式。由于 JavaScript 中的函数具有 “头等公民” 的地位,可以像变量一样被传递和操作,在日常开发中,开发者更容易编写出函数式编程风格的代码。而 Java 则在面向对象编程方面有着更为严格和复杂的规则。接下来我们来了解下 Java 中面向对象的关键概念
访问限制符
在 Java 中,访问限制符用于控制类、接口、变量、方法等成员的访问范围,以保证程序的安全性和可维护性。Java 中有四种访问限制符:
- public:具有最大的访问权限,任何类、接口、包都能访问被 public 修饰的成员,是最宽松的访问限制符。
- protected:访问权限次之,被 protected 修饰的成员能被同一个包中的类、接口访问,即使在不同包中,只要是其子类也可以访问。
- default:没有显式使用访问限制符时,成员就具有 default 访问权限,只能被同一个包中的类、接口访问。
- 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方法就使用了重载。它可以接收不同类型的参数(如int、double等),方便开发者求出不同类型数值中的最大值。
final
final关键字在 Java 中有多种用途。当它修饰变量时,表示这个变量只能被赋值一次,一旦赋值就不能再更改。按照编程习惯,常量名通常使用全大写字母表示。
final int COUNT = 10;
final也可以修饰方法,被final修饰的方法不能被子类重写。在设计类时,如果某些方法不希望被重写,就可以将其定义为final方法
类似地,如果一个类被final关键字修饰,那么这个类就无法被继承。日常编程中经常使用的String类就是被final修饰的,这保证了String类的不可变性,提高了程序的安全性和稳定性