第一部分:封装概念与重要性
理解面向对象封装的核心思想和实现方式
学习目标
掌握封装的定义、实现步骤,理解数据隐藏的重要性
❌ 未封装(数据暴露)
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中实现数据验证