Skip to content

10.6 封装概念入门

封装的概念

封装(Encapsulation)是面向对象编程的核心特性之一,它将数据和方法封装在对象中,对外只暴露必要的接口,隐藏实现细节。

封装的作用

  1. 保护数据:防止外部代码直接访问和修改对象的内部数据
  2. 隐藏实现细节:只暴露必要的接口,简化外部代码的使用
  3. 提高代码的可维护性:修改内部实现时,不会影响外部代码
  4. 提高代码的安全性:防止不合理的操作和数据修改

封装的实现

在 Java 中,封装的实现主要通过以下方式:

  1. 将属性设为 private:使用 private 访问修饰符,使属性只能在本类中访问
  2. 提供公共的 getter 和 setter 方法:通过公共方法来访问和修改属性

示例:基本封装

java
public class Person {
    // 将属性设为 private
    private String name;
    private int age;
    
    // 提供公共的 setter 方法
    public void setName(String name) {
        this.name = name;
    }
    
    public void setAge(int age) {
        // 可以在 setter 方法中添加验证逻辑
        if (age >= 0 && age <= 150) {
            this.age = age;
        } else {
            System.out.println("Invalid age");
        }
    }
    
    // 提供公共的 getter 方法
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
}

访问修饰符

Java 提供了四种访问修饰符,用于控制类、属性和方法的访问权限:

修饰符访问权限适用范围
public公共的,任何类都可以访问类、属性、方法
protected受保护的,同一个包或子类可以访问属性、方法
default默认的,同一个包可以访问类、属性、方法
private私有的,只有本类可以访问属性、方法

示例:封装的使用

示例 1:基本使用

java
public class Student {
    // 私有属性
    private String name;
    private int studentId;
    private double grade;
    
    // 公共的 setter 方法
    public void setName(String name) {
        this.name = name;
    }
    
    public void setStudentId(int studentId) {
        this.studentId = studentId;
    }
    
    public void setGrade(double grade) {
        // 验证成绩的有效性
        if (grade >= 0 && grade <= 100) {
            this.grade = grade;
        } else {
            System.out.println("Invalid grade");
        }
    }
    
    // 公共的 getter 方法
    public String getName() {
        return name;
    }
    
    public int getStudentId() {
        return studentId;
    }
    
    public double getGrade() {
        return grade;
    }
    
    // 其他方法
    public boolean isPass() {
        return grade >= 60;
    }
}

public class StudentExample {
    public static void main(String[] args) {
        // 创建学生对象
        Student student = new Student();
        
        // 使用 setter 方法设置属性
        student.setName("John");
        student.setStudentId(1001);
        student.setGrade(85.5);
        
        // 使用 getter 方法获取属性
        System.out.println("Name: " + student.getName());
        System.out.println("Student ID: " + student.getStudentId());
        System.out.println("Grade: " + student.getGrade());
        System.out.println("Pass: " + student.isPass());
        
        // 尝试设置无效的成绩
        student.setGrade(150); // 会输出 "Invalid grade"
    }
}

示例 2:封装的验证

java
public class BankAccount {
    // 私有属性
    private String accountNumber;
    private double balance;
    
    // 构造方法
    public BankAccount(String accountNumber, double balance) {
        this.accountNumber = accountNumber;
        // 验证余额的有效性
        if (balance >= 0) {
            this.balance = balance;
        } else {
            this.balance = 0;
            System.out.println("Initial balance cannot be negative");
        }
    }
    
    // 公共的 setter 方法
    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }
    
    // 公共的 getter 方法
    public String getAccountNumber() {
        return accountNumber;
    }
    
    public double getBalance() {
        return balance;
    }
    
    // 其他方法
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: " + amount);
            System.out.println("New balance: " + balance);
        } else {
            System.out.println("Deposit amount must be positive");
        }
    }
    
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("Withdrawn: " + amount);
            System.out.println("New balance: " + balance);
        } else if (amount <= 0) {
            System.out.println("Withdrawal amount must be positive");
        } else {
            System.out.println("Insufficient balance");
        }
    }
}

public class BankAccountExample {
    public static void main(String[] args) {
        // 创建银行账户对象
        BankAccount account = new BankAccount("123456789", 1000.0);
        
        // 查看初始余额
        System.out.println("Account Number: " + account.getAccountNumber());
        System.out.println("Initial Balance: " + account.getBalance());
        
        // 存款
        account.deposit(500.0);
        
        // 取款
        account.withdraw(200.0);
        
        // 尝试取超出余额的金额
        account.withdraw(2000.0);
        
        // 尝试存入负数
        account.deposit(-100.0);
    }
}

示例 3:封装的继承

java
public class Person {
    // 私有属性
    private String name;
    private int age;
    
    // 公共的 setter 方法
    public void setName(String name) {
        this.name = name;
    }
    
    public void setAge(int age) {
        if (age >= 0 && age <= 150) {
            this.age = age;
        } else {
            System.out.println("Invalid age");
        }
    }
    
    // 公共的 getter 方法
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
}

public class Student extends Person {
    // 私有属性
    private int studentId;
    private double grade;
    
    // 公共的 setter 方法
    public void setStudentId(int studentId) {
        this.studentId = studentId;
    }
    
    public void setGrade(double grade) {
        if (grade >= 0 && grade <= 100) {
            this.grade = grade;
        } else {
            System.out.println("Invalid grade");
        }
    }
    
    // 公共的 getter 方法
    public int getStudentId() {
        return studentId;
    }
    
    public double getGrade() {
        return grade;
    }
    
    // 其他方法
    public boolean isPass() {
        return grade >= 60;
    }
}

public class StudentExample {
    public static void main(String[] args) {
        // 创建学生对象
        Student student = new Student();
        
        // 设置属性
        student.setName("John");
        student.setAge(18);
        student.setStudentId(1001);
        student.setGrade(85.5);
        
        // 获取属性
        System.out.println("Name: " + student.getName());
        System.out.println("Age: " + student.getAge());
        System.out.println("Student ID: " + student.getStudentId());
        System.out.println("Grade: " + student.getGrade());
        System.out.println("Pass: " + student.isPass());
    }
}

封装的优势

  1. 数据保护:通过私有属性和公共方法,保护数据不被直接修改
  2. 代码可维护性:修改内部实现时,不会影响外部代码
  3. 代码安全性:可以在 setter 方法中添加验证逻辑,确保数据的有效性
  4. 代码简洁性:外部代码只需要关注公共接口,不需要了解内部实现
  5. 代码可扩展性:可以在不影响外部代码的情况下,扩展和修改内部实现

封装的最佳实践

  1. 将属性设为 private:使用 private 访问修饰符,使属性只能在本类中访问
  2. 提供公共的 getter 和 setter 方法:通过公共方法来访问和修改属性
  3. 在 setter 方法中添加验证逻辑:确保数据的有效性
  4. 保持 getter 和 setter 方法的简洁性:只做必要的操作
  5. 避免在 getter 方法中返回可变对象的引用:如果返回可变对象,应该返回其副本

示例:避免返回可变对象的引用

java
import java.util.ArrayList;
import java.util.List;

public class Student {
    private String name;
    private List<String> courses;
    
    public Student(String name) {
        this.name = name;
        this.courses = new ArrayList<>();
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    // 错误:返回可变对象的引用
    public List<String> getCourses() {
        return courses;
    }
    
    // 正确:返回可变对象的副本
    public List<String> getCoursesSafe() {
        return new ArrayList<>(courses);
    }
    
    public void addCourse(String course) {
        courses.add(course);
    }
    
    public void removeCourse(String course) {
        courses.remove(course);
    }
}

public class StudentExample {
    public static void main(String[] args) {
        Student student = new Student("John");
        student.addCourse("Math");
        student.addCourse("English");
        
        // 错误:直接修改返回的列表
        List<String> courses = student.getCourses();
        courses.add("Physics"); // 会修改学生的课程列表
        System.out.println("Courses: " + student.getCourses());
        
        // 正确:修改副本不会影响原始列表
        List<String> coursesSafe = student.getCoursesSafe();
        coursesSafe.add("Chemistry"); // 不会修改学生的课程列表
        System.out.println("Courses after modifying safe copy: " + student.getCourses());
    }
}

常见问题

1. 过度封装

症状:提供了过多的 getter 和 setter 方法,导致代码冗余

解决方案:只提供必要的 getter 和 setter 方法,避免过度封装

2. 封装不彻底

症状:部分属性没有设为 private,或者没有提供相应的 getter 和 setter 方法

解决方案:将所有属性设为 private,并提供必要的 getter 和 setter 方法

3. 在 setter 方法中缺少验证

症状:可以设置无效的数据,导致对象状态不正确

解决方案:在 setter 方法中添加验证逻辑,确保数据的有效性

4. 返回可变对象的引用

症状:外部代码可以直接修改对象的内部状态

解决方案:返回可变对象的副本,而不是引用

总结

封装是面向对象编程的核心特性之一,它将数据和方法封装在对象中,对外只暴露必要的接口,隐藏实现细节。

封装的实现主要通过以下方式:

  • 将属性设为 private
  • 提供公共的 getter 和 setter 方法

封装的优势包括:

  • 保护数据
  • 隐藏实现细节
  • 提高代码的可维护性
  • 提高代码的安全性

通过合理使用封装,可以使代码更加安全、可维护和可扩展。

© 2026 编程马·菜鸟教程 版权所有