Skip to content

11.1 封装(private、get/set 方法)

封装的概念

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

封装的实现

在 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私有的,只有本类可以访问属性、方法

getter 和 setter 方法

getter 方法

getter 方法用于获取属性的值,方法名通常以 get 开头,后跟属性名(首字母大写)。

语法:

java
public 返回类型 get属性名() {
    return 属性名;
}

示例:

java
public String getName() {
    return name;
}

public int getAge() {
    return age;
}

setter 方法

setter 方法用于设置属性的值,方法名通常以 set 开头,后跟属性名(首字母大写)。

语法:

java
public void set属性名(参数类型 参数名) {
    this.属性名 = 参数名;
}

示例:

java
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");
    }
}

示例:封装的使用

示例 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 Employee {
    // 私有属性
    private String name;
    private int employeeId;
    private double salary;
    
    // 构造方法
    public Employee(String name, int employeeId, double salary) {
        this.name = name;
        this.employeeId = employeeId;
        setSalary(salary); // 使用 setter 方法设置工资,以便进行验证
    }
    
    // 公共的 setter 方法
    public void setName(String name) {
        this.name = name;
    }
    
    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }
    
    public void setSalary(double salary) {
        // 验证工资的有效性
        if (salary >= 0) {
            this.salary = salary;
        } else {
            this.salary = 0;
            System.out.println("Salary cannot be negative");
        }
    }
    
    // 公共的 getter 方法
    public String getName() {
        return name;
    }
    
    public int getEmployeeId() {
        return employeeId;
    }
    
    public double getSalary() {
        return salary;
    }
    
    // 其他方法
    public void raiseSalary(double percentage) {
        if (percentage > 0) {
            salary += salary * percentage / 100;
            System.out.println("Salary raised by " + percentage + "%");
            System.out.println("New salary: " + salary);
        } else {
            System.out.println("Percentage must be positive");
        }
    }
}

public class EmployeeExample {
    public static void main(String[] args) {
        // 创建员工对象
        Employee employee = new Employee("John Doe", 1001, 5000.0);
        
        // 查看员工信息
        System.out.println("Name: " + employee.getName());
        System.out.println("Employee ID: " + employee.getEmployeeId());
        System.out.println("Salary: " + employee.getSalary());
        
        // 涨工资
        employee.raiseSalary(10); // 涨 10%
        
        // 修改员工信息
        employee.setName("Jane Smith");
        employee.setSalary(6000.0);
        
        // 查看更新后的员工信息
        System.out.println("\nUpdated Employee Info:");
        System.out.println("Name: " + employee.getName());
        System.out.println("Salary: " + employee.getSalary());
    }
}

封装的优势

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

封装的最佳实践

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

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

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. 返回可变对象的引用

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

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

5. 命名不规范

症状:getter 和 setter 方法的命名不符合规范,影响代码的可读性

解决方案:遵循命名规范,getter 方法以 get 开头,setter 方法以 set 开头,后跟属性名(首字母大写)

总结

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

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

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

封装的优势包括:

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

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

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