Java 面向对象编程基础:类、对象与封装

Java 的核心编程范式之一是面向对象编程(Object-Oriented Programming, OOP)。它将现实世界中的事物抽象为“对象”,并通过对象之间的交互来构建程序。理解 类(Class)对象(Object)封装(Encapsulation) 是掌握 Java OOP 的基石。本文将详细介绍这些概念并结合代码示例进行说明。

一、面向对象思想

面向对象是一种解决问题的思维方式。它认为世界是由对象组成的,程序关注的是对象本身及其属性(状态)、行为(功能),以及对象之间的交互。这与 面向过程 注重解决问题的具体步骤形成对比。

  • 面向过程:关注“如何做”。例如洗衣服,需要详细描述每一步:接水、浸泡、搓洗、漂洗、拧干、晾晒。

  • 面向对象:关注“谁来做”。例如洗衣服,涉及几个对象:“脏衣服”、“洗衣机”、“洗衣粉”。程序关注的是这些对象各自的状态(衣服的脏污程度、洗衣机的模式、洗衣粉的量)和行为(洗衣机有“启动”、“选择模式”等功能,洗衣粉有“溶解去污”的功能),以及它们如何交互(人把衣服和洗衣粉放进洗衣机,启动洗衣机)。

两者各有适用场景,OOP 的优势在于能更好地模拟现实世界,提高代码的可维护性、复用性和可扩展性。

二、类(Class):对象的蓝图

1. 定义

是对具有相同属性和行为的一组对象的 抽象描述。它定义了这类对象共有的 属性(成员变量)行为(成员方法)。类本身是抽象的,它充当了创建具体对象的 模板蓝图

例如,我们可以定义一个 Person 类来描述“人”这个概念:

  • 属性(成员变量)name(姓名)、age(年龄)。

  • 行为(成员方法)introduce()(自我介绍)、haveBirthday()(过生日)。

2. 类的声明语法


Java

public class ClassName {
    // 成员变量 (属性)
    dataType variableName1;
    dataType variableName2;

    // 构造方法 (用于创建对象)
    public ClassName(parameters) { ... }

    // 成员方法 (行为)
    returnType methodName1(parameters) { ... }
    returnType methodName2(parameters) { ... }
}

3. 代码示例:定义一个 Person 类


Java

public class Person {
    // 成员变量 (属性)
    private String name; // 使用 private 封装,后面会解释
    private int age;

    // 构造方法 1:无参构造
    public Person() {
        this.name = "Unknown";
        this.age = 0;
    }

    // 构造方法 2:带参数构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 成员方法:自我介绍
    public void introduce() {
        System.out.println("Hello, my name is " + name + ", I'm " + age + " years old.");
    }

    // 成员方法:过生日
    public void haveBirthday() {
        age++; // 年龄增加一岁
        System.out.println("Happy Birthday! Now I'm " + age);
    }

    // Getter 方法:获取姓名 (封装的一部分)
    public String getName() {
        return name;
    }

    // Setter 方法:设置姓名 (封装的一部分)
    public void setName(String name) {
        this.name = name;
    }

    // Getter 方法:获取年龄
    public int getAge() {
        return age;
    }

    // Setter 方法:设置年龄 (可加入校验逻辑)
    public void setAge(int age) {
        if (age >= 0) { // 简单的校验,年龄不能为负
            this.age = age;
        } else {
            System.out.println("Invalid age! Age cannot be negative.");
        }
    }
}

解释:

  • private String name;private int age;:声明了两个私有成员变量,代表人的姓名和年龄。

  • public Person()public Person(String name, int age):这是两个构造方法。它们用于在创建 Person 对象时进行初始化。第一个无参构造给 nameage 赋予默认值;第二个带参构造允许在创建对象时指定初始姓名和年龄。

  • public void introduce()public void haveBirthday():这是两个成员方法,定义了 Person 对象可以执行的行为:自我介绍和过生日。

  • public String getName()public void setName(String name)public int getAge()public void setAge(int age):这些是用于访问和修改私有成员变量的 GetterSetter 方法,是实现封装的关键。

三、对象(Object):类的实例

1. 定义

对象 的具体实例。类是抽象的蓝图,对象是根据这个蓝图创建出来的、存在于内存中的具体实体。每个对象都拥有类所定义的属性和方法,并且各自拥有独立的属性值。

例如,根据 Person 类,我们可以创建多个具体的“人”对象:

  • person1: name="Alice", age=25

  • person2: name="Bob", age=30

2. 创建对象(实例化)

使用 new 关键字后跟类的构造方法来创建对象。这个过程称为 实例化


Java

ClassName objectName = new ClassName(arguments); // 根据构造方法选择参数

3. 使用对象

创建对象后,可以通过 点号 . 操作符来访问对象的成员变量(通常通过 Getter/Setter)和调用成员方法。


Java

objectName.methodName(arguments); // 调用方法
objectName.getVariableName();     // 通过 Getter 获取属性值
objectName.setVariableName(value); // 通过 Setter 设置属性值

4. 代码示例:创建并使用 Person 对象


Java

public class Main {
    public static void main(String[] args) {
        // 1. 使用无参构造创建 Person 对象 person1
        Person person1 = new Person(); // name="Unknown", age=0
        person1.introduce(); // 输出: Hello, my name is Unknown, I'm 0 years old.

        // 2. 使用带参构造创建 Person 对象 person2
        Person person2 = new Person("Alice", 25);
        person2.introduce(); // 输出: Hello, my name is Alice, I'm 25 years old.

        // 3. 使用 Setter 方法修改 person1 的属性
        person1.setName("Bob");
        person1.setAge(30);
        person1.introduce(); // 输出: Hello, my name is Bob, I'm 30 years old.

        // 4. 使用 Getter 方法获取 person2 的年龄
        System.out.println(person2.getName() + "'s age is " + person2.getAge()); // 输出: Alice's age is 25

        // 5. 调用行为方法
        person2.haveBirthday(); // 输出: Happy Birthday! Now I'm 26
        person2.introduce();    // 输出: Hello, my name is Alice, I'm 26 years old.

        // 6. 尝试设置无效年龄 (通过 Setter 的校验)
        person1.setAge(-5);     // 输出: Invalid age! Age cannot be negative.
        System.out.println(person1.getAge()); // 输出: 30 (保持不变)
    }
}

解释:

  • Person person1 = new Person();:使用无参构造创建对象 person1,其初始状态为 name="Unknown", age=0

  • Person person2 = new Person("Alice", 25);:使用带参构造创建对象 person2,初始状态为 name="Alice", age=25

  • person1.setName("Bob")person1.setAge(30):通过 Setter 方法修改 person1 的属性值。

  • person2.getName()person2.getAge():通过 Getter 方法获取 person2 的属性值。

  • person2.haveBirthday():调用成员方法改变对象状态(age 增加)。

  • person1.setAge(-5):尝试设置非法值,Setter 方法中的校验逻辑阻止了修改,并打印错误信息。

四、封装(Encapsulation):保护数据

1. 定义

封装 是面向对象编程的三大基本特性之一(另外两个是继承和多态)。封装的核心思想是将对象的 数据(属性) 和对数据的 操作(方法) 捆绑在一起,形成一个独立的单元(即类)。同时,隐藏对象内部的实现细节,只对外暴露有限的、可控的 接口(通常是公共方法) 来与对象进行交互。

2. 目的

  • 提高安全性:防止外部代码随意、非法地修改对象的内部状态。例如,在 setAge 方法中加入年龄不能为负的校验逻辑。

  • 提高可维护性:内部实现细节的改变(如属性名改变、校验逻辑调整)不会影响依赖于对象公共接口的外部代码。

  • 提高复用性:封装好的类可以被当作一个“黑盒”组件在其他地方复用,使用者只需关注接口。

  • 简化复杂性:使用者无需了解对象内部的复杂逻辑,只需知道如何使用提供的公共方法。

3. 实现方式:访问权限修饰符

Java 使用 访问权限修饰符 来控制类、成员变量和方法的可见性:

  • private:私有的。仅在 当前类内部 可以访问。这是实现封装最常用的修饰符,用于修饰不希望被外部直接访问的成员变量(属性)。

  • (default) / 无修饰符:默认的 / 包私有的。在 同一个包内 可以访问。

  • protected:受保护的。在 同一个包内不同包的子类 中可以访问。

  • public:公共的。任何地方 都可以访问。通常用于修饰类本身、构造方法和希望外部调用的成员方法(如 Getter/Setter)。

4. 封装的核心实践:Getter 和 Setter

对私有(private)成员变量的访问和修改,通常通过公共的(publicGetter(获取值)和 Setter(设置值)方法来实现。这被称为 属性封装

  • Getter:命名通常为 getXxx()Xxx 是属性名,首字母大写),没有参数,返回对应属性的值。

  • Setter:命名通常为 setXxx(),有一个与属性类型相同的参数(用于接收新值),返回类型为 void。可以在 Setter 方法中加入必要的 数据校验逻辑

回顾 Person 类的封装实现:


Java

public class Person {
    private String name; // 私有属性
    private int age;     // 私有属性

    // Getter for name
    public String getName() {
        return name; // 返回当前对象的 name 值
    }

    // Setter for name
    public void setName(String name) {
        this.name = name; // this.name 指当前对象的 name,= 右边的 name 是传入的参数
    }

    // Getter for age
    public int getAge() {
        return age;
    }

    // Setter for age (带校验)
    public void setAge(int age) {
        if (age >= 0) { // 校验逻辑:年龄必须 >= 0
            this.age = age;
        } else {
            System.out.println("Invalid age! Age cannot be negative.");
        }
    }
    // ... 其他方法省略 ...
}

关键点:

  • 属性 nameage 被声明为 private,外部代码无法直接访问 person.nameperson.age

  • 外部代码只能通过 getName()setName(String name)getAge()setAge(int age) 这些公共方法来读取或修改属性值。

  • setAge(int age) 方法中,加入了数据校验逻辑(age >= 0),确保了数据的有效性。这是封装带来的安全性的直接体现。

五、总结

  • 类(Class):是对象的抽象模板,定义了对象共有的属性(成员变量)和行为(成员方法)。

  • 对象(Object):是类的具体实例,拥有类定义的属性和方法,每个对象在内存中独立存在。

  • 封装(Encapsulation):是 OOP 的核心特性之一,通过将数据(属性)和操作数据的方法捆绑在一起,并隐藏内部实现细节(通常使用 private 修饰属性),仅对外提供公共访问接口(Getter/Setter 方法),来提高代码的安全性、可维护性和复用性。

理解并熟练运用类、对象和封装,是编写高质量、可维护 Java 程序的基础。它奠定了后续学习继承、多态等其他 OOP 特性的基石。

更多推荐