第一部分:封装概念与重要性

理解面向对象封装的核心思想和实现方式

学习目标

掌握封装的定义、实现步骤,理解数据隐藏的重要性

❌ 未封装(数据暴露)

public class BankAccount { public String accountNumber; // 任何人都能访问 public double balance; // 数据不安全 public static void main(String[] args) { BankAccount acc = new BankAccount(); acc.balance = -1000; // 外部直接修改,不合理 } }

✅ 封装后(数据隐藏)

public class BankAccount { private String accountNumber; // 私有,外部无法访问 private double balance; // 通过方法控制访问 public void deposit(double amount) { if (amount > 0) { balance += amount; } } public double getBalance() { return balance; } }

封装的好处

  • 数据安全:防止外部直接修改数据
  • 易于维护:内部实现改变不影响外部调用
  • 接口清晰:只暴露必要的接口给外部
  • 代码复用:隐藏实现细节,提高可复用性

第二部分:四种访问权限详解

深入理解private、default、protected、public的区别

学习目标

掌握四种访问权限的语法规则和使用场景

访问控制符
同类
同包
子类
其他包
public
protected
default
private
1

private(私有)

public class Employee { private String name; // 只能在本类中访问 private double salary; // 外部无法直接访问 public Employee(String name, double salary) { this.name = name; this.salary = salary; } private void calculateBonus() { // 私有方法 System.out.println("计算奖金逻辑"); } }
2

default(包访问)

// 文件:com/example/PackageA/Person.java package com.example.PackageA; class Person { // 没有修饰符,默认包访问 String name; // 同包可访问 void greet() { // 同包可访问 System.out.println("Hello, " + name); } } // 文件:com/example/PackageA/Test.java package com.example.PackageA; public class Test { public static void main(String[] args) { Person p = new Person(); // 同包可以访问 p.name = "张三"; p.greet(); } }
3

protected(受保护)

package com.example.animals; public class Animal { protected String name; // 同包 + 子类可访问 protected void eat() { // 受保护方法 System.out.println(name + "正在进食"); } } // 不同包的子类 package com.example.zoo; import com.example.animals.Animal; public class Lion extends Animal { public void roar() { this.name = "狮子"; // 子类可以访问protected成员 this.eat(); // 可以调用protected方法 } }
4

public(公有)

package com.example.utils; public class StringHelper { public static boolean isEmpty(String str) { // 任何地方可访问 return str == null || str.trim().isEmpty(); } public static String reverse(String str) { // 公共工具方法 return new StringBuilder(str).reverse().toString(); } }

第三部分:Getter/Setter方法

掌握标准的属性访问控制实现方式

学习目标

学会编写规范的Getter/Setter,理解数据验证的重要性

1

标准Getter/Setter实现

public class Product { private String name; private double price; private int stock; // Getter方法:获取属性值 public String getName() { return name; } public double getPrice() { return price; } public int getStock() { return stock; } // Setter方法:设置属性值(带验证) public void setName(String name) { if (name != null && !name.trim().isEmpty()) { this.name = name.trim(); } } public void setPrice(double price) { if (price >= 0) { this.price = price; } } public void setStock(int stock) { if (stock >= 0) { this.stock = stock; } } }
2

只读属性(无Setter)

public class ImmutablePerson { private final String id; // 只读属性 private final String name; private final LocalDate birthDate; public ImmutablePerson(String id, String name, LocalDate birthDate) { this.id = id; this.name = name; this.birthDate = birthDate; } // 只有Getter,没有Setter public String getId() { return id; } public String getName() { return name; } public LocalDate getBirthDate() { return birthDate; } public int getAge() { return Period.between(birthDate, LocalDate.now()).getYears(); } }
3

Lombok简化代码

// 使用Lombok自动生成Getter/Setter import lombok.Data; import lombok.Setter; @Data // 自动生成所有Getter/Setter/toString/equals/hashCode public class User { private String username; private String email; @Setter(AccessLevel.PRIVATE) // 私有Setter private LocalDateTime createdAt; }

Getter/Setter最佳实践

  • 所有属性设为private,通过Getter/Setter访问
  • Setter方法中加入数据验证逻辑
  • 只读属性只提供Getter
  • 返回引用类型时返回不可变对象或副本
  • 使用IDE自动生成Getter/Setter代码

第四部分:包与访问权限

理解包的创建和使用对访问权限的影响

学习目标

掌握包的组织方式,理解包访问权限的实际应用

com.company.project
// 项目结构 src/ └── com/ └── company/ └── project/ ├── model/ │ ├── Employee.java // 实体类 │ └── Department.java // 实体类 ├── service/ │ ├── EmployeeService.java // 业务逻辑 │ └── DepartmentService.java └── util/ ├── StringUtil.java // 工具类 └── DateUtil.java
1

包访问权限示例

// 文件:com/example/model/Product.java package com.example.model; public class Product { private String name; private double price; int stock; // 包访问,同包可见 public Product(String name, double price) { this.name = name; this.price = price; } } // 文件:com/example/service/ProductService.java package com.example.service; import com.example.model.Product; public class ProductService { public void updateStock(Product product, int quantity) { // 可以访问同包的stock变量 product.stock += quantity; } }

第五部分:封装与设计模式

学习基于封装的常见设计模式

隐藏实现细节

public interface List { void add(Object element); Object get(int index); int size(); } public class ArrayList implements List { private Object[] elements; private int size; public void add(Object element) { // 内部实现细节被隐藏 ensureCapacity(); elements[size++] = element; } private void ensureCapacity() { // 私有方法,封装实现细节 } }

不可变对象

public final class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } // 没有Setter,保证不可变性 }

第六部分:常见错误与解决方案

总结封装和访问权限使用中的典型问题

访问权限错误

错误1:直接访问私有属性
public class Test { public static void main(String[] args) { Person p = new Person(); p.name = "张三"; // 编译错误!name是私有的 } }
解决方案:
p.setName("张三"); // 通过Setter方法

Getter/Setter陷阱

错误2:返回引用类型的直接引用
public class DateHolder { private Date date; public Date getDate() { return date; // 返回的是原对象的引用! } } // 外部代码 DateHolder holder = new DateHolder(); holder.getDate().setTime(0); // 修改了内部状态!
正确做法:
public Date getDate() { return new Date(date.getTime()); // 返回副本 }

第七部分:实战演练 - 银行账户系统

综合运用封装和访问权限构建完整系统

项目需求

  • 创建BankAccount类,包含账号、余额、密码
  • 实现存款、取款、转账功能
  • 确保余额不能为负数
  • 密码验证后才能操作
  • 提供账户统计信息

完整实现

package com.bank.system; import java.math.BigDecimal; import java.util.Objects; public class BankAccount { // 静态变量:类级别共享 private static int nextAccountNumber = 100001; private static int totalAccounts = 0; // 实例变量:对象级别 private final String accountNumber; private final String ownerName; private final String password; private BigDecimal balance; public BankAccount(String ownerName, String password, BigDecimal initialDeposit) { this.accountNumber = String.valueOf(nextAccountNumber++); this.ownerName = ownerName; this.password = password; this.balance = initialDeposit.max(BigDecimal.ZERO); totalAccounts++; } // 公共方法:存款 public boolean deposit(BigDecimal amount) { if (isValidAmount(amount)) { balance = balance.add(amount); return true; } return false; } // 公共方法:取款(需要密码验证) public boolean withdraw(BigDecimal amount, String password) { if (!verifyPassword(password)) { return false; } if (isValidAmount(amount) && hasSufficientFunds(amount)) { balance = balance.subtract(amount); return true; } return false; } // 公共方法:转账 public boolean transferTo(BankAccount target, BigDecimal amount, String password) { if (Objects.isNull(target) || target == this) { return false; } if (withdraw(amount, password)) { return target.deposit(amount); } return false; } // 公共Getter方法 public String getAccountNumber() { return accountNumber; } public String getOwnerName() { return ownerName; } public BigDecimal getBalance() { return balance; } // 静态方法:获取总账户数 public static int getTotalAccounts() { return totalAccounts; } // 私有方法:内部验证 private boolean verifyPassword(String password) { return this.password.equals(password); } private boolean isValidAmount(BigDecimal amount) { return amount != null && amount.compareTo(BigDecimal.ZERO) > 0; } private boolean hasSufficientFunds(BigDecimal amount) { return balance.compareTo(amount) >= 0; } // 受保护方法:子类可重写 protected void logTransaction(String type, BigDecimal amount) { System.out.printf("账户%s: %s ¥%s%n", accountNumber, type, amount); } // 包访问方法:同包可见 void adjustBalance(BigDecimal adjustment) { this.balance = balance.add(adjustment); } } // 测试类 public class BankSystemTest { public static void main(String[] args) { BankAccount acc1 = new BankAccount("张三", "123456", new BigDecimal("1000.00")); BankAccount acc2 = new BankAccount("李四", "654321", new BigDecimal("500.00")); // 测试转账 boolean success = acc1.transferTo(acc2, new BigDecimal("200.00"), "123456"); System.out.println("转账成功:" + success); System.out.println("张三余额:" + acc1.getBalance()); System.out.println("李四余额:" + acc2.getBalance()); System.out.println("总账户数:" + BankAccount.getTotalAccounts()); } }
转账成功:true 张三余额:800.00 李四余额:700.00 总账户数:2

学习总结

  • 封装原则:隐藏内部实现,暴露必要接口
  • 访问权限:private > default > protected > public
  • Getter/Setter:标准的数据访问方式
  • 包管理:合理组织代码结构
  • 不可变对象:使用final和私有构造器
  • 验证逻辑:在Setter中实现数据验证